cocoデータセットのjsonをtxtに変える方法

## はじめに
今回は、深層学習のデータセットで有名な**cocoデータセット**の`jsonファイル`からアノテーションの`txtファイル`に変換するコードを紹介します。
アルバイト中に「jsonからtxtのアノテーションファイルってどうやって作るんだ？」という問題にぶつかり、色々調べて解決できたのでせっかくだから記事に起こそうという感じで今回は執筆しています。
実は、他の方が紹介されているwebサイトがあったのですが、`404`とサイトが閉鎖されていたため自分が新しく書き直すことにしました。
それでは、内容の方に入っていきましょう！

## 実験環境
コードの紹介に入る前にひとまず実験環境と使ったjsonファイルだけ紹介します。

>* GoogleColaboratory 
>* [Jsonファイル](https://github.com/ACI-Institute/faces4coco)

## コードの紹介
それでは今回使ったコードです！
最後にまとめて載せているのでそこだけ欲しいという人は途中は飛ばしてください（＾＾）

まず必要ライブラリのインポートです。

```.py
from pycocotools.coco import COCO
import matplotlib.pyplot as plt
import numpy as np
import skimage.io as io
import pylab
import os
import shutil
```

その後保存するディレクトリやclassのディレクトリへのパスを指定します。
アノテーションをしたこと人は分かるかもしれませんが、`classes.txt`にはアノテーションに使うラベルを記載します。

```.py
class_txt = '/content/coco/classes.txt'

'''
classes.txtの中身↓
person
car
'''

# アノテーションファイルを保存する用のディレクトリ
save_dir = '/content/coco/dataset/'

# 指定したラベルがある画像を保存するようのディレクトリ
img_dir = '/content/coco/images/'

# もしディレクトリがなければ作る
os.makedirs(save_dir, exist_ok=True)

# もしディレクトリがなければ作る
os.makedirs(img_dir, exist_ok=True)

# classes.txtから必要なクラスのアノテーションを取得（今回はpersonとface）
with open(class_txt, 'r') as f:
    classes = f.readlines()
```

指定したラベルのある画像とアノテーションファイルを読み込んで保存します。

```.py
error_files = []

for i in range(0, len(classes)):
    class_name = classes[i].split('\n')[0] 
    catIds = coco.getCatIds(catNms=[class_name]);
    imgIds = coco.getImgIds(catIds=catIds );

    for j in range(0, len(imgIds)):
        img = coco.loadImgs(imgIds[j])[0]
        I = io.imread(img['coco_url'])
        
        # 画像を保存するディレクトリを変更してる
        os.chdir(img_dir)
        io.imsave(img['file_name'],I)
        os.chdir('../../')

        print(img['file_name'])
        annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
        anns = coco.loadAnns(annIds)
        try:
            I = io.imread(img['coco_url'])
        except KeyboardInterrupt:
            sys.exit
        except:
            error_files.append(img['file_name'])
            continue

        # アノテーション情報を取り出し、新たにtxtファイルに書き出す必要がある
        for k in range(0, len(anns)):
            x = (anns[k]['bbox'][0] + (anns[k]['bbox'][2]/2)) / I.shape[1]
            y = (anns[k]['bbox'][1] + (anns[k]['bbox'][3]/2)) / I.shape[0]
            w =  anns[k]['bbox'][2] / I.shape[1]
            h =   anns[k]['bbox'][3] / I.shape[0]
            with open(save_dir + img['file_name'].split('.')[0] + '.txt', 'a') as f:
                f.write(str(i) + ' ' + str(x) + ' ' + str(y) \
                                       + ' ' + str(w) + ' ' + str(h) + '\n')
```

## コード全体

```.py
from pycocotools.coco import COCO
import matplotlib.pyplot as plt
import numpy as np
import skimage.io as io
import pylab
import os
import shutil

annFile='/content/coco/cocoface_instances_val2017.json'
coco=COCO(annFile)

class_txt = '/content/coco/classes.txt'

save_dir = '/content/coco/dataset/'

img_dir = '/content/coco/images/'

os.makedirs(save_dir, exist_ok=True)

os.makedirs(img_dir, exist_ok=True)

dataset_dir = os.path.abspath('..') + '/dataset/'

# classes.txtから必要なクラスのアノテーションを取得（今回はpersonとface）
with open(class_txt, 'r') as f:
    classes = f.readlines()

error_files = []

for i in range(0, len(classes)):
#for i in range(0, 1):
    class_name = classes[i].split('\n')[0]
    catIds = coco.getCatIds(catNms=[class_name]);
    imgIds = coco.getImgIds(catIds=catIds );
    print("Number of data of" + ' ' + class_name + ': ' + str(len(imgIds)))

    for j in range(0, len(imgIds)):
        img = coco.loadImgs(imgIds[j])[0]
        I = io.imread(img['coco_url'])
        
        # 画像を保存するディレクトリを変更してる
        os.chdir(img_dir)
        io.imsave(img['file_name'],I)
        os.chdir('../../')

        print(img['file_name'])
        annIds = coco.getAnnIds(imgIds=img['id'], catIds=catIds, iscrowd=None)
        anns = coco.loadAnns(annIds)
        try:
            I = io.imread(img['coco_url'])
        except KeyboardInterrupt:
            sys.exit
        except:
            error_files.append(img['file_name'])
            continue

        for k in range(0, len(anns)):
            x = (anns[k]['bbox'][0] + (anns[k]['bbox'][2]/2)) / I.shape[1]
            y = (anns[k]['bbox'][1] + (anns[k]['bbox'][3]/2)) / I.shape[0]
            w = anns[k]['bbox'][2] / I.shape[1]
            h = anns[k]['bbox'][3] / I.shape[0]
            with open(save_dir + img['file_name'].split('.')[0] + '.txt', 'a') as f:
                f.write(str(i) + ' ' + str(x) + ' ' + str(y) \
                                       + ' ' + str(w) + ' ' + str(h) + '\n')

if len(error_files) != 0:
    print("Error occures when files read")
    print("File: " + error_files)

print('completed.')
```

## colabで実験した人用
作成したファイルをローカルの環境にインストールするのは`zip`にしてから保存するのがおすすめです。

```.py
from google.colab import files
files.download("/content/download.zip")
```

## 終わりに
今回はcocoデータセットのjsonファイルをtxtファイルに変える方法を紹介しました。少しニッチな内容になってしまったかもしれませんが、アノテーションをする方の助けになると幸いです。