スポンサーリンク

【YOLO】VottでYOLOのアノテーションデータを作る方法ご紹介。

YOLO
スポンサーリンク

今回の記事はVottでYOLOのアノテーションデータを作る方法をご紹介する内容です。Yoloで自己学習モデルを作成しようと考えている方は是非参考にしてみて下さい。

スポンサーリンク

Vottの使い方

まずはVottをインストールします。下記ページからインストール可能です。

Vott公式

windowsの方はexeファイル、マックの方はdmgファイルをインストールしましょう。

アプリを開くとデータソースの設定をまずは行います。

まずはホーム画面が立ち上がるので左メニューのコンセントマーク(接続)をクリックし、データを接続します。

接続画面の+ボタンを押して接続内容を設定します。

接続設定の表示名は適当な名前を設定、プロバイダーはローカルファイルシステムでアノテーションを行う画像を設定します。

接続が完了すれば新規プロジェクトを立ち上げます。

表示名はプロジェクト名になります。必須項目の設定を行います。ソースは先ほど設定した接続の表示名、ターゲット接続先も同様に同じ表示名に設定します。

あとは画像にひたすらアノテーションをつけていきます。

VottでYOLOのアノテーションデータの作り方

アノテーションを作成した後は出力します。

メニューバーの斜め上矢印の出力ボタンからエクスポート設定を行います。エクスポートの形式はPascal VOC形式で出力します。

アノテーションデータのYOLO formatへの変換

出力されたアノテーションデータはxml形式となっております。このxml形式はYoloの正式なフォーマットではないため変換する必要があります。

変換方法は別記事で詳しく記載しているのでそちらも参考になると思います。

VottのアノテーションデータをYOLOフォーマットに変換する方法ご紹介。

まずは、「yolotest」というプロジェクトフォルダを作成し、そこに全てのデータを配置していきます。フォルダ構成は以下のようにして下さい。

yolotest
  |--xmls  ←xmlファイル
  |--images  ←アノテーション付けした画像
  |--labels  ←出力フォルダ
  |--classes.txt  ←クラス名を全て改行して記載

例)classes.txtの記載
---------
person
dog
apple
---------

次に配置したxmlデータをYOLOフォーマットに変換するためにPythonコードで変換を行います。フルパスの部分と画像の拡張子の部分は修正して下さい。

# ここを修正
## フルパスに修正
parentpath = r"C:〇〇\〇〇\yolotest" # フォルダのパス
## 拡張子によって変更
ext = '.png' # 画像拡張しを記載.jpgなど

#ここはanotetiondataフォルダの構成が変わっている場合個別に設定(基本的に変更なし)
addxmlpath = parentpath + r"\anotetiondata\xmls" # xmlを格納するフォルダパス
addimgpath = parentpath + r"\anotetiondata\images" # 画像を格納するフォルダパス
outputpath = parentpath + r"\anotetiondata\labels" # 出力フォルダパス
classes_txt = parentpath + r"\anotetiondata\classes.txt" # クラス名一覧テキスト

#!/usr/bin/env python
# -*- coding: utf8 -*-
import os
import sys
from xml.etree import ElementTree
from xml.etree.ElementTree import Element, SubElement
from lxml import etree
import codecs
import cv2
from glob import glob

XML_EXT = '.xml'
ENCODE_METHOD = 'utf-8'

class PascalVocReader:
    def __init__(self, filepath):
        # shapes type:
        # [labbel, [(x1,y1), (x2,y2), (x3,y3), (x4,y4)], color, color, difficult]
        self.shapes = []
        self.filepath = filepath
        self.verified = False
        try:
            self.parseXML()
        except:
            pass

    def getShapes(self):
        return self.shapes

    def addShape(self, label, bndbox, filename, difficult):
        xmin = int(bndbox.find('xmin').text.split('.')[0])
        ymin = int(bndbox.find('ymin').text.split('.')[0])
        xmax = int(bndbox.find('xmax').text.split('.')[0])
        ymax = int(bndbox.find('ymax').text.split('.')[0])
        points = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]
        self.shapes.append((label, points, filename, difficult))

    def parseXML(self):
        assert self.filepath.endswith(XML_EXT), "Unsupport file format"
        parser = etree.XMLParser(encoding=ENCODE_METHOD)
        xmltree = ElementTree.parse(self.filepath, parser=parser).getroot()
        filename = xmltree.find('filename').text
        path = xmltree.find('path').text
        
        try:
            verified = xmltree.attrib['verified']
            if verified == 'yes':
                self.verified = True
        except KeyError:
            self.verified = False

        for object_iter in xmltree.findall('object'):
            
            bndbox = object_iter.find("bndbox")
            label = object_iter.find('name').text

            # Add chris

            difficult = False
            if object_iter.find('difficult') is not None:
                difficult = bool(int(object_iter.find('difficult').text))
            self.addShape(label, bndbox, path, difficult)
        return True


classes = dict()
num_classes = 0

try:
    input = raw_input
except NameError:
    pass

if os.path.isfile(classes_txt):
    with open(classes_txt, "r") as f:
        class_list = f.read().strip().split()
        classes = {k : v for (v, k) in enumerate(class_list)}

xmlPaths = glob(addxmlpath + "/*.xml")
#imgPaths = glob(addimgpath + "/*"+ext)

for xmlPath in xmlPaths:
    tVocParseReader = PascalVocReader(xmlPath)
    shapes = tVocParseReader.getShapes()
   
    with open(outputpath + "/" + os.path.basename(xmlPath)[:-4] + ".txt", "w") as f:
        
        for shape in shapes:
            class_name = shape[0]
            box = shape[1]
            #filename = os.path.splittext(xmlPath)[0] + ext
            filename = os.path.splitext(addimgpath + "\\" + os.path.basename(xmlPath)[:-4])[0] + ext

            if class_name not in classes.keys():
                classes[class_name] = num_classes
                num_classes += 1
            class_idx = classes[class_name]

            (height, width, _) = cv2.imread(filename).shape

            coord_min = box[0]
            coord_max = box[2]

            xcen = float((coord_min[0] + coord_max[0])) / 2 / width
            ycen = float((coord_min[1] + coord_max[1])) / 2 / height
            w = float((coord_max[0] - coord_min[0])) / width
            h = float((coord_max[1] - coord_min[1])) / height

            f.write("%d %.06f %.06f %.06f %.06f\n" % (class_idx, xcen, ycen, w, h))
            #print(class_idx, xcen, ycen, w, h)

with open(parentpath + "classes.txt", "w") as f:
    for key in classes.keys():
        f.write("%s\n" % key)
        #print(key)

これでlabelsに出力されたファイルをYOLOのオリジナル学習に使用できます。

以上でVottで作成したアノテーションデータをYOLOのオリジナル学習に利用できるようになりました。

では今回の記事は以上です。

YOLOをPythonで動かしてオリジナル画像で学習させる方法に関しては別記事を記載しているのでそちらの記事を参考にしてみて下さい。

スポンサーリンク
スポンサーリンク
スポンサーリンク

ブログに関しては500円程度かかりますが、それ以外は無料です。知識の吸収と並行してアウトプットは非常に効率が良いです。テックアカデミーに関しては講座レベルが高いにも関わらず、無料体験や人気口座も大幅値下げがあるので、重点的に学びたいものを無料体験してみてください。

転職時にも、エンジニアからテックアカデミー・Paizaは認知度が高いので、未経験入社採用を行う際履歴書で目に留まります。特にPaizaのスキルレベルA・SなどはIT業界でも評価されます。

テックアカデミー・Paizaの無料登録ができる期間中にぜひご利用してみてください。私も活用経験ありです。

YOLO
スポンサーリンク
スポンサーリンク
ともぶろぐ

コメント

タイトルとURLをコピーしました