roboflow Workspaceでアノテーションしたデータを Google Colabで使う

roboflow Workspaceでアノテーションしたデータを Google Colabで使う

ダウンロードして解凍したファイル
anotation_test.v1i.yolov8-obb

Google Drive へアップロード

これをGoogleColabで学習する

使用するのはT4GPU

まずは

# Install ultralytics
!pip install ultralytics


ultralyticsをインストール

from google.colab import drive
drive.mount('/content/drive')

でGoogle Driveのデータを使うためドライブをマウント

次にYamlファイルを探す

/content/drive/MyDrive/InventoryControl/DailyNecessities/anotation_test.v1i.yolov8-obb/data.yaml

がパスだったので

!yolo obb train data=/content/drive/MyDrive/InventoryControl/DailyNecessities/anotation_test.v1i.yolov8-obb/data.yaml pretrained=yolov8n-obb.pt epochs=100 imgsz=640 exist_ok=True

で実行

エポック100回で学習
モデルは小型のyolov8n-obb.ptにしましたが、
(n)の部分を(s),(l),(x)など変更可能

なお実行結果は

Runs/obb/train/

の中に格納されている

学習したモデルで推論するので
Testフォルダにある画像で推論するので
対象ファイルを右クリックしコピー

best.pt
を右クリックしパスをコピーする

今回なら
/content/runs/obb/train/weights/best.pt
となる

あとは
model= にモデルのパス と source=’ ‘ にテスト画像パス を設定して推論

とりあえず

/content/drive/MyDrive/InventoryControl/DailyNecessities/anotation_test.v1i.yolov8-obb/test/images/Baskulin3_rot270_006_jpg.rf.8e9ca7b3fac80e0bb5b1a45dedae21d1.jpg

の画像を使うことにする

!yolo obb predict model=/content/runs/obb/train/weights/best.pt source='/content/drive/MyDrive/InventoryControl/DailyNecessities/anotation_test.v1i.yolov8-obb/test/images/Baskulin3_rot270_006_jpg.rf.8e9ca7b3fac80e0bb5b1a45dedae21d1.jpg' save=True save_txt=True exist_ok=True

を実行

とりあえず1つだけの画像でテストしたら

/content/runs/obb/predict/labels/Baskulin3_rot270_006_jpg.rf.8e9ca7b3fac80e0bb5b1a45dedae21d1.txt

にラベルがあり

/content/runs/obb/predict/Baskulin3_rot270_006_jpg.rf.8e9ca7b3fac80e0bb5b1a45dedae21d1.jpg

に検出がされた画像が出ていた

0 0.0758179 0.237794 0.0758906 0.693102 0.643104 0.693011 0.643031 0.237704

最初の0は
クラスだが
今回は1つしかクラスがないので0になる

アノテーション(ラベル付け)をしていない画像を推論して、推論結果が正しかった場合、検出ラベルを学習用のデータとすることもできます。OBBのアノテーションは多少手間がかかります。そこで、最初に少量のアノテーションを行い、次にアノテーションしていない画像で推論を行い、その後推論に成功したラベルを学習データに加えて画集データを充実していくこともできます。
とのこと

次は対象物のカウント

カウントの仕方は色々ありますが、今回は、クラス設定が1つということもあり、簡単なカウントの1つとしてこのラベルの行数をカウントする方法にしました。検出したクラスのチェックもしていません。
先ほどのノートブックに以下のコードを追加し実行して、検出物のカウントします。テスト画像の変更は、source_file = の設定で変更できます。

せっかくなので
ドラッグストアでバスクリンの種類の確認のため撮影した画像でカウントの実験をする
(以前バスクリンの種類を間違えたら家族に不評だったので)

パスは

/content/drive/MyDrive/PXL_20240529_101238381.jpg

なのでこれを指定する

対象の画像を変更するには
source_fileのパスを変更する

以下変更コード

import os
import subprocess

source_file = '/content/drive/MyDrive/PXL_20240529_101238381.jpg'

# テキストファイルのパスを構築(画像ファイル名と同じ)
file_name, file_extension = os.path.splitext(source_file)
label_file_path = '/content/runs/obb/predict/labels/' + os.path.basename(file_name) + '.txt'

# ファイルの存在を確認し、存在する場合は削除
if os.path.exists(label_file_path):
    os.remove(label_file_path)

# YOLOを使用して予測を実行
!yolo obb predict model=/content/runs/obb/train/weights/best.pt source='{source_file}' save=True save_txt=True exist_ok=True

# ファイルが存在する場合のみ、テキストファイルの行数を取得して表示
if os.path.exists(label_file_path):
    num_lines = subprocess.check_output(["wc", "-l", label_file_path]).decode().split()[0]
    print("バスクリンの数は", num_lines)
else:
    print("ファイルが見つかりませんでした。")

これを実行するとバスクリンの数をカウントしているのがわかる

実行結果は

Ultralytics YOLOv8.2.25 🚀 Python-3.10.12 torch-2.3.0+cu121 CUDA:0 (Tesla T4, 15102MiB)
YOLOv8n-obb summary (fused): 187 layers, 3077414 parameters, 0 gradients, 8.3 GFLOPs

/usr/local/lib/python3.10/dist-packages/torch/nn/modules/conv.py:456: UserWarning: Plan failed with a cudnnException: CUDNN_BACKEND_EXECUTION_PLAN_DESCRIPTOR: cudnnFinalize Descriptor Failed cudnn_status: CUDNN_STATUS_NOT_SUPPORTED (Triggered internally at ../aten/src/ATen/native/cudnn/Conv_v8.cpp:919.)
  return F.conv2d(input, weight, bias, self.stride,
image 1/1 /content/drive/MyDrive/PXL_20240529_101238381.jpg: 640x512 75.5ms
Speed: 4.4ms preprocess, 75.5ms inference, 983.9ms postprocess per image at shape (1, 3, 640, 512)
Results saved to runs/obb/predict
2 labels saved to runs/obb/predict/labels
💡 Learn more at https://docs.ultralytics.com/modes/predict
バスクリンの数は 4

となっているので
同じバスクリンでも違う種類は識別できているのがわかる

引き続き
https://axross-recipe.com/recipes/1302#requiredSkills
を参考に学習の後に
PCやラズパイでの推論を試す

roboflow Workspaceでアノテーションその1

roboflow Workspaceでアノテーション

https://axross-recipe.com/recipes/1469
を参考に行う

https://blog.roboflow.com/train-yolov8-obb-model/
のチュートリアルも参考にする

https://blog.roboflow.com
へアクセスし
GoogleIDなどで、Sign InすればOK

その場合
Continue with Google をクリック

ユーザ名を入力すると
プランが2つ出る
Free の無料か
Starter traialの月額249$
のどっちかになるので

とりあえずはFree にする

これでCreate workspace をクリック

次に
Invite teammates.
Add collaborators to help with labeling, upload data, train models, and more.
チームメイトを招待します。
ラベル付け、データのアップロード、モデルのトレーニングなどを支援するコラボレーターを追加します。

とあるけど
とりあえずSkipでOK

これでプロジェクトの作成画面になるが
デフォルトだとライセンスが
CC BY 4.0
になっている

Project Name は
anotation_test
とした

Annotation Group
注釈グループ
が識別のため必要らしい

とりあえずバスクリンなので
Baskulin

Project Typeには
Object Detection
を選択

これで
Create Project をクリック

次に画像ファイル
もしくは画像フォルダを選択する

ここでバスクリンを撮影したフォルダを指定するが
せっかくなので
画像の水増しをする

まず
Pixcel 8 で撮影したバスクリンの画像を
Google Photo からダウンロード

4枚の写真になっているので
これを水増しする

なお複数の写真をダウンロードすると
圧縮ファイルになっているのでこれを解凍する

vim generate_images.py

で内容を

import sys
import os
from PIL import Image

# コマンドライン引数から画像ファイル名を取得
if len(sys.argv) != 2:
    print("Usage: python generate_images.py imagefile.png")
    sys.exit(1)

image_file = sys.argv[1]

# 画像を読み込む
try:
    image = Image.open(image_file)
except IOError:
    print(f"Could not open the image file {image_file}")
    sys.exit(1)

# ファイル名と拡張子を分離し、ディレクトリ名を決定
file_name, file_extension = os.path.splitext(os.path.basename(image_file))
directory_name = file_name

# ディレクトリが存在しない場合は作成
if not os.path.exists(directory_name):
    os.makedirs(directory_name)

# 画像の変形と保存を行う関数
def save_images(image, prefix, transform, count=100):
    for i in range(count):
        filename = f'{prefix}{i+1:03}{file_extension}'
        filepath = os.path.join(directory_name, filename)
        transformed_image = image.transpose(transform)
        transformed_image.save(filepath)

# 各変換を適用して画像を保存
save_images(image, f'{file_name}_', Image.FLIP_TOP_BOTTOM, 100)
save_images(image, f'{file_name}_rot90_', Image.ROTATE_90, 100)
save_images(image, f'{file_name}_rot270_', Image.ROTATE_270, 100)

という画像を回転させてコピーするスクリプトを作成

次に写真をコピーしておく

cp ~/Downloads/Photos-001\ \(1\)/PXL_20240504_1732* .

ファイル名を変えた方が楽なので
ターミナルで

i=1
for file in PXL_20240504_173232354.jpg PXL_20240504_173242547.jpg PXL_20240504_173237123.jpg PXL_20240504_173253414.jpg; do
  mv "$file" "basclin$i.jpg"
  i=$((i + 1))
done

を実行

すると

basclin1.jpg
basclin2.jpg
basclin3.jpg
basclin4.jpg

というようにファイル名が変わる

よくみたらスペルミスなので

i=1
for file in basclin*.jpg; do
  mv "$file" "Baskulin$i.jpg"
  i=$((i + 1))
done

で修正

これでファイル名が

Baskulin1.jpg
Baskulin2.jpg
Baskulin3.jpg
Baskulin4.jpg

となったので

python generate_images.py Baskulin1.jpg
python generate_images.py Baskulin2.jpg
python generate_images.py Baskulin3.jpg
python generate_images.py Baskulin4.jpg

でファイルを量産

これで各ファイルごとのフォルダができたので
これを1つのフォルダにまとめる

ls -d */ | grep Bas


Basと書かれたディレクトリのみ表示できるので

move_files.sh

というスクリプトを作成

#!/bin/bash

# 移動先のディレクトリを作成(存在しない場合)
mkdir -p baskulin

# Baskulinで始まる全ディレクトリのファイルを baskulin ディレクトリに移動
for dir in Baskulin*/; do
    # ディレクトリ内のファイルを baskulin に移動
    mv "$dir"* baskulin/
done

echo "All files have been moved to the 'baskulin' directory."

として保存

chmod +x move_files.sh

で実行権限付与

./move_files.sh

で実行すれば全てのファイルが
1つのフォルダに移動される

そしてこのフォルダをアップロード

これでsave and continueをクリック

なお100枚以上に増やしたけど
同じ画像の場合は全て1つのものとしてみなすため
意味がなかった

回転させたりした場合は
異なる画像としてカウントされる

これでデータがアップできたので
次はアノテーション