オリジナルから以下の変更を行っています。
YOLOv3のKeras版実装では、yolo_video.py
の引数名とyolo.py
の引数名が不一致であるため、yolo_video.py
で受け取った引数をyolo.py
で受け取ることができていません。
yolo_video.py
を次のように変更し、引数(model_path, anchors_path, classes_path)を受け取れることができるようにします。これに従い引数名も変更となります。
yolo_video.py
の一部(変更前)
parser.add_argument(
'--model', type=str,
help='path to model weight file, default ' + YOLO.get_defaults("model_path")
)
parser.add_argument(
'--anchors', type=str,
help='path to anchor definitions, default ' + YOLO.get_defaults("anchors_path")
)
parser.add_argument(
'--classes', type=str,
help='path to class definitions, default ' + YOLO.get_defaults("classes_path")
)
yolo_video.py
の一部(変更後)
parser.add_argument(
'--model_path', type=str,
help='path to model weight file, default ' + YOLO.get_defaults("model_path")
)
parser.add_argument(
'--anchors_path', type=str,
help='path to anchor definitions, default ' + YOLO.get_defaults("anchors_path")
)
parser.add_argument(
'--classes_path', type=str,
help='path to class definitions, default ' + YOLO.get_defaults("classes_path")
)
VoTTで出力したファイルには、アノテーション位置がfloatで書かれています。そのままでは
voc_annotation.py
で読み込めないので、下記のようにfloatからintに変換するようにソースを修正します。
引用:https://qiita.com/moto2g/items/dde7a55fceda862b2390
voc_annotation.py
の一部(変更前)
b = (int(xmlbox.find('xmin').text), int(xmlbox.find('ymin').text), int(xmlbox.find('xmax').text), int(xmlbox.find('ymax').text))
voc_annotation.py
の一部(変更後)
b = (int(float(xmlbox.find('xmin').text)),
int(float(xmlbox.find('ymin').text)),
int(float(xmlbox.find('xmax').text)),
int(float(xmlbox.find('ymax').text)))
train.py
で「tensorflow.python.framework.errors_impl.ResourceExhaustedError: 2 root error(s) found. 」というエラーが表示され最後まで処理が行われない場合は、GPUのメモリが足りない可能性があります。
以下の個所のbatch_size
を、エラーがでなくなるまで小さくする必要があります。
(batch_size指定は2か所ありますが、76行目の「note that more GPU memory is required after unfreezing the body」というコメントが記載されている個所となります。)
train.py
の一部(変更前)
batch_size = 32 # note that more GPU memory is required after unfreezing the body
以下では8
に変更していますが、GPUのメモリエラーが出なくなるまで小さくします。
私の環境(NVIDIA GeForce GTX 1080 Ti)では32
→8
まで減らす必要がありました。
train.py
の一部(変更後)
batch_size = 8 # note that more GPU memory is required after unfreezing the body
TensorBoardへの対応、mAPへの対応を行ったプルリクエストの反映と関連する設定について記載します。 (YOLOv3のKeras版実装の最終更新は2年ほど前のためか、上記プルリクエストは反映されていません)
-
tensorboard_logging.py
をダウンロードして追加します。tensorboard_logging.py([Raw]ボタンの結果を保存する)
keras-yolo3\tensorboard_logging.py
-
train_v2.py
をダウンロードして追加します。train_v2.py([Raw]ボタンの結果を保存する)
keras-yolo3\train_v2.py
-
model.py
をダウンロードして上書きします。model.py([Raw]ボタンの結果を保存する)
keras-yolo3\yolo3\train_v2.py
リサイズ用スクリプトとして、ここではsleeplessさんのkeras-yolo3のフォークのresize_images.py
を参考に、一部変更して使用します。
resize_images.py
をダウンロードし以下に配置します。
keras-yolo3\resize_images.py
元のソースから以下を変更しています。
- リサイズ後の大きさ
image_size = 320
を、yolov3.cfg
に合わせる形でimage_size = 416
に変更 - リサイズに使用する関数を、推論時と同じ
rgb_im.resize
に変更 - 正方形ではない画像を正方形に収まるようにリサイズする位置を、推測時と合わせる形で中央に変更
- 正方形になるように埋める背景色を、推論時と合わせる形で灰色(128,128,128)に変更
resize_images.py(image_sizeを416に変更済み)
import os
import sys
from glob import glob
from PIL import Image
def resize_images(images_dir, image_save_dir, image_size):
os.makedirs(image_save_dir, exist_ok=True)
img_paths = glob(os.path.join(images_dir, '*'))
for img_path in img_paths:
# image open
image = Image.open(img_path)
rgb_im = image.convert('RGB')
# resize image with unchanged aspect ratio using padding
iw, ih = image.size
w, h = (image_size,image_size)
scale = min(w/iw, h/ih)
nw = int(iw*scale)
nh = int(ih*scale)
# resize
rgb_im = rgb_im.resize((nw,nh), Image.BICUBIC)
# make background
back_ground = Image.new("RGB", (image_size,image_size), color=(128,128,128))
back_ground.paste(rgb_im, ((w-nw)//2, (h-nh)//2))
# make path
save_path = os.path.join(image_save_dir, os.path.basename(img_path))
end_index = save_path.rfind('.')
save_path = save_path[0:end_index]+'.png'
print('save',save_path)
back_ground.save(save_path,format='PNG')
def _main():
images_dir = 'images/' # input directory
image_save_dir = 'resize_image/' # output directory
image_size = 416
if len(sys.argv) > 1:
image_size = int(sys.argv[1])
resize_images(images_dir=images_dir, image_save_dir=image_save_dir, image_size=image_size)
if __name__ == '__main__':
_main()
参考:https://github.com/sleepless-se/keras-yolo3/blob/master/resize_images.py
「5. リサイズ用スクリプトの準備」で出力形式をPNGに変更したことに伴い、voc_annotation.py
を変更します。
voc_annotation.py
の一部(変更前)
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'%(wd, year, image_id))
voc_annotation.py
の一部(変更後)
list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.png'%(wd, year, image_id))
以下のように、voc_annotation.py
で出力されるファイル名とtrain.py
で定義されているファイルが不一致となっています。
ソース | ファイル名 |
---|---|
voc_annotation.py |
2007_train.txt |
train.py |
train.txt |
以下では、train.py
を変更していますが、voc_annotation.py
の変更を検討してもよろしいかと思います。
voc_annotation.py
で出力されるファイル名に合わせて、train.py
のソースを変更します。
train.py
の一部(変更前)
annotation_path = 'train.txt'
train.py
の一部(変更後)
annotation_path = '2007_train.txt'
YOLOv3のKeras版実装では、YOLOv3-tiny版のアンカーファイルの扱い方について、議論があるようです。Pull Request(503,622)、Issue(306,428,512,599,625)が上げられています。しかしYOLOv3のKeras版実装の最終更新は2年ほど前のためか、リポジトリへの反映は行われていません。
Darknet側の修正も踏まえ、最小のアンカーボックスを使う場合の反映方法を示します。
model.py
の一部(変更前)
anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
model.py
の一部(変更後)
anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [0,1,2]]
OpenCV 3系(Python 3.6)の環境では、最終フレームの認識が後にエラーが表示されることがあり、AttributeError: 'NoneType' object has no attribute '__array_interface__'
というエラーが表示されます。
yolo.py
の一部(変更前)
while True:
return_value, frame = vid.read()
image = Image.fromarray(frame)
yolo.py
の一部(変更後)
while True:
return_value, frame = vid.read()
if type(frame) == type(None): break
image = Image.fromarray(frame)
OpenCV 3系(Python 3.6)の環境では、MP4Vをavc1と認識することがあるため、int(cap.get(cv2.CAP_PROP_FOURCC))
による動画形式の自動認識がうまくいきません。
yolo.py
の一部(変更前)
video_FourCC = int(vid.get(cv2.CAP_PROP_FOURCC))
yolo.py
の一部(変更後)
# video_FourCC = int(vid.get(cv2.CAP_PROP_FOURCC))
# print(int(vid.get(cv2.CAP_PROP_FOURCC)).to_bytes(4, 'little').decode('utf-8'))
video_FourCC = cv2.VideoWriter_fourcc(*'MP4V') #OpenCV 3(Python 3.6)では、MP4Vがavc1と認識されるので固定値で設定
TensorBoard対応のプルリクエストはyolov3-tiny 対応ではなかったため、対応するように変更(Y.A.さんに感謝します。)
https://github.com/tfukumori/keras-yolo3/commit/564cce259df453366b64921f34412e5dcd65cc64
具体的には以下の変更を行っている([@caramel_3]https://qiita.com/caramel_3) さんに感謝します)。
- anchors_file引数のデフォルト値を削除(
model_data/yolo_anchors.txt
、またはmodel_data/tiny_yolo_anchors.txt
を指定する) - アンカーファイルのアンカー数に応じて使用するモデルを変更
- tinyモデルの読み込み関数を追加
- 重みに加えてモデルも保存するように変更
- アウトプットをtinyモデルに対応(出力数がも異なる)
- TensorBoard用のlogファイルをtinyモデルに対応
gitでソース管理する場合は、以下を.gitignore
に追加します。
# Custom
images/
resize_image/
VOCdevkit/
2007_test.txt
2007_train.txt
2007_val.txt
yolo_logs/
tmp_gt_files/
tmp_gt_files_org/
tmp_pred_files/
.vscode/
https://github.com/tfukumori/keras-yolo3/blob/master/yolo_webcam.py
接続したカメラに対してリアルタイムで認識を行う際に使用します。 オプションを指定することでファイル保存を行うこともできます。
接続したカメラへの認識のソースは以下を参考にしています。 https://qiita.com/yoyoyo_/items/10d550b03b4b9c175d9c
また、リアルタイムの認識ということでFPSが一定ではないため結果をCBRとして保存するための方法としては、以下を参考にしています。 https://madeinpc.blog.fc2.com/?no=1364