線状物体の自動アノテーションで効率化

前書き

最近ブログをサボっていましたので(n 回目)6月から心機一転ブログを再開しようと思います(n 回目)。

アノテーションとは

物体検出アルゴリズムを適用して、対象物体を検出する際、どうしても教師データを作成する「アノテーション」という作業が必要になります。アノテーションとは可視画像において検出対象となる物体領域を示し、座標情報などをメタデータとして作成する作業です。

アノテーション作業の詳細
melheaven.hatenadiary.jp

課題

私が普段使用しているアノテーションソフトは「LabelMe」「LabelImg」などが挙げられます。私の研究分野では企業から画像データを頂き、画像に示された領域の検出を実現させるケースが多いです。ただ画像データの量は非常に膨大で一定の精度を得る為のアノテーションを実行するには途轍もない労力と時間を要するのです。これを何とか効率化したいと考えました。

  • 膨大なデータのアノテーションに時間・労力を要する
  • 技術者は対象領域をトレースした画像をかなり所持している(個人談)→ ただデータを活かしきれていない。

研究室にひび割れ検出の研究をしている学生がおりまして、「トレースされた画像をアノテーションソフトへ自動化する」という目標を掲げました。
この記事の前提条件は以下の通りです。

  • 線状の物体を赤・青・緑色でトレースしているしている画像が存在
  • トレース作業前後の画像が存在する
  • アノテーションデータはYOLO形式とする

環境

コード

github.com

アルゴリズム

  1. 画像を取得
  2. 画像からトレース領域(赤・青・水・緑)の画素を検出
  3. 各画素の座標を取得
  4. 検出した画素を中心にFilterSize:(2F+1)の矩形を生成

実装(一部コードを抜粋)

特定色の画素座標を抽出:指定した範囲の色の物体の座標を取得する関数

引数
  • frame:画像
  • AREA_RATIO_THRESHOLD:area_ratio未満の画素は無視する
  • LOW_COLOR:抽出する色の下限
  • HIGH_COLOR:抽出する色の上限
返り値
  • mapcount:抽出した画素の座標
  • ex_img:出力画像
def find_specific_color(frame,AREA_RATIO_THRESHOLD,LOW_COLOR,HIGH_COLOR):

    # 高さ,幅,チャンネル数
    h,w,c = frame.shape

    # hsv色空間に変換
    hsv = cv2.cvtColor(frame,cv2.COLOR_BGR2HSV)
    
    # 色を抽出する
    ex_img = cv2.inRange(hsv,LOW_COLOR,HIGH_COLOR)
    indices = np.dstack(np.where(ex_img != 0))
    
    mapcount=[]
    #左,右,上,下
    mapminmax=[w+1,0,h+1,0]
    maplist = np.zeros((w,h))
    
    for indice in indices:
        for ind in indice:
            if ind.any():
                # 境界処理
                if ind[1]==0 or ind[0]==0 or ind[1]==(ex_img.shape[1]-1) or ind[0]==(ex_img.shape[0]-1): pass
                else:
                    mapcount.append([ind[0], ind[1]])
    
    return ex_img, mapcount

抽出画素座標を中心に矩形生成

検出した画素を中心にFilterSize:(2F+1)の矩形を生成しますが、全てに画素に対して矩形に生成すべきでないです。
ここで矩形生成の中心となった座標からF以上離れていないと矩形を生成できないようなアルゴリズムを作成しました。

  1. 長さw分画素値配列W、長さh分の画素値配列Hを用意
  2. 画素値配列Wに 画素座標(i, j)の場合、W[i-10〜i+10]に j を入力(※)
  3. 画素値配列Hに 画素座標(i, j)の場合、W[j-10〜j+10]に i を入力(※)
  4. 幅:i-10〜i+10行の画素において、j とF 以上離れた画素でない かつ 
  5. 高さ:j-10〜j+10行の画素において、i とF 以上離れた画素でないと矩形を生成しないとする
  6. 矩形生成・アノテーション作成
wrange = np.zeros(wim)
hrange = np.zeros(him)
count = 0
f = filtersize//2
    
    for _map in _mapcount:
        if abs(wrange[_map[0]-1]-_map[1])>f  and  abs(hrange[_map[1]-1]-_map[0])>f :
            count+=1
            wrange[_map[0]-f:_map[0]+f]=_map[1]
            hrange[_map[1]-f:_map[1]+f]=_map[0]
            cv2.rectangle(im, ( _map[0]-f,_map[1]-f), (_map[0]+f,_map[1]+f), (255, 255, 0))
            # アノテーションデータ作成
            generateAnnotationTxt(_map[0], _map[1], wim, him, filtersize, labelimg_path, foldernum)

アノテーションファイルに変換

YOLO Formatのアノテーションデータの形式は以下を参考にしました。
github.com

アノテーションファイル(.txt)の形式は以下のようになっています。
注意しなければならないのは”一画像一ファイル”で”一矩形ー行”となっており、各行は必ず末尾に改行が必要です。

<クラス番号:0〜> <画素x座標 / 画像幅> <画素y座標 / 画像高さ> <矩形x座標 / 画像幅> <矩形y座標 / 画像高さ>
def generateAnnotationTxt(centerx, centery, imw, imh, filter_size, filetitle, fn):
    classNumber = 0
    f = open(filetitle+'_'+fn+'.txt','a')
    f.write('{} {:.6f} {:.6f} {:.6f} {:.6f}\n'.format(classNumber, centerx/imw, centery/imh, filter_size/imw, filter_size/imh))
    f.close()

結果

f:id:electric-city:20210603141732p:plain:w300:h300
緑・青部分の矩形化
f:id:electric-city:20210603141727p:plain:w300:h300
Annotation File(.txt)

まとめ

  • 膨大なトレース画像からトレースされた領域の座標を元にアノテーションファイルの生成を自動化
  • アノテーションに要する時間・労力を抑えたので、今後は膨大な画像の学習が可能になる