YOLOで物体検出 その2(Python、Node-RED)
はじめに
今回はYOLOをPythonで実行してみました。
以前の記事でYOLOを試したときは簡単なコマンドしか試していませんでしたが、実際にロボットに搭載して処理を行うにはPythonで実行する必要がありそうです。
検出した物体の画像上での座標は取得したかったので試してみました。
▼以前の記事はこちら
環境を構築する
今回もWindows 10のノートPCで実行しています。
まずは仮想環境を作成して、パッケージをインストールします。WARNINGでPythonのバージョンが3.10以上を推奨されていたので、バージョンを指定して作成することをお勧めします。
▼一応Pythonの3.8、3.9でも実行できました。
Pythonのバージョンを3.10に指定するのであれば、以下のコマンドを実行します。
py -3.10 -m venv yolo310
cd yolo310
.\Scripts\activate
pip install ultralytics
サンプルプログラムを試してみる
▼以下のUltralyticsのページに、Pythonのサンプルコードがありました。
https://docs.ultralytics.com/ja/usage/python
TrainやValはデータセットを利用するときに実行するとして、今回はPredictを実行しました。
▼Unreal Engineの画面ですが、この画像で予測を行ってみました。
▼画像ファイルから物体検出を行うプログラムはこちら
import cv2
from PIL import Image
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
# accepts all formats - image/dir/Path/URL/video/PIL/ndarray. 0 for webcam
results = model.predict(source="C:/Users/mgs_1/Downloads/unreal.png", save=True)
# from PIL
im1 = Image.open("bus.jpg")
results = model.predict(source=im1, save=True) # save plotted images
# from ndarray
im2 = cv2.imread("bus.jpg")
results = model.predict(source=im2, save=True, save_txt=True) # save predictions as labels
# from list of PIL/ndarray
results = model.predict(source=[im1, im2])
▼検出結果が表示されています。
model.predictにsave=Trueを含んでいたものは、runs/detect/predict11フォルダに検出後の画像が保存されていました。フォルダ名のpredictの後の番号は、保存されるたびに増加していきます。
▼Unreal Engineの画像でも、 personや potted plantとして検出されています。
▼以前試したバスの画像も検出されていました。
model.predictのsourceが0のときはwebcamとのことだったので、その部分だけ残してみました。
▼プログラムはこちら
import cv2
from PIL import Image
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model.predict(source="0")
▼カメラが起動して、検出した物体の一覧が表示されました。
ノートPCのフロントカメラに映っていた私を検出したのだと思います。カメラからずれると、椅子が検出されました。
座標を取得するプログラムを実行する
ChatGPTに相談しながら、検出した物体の座標を取得するプログラムを作成しました。OpenCVでカメラの映像や、検出した領域を囲うバウンディングボックスを描画するようにしています。
▼プログラムはこちら
import cv2
from ultralytics import YOLO
# YOLO モデルのロード
model = YOLO("yolov8n.pt") # 必要に応じてモデルを変更
# カメラのキャプチャ(デフォルトカメラを使用)
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("カメラを開くことができませんでした")
exit()
while True:
ret, frame = cap.read()
if not ret:
print("フレームを取得できませんでした")
break
# フレームの推論
results = model(frame)
# 検出結果の処理
for result in results:
boxes = result.boxes # バウンディングボックスの情報
for box in boxes:
# 座標の取得
x1, y1, x2, y2 = box.xyxy[0] # 左上と右下の座標
x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)
# クラスとスコアの取得
cls = int(box.cls[0]) # クラスID
score = float(box.conf[0]) # 信頼度
# クラス名の取得(モデルによって異なります)
class_name = model.names[cls]
# 座標の表示
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
label = f"{class_name} {score:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)
# 座標の出力(必要に応じて利用)
print(f"検出: {class_name}, 座標: ({x1}, {y1}), ({x2}, {y2}), 信頼度: {score}")
# フレームの表示
cv2.imshow("YOLO Real-Time Detection", frame)
# 'q' キーで終了
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# リソースの解放
cap.release()
cv2.destroyAllWindows()
実際に実行してみました。
▼ほとんどリアルタイムで物体を検出することができています。
▼ターミナルでは、検出した物体の種類と座標が表示されています。
▼取得した座標を次のプログラムにどう渡すかは、考える必要がありそうです。
Node-REDで実行する
Pythonで実行できたということは、Node-REDでも実行できるということです。私が開発したpython-venvノードを利用してみました。
▼最近アップデートして、仮想環境に追加された実行ファイルも実行できるようになりました。yolo.exeも実行できます。
https://flows.nodered.org/node/@background404/node-red-contrib-python-venv
▼全体のフローはこちら
[{"id":"7e90aba4fd463188","type":"venv","z":"790506c326ae6cc7","venvconfig":"a567c3477dd0b46c","name":"","code":"import cv2\nfrom ultralytics import YOLO\n\n# YOLO モデルのロード\nmodel = YOLO(\"yolov8n.pt\") # 必要に応じてモデルを変更\n\n# カメラのキャプチャ(デフォルトカメラを使用)\ncap = cv2.VideoCapture(0)\n\nif not cap.isOpened():\n print(\"カメラを開くことができませんでした\")\n exit()\n\nwhile True:\n ret, frame = cap.read()\n if not ret:\n print(\"フレームを取得できませんでした\")\n break\n\n # フレームの推論\n results = model(frame)\n\n # 検出結果の処理\n for result in results:\n boxes = result.boxes # バウンディングボックスの情報\n for box in boxes:\n # 座標の取得\n x1, y1, x2, y2 = box.xyxy[0] # 左上と右下の座標\n x1, y1, x2, y2 = int(x1), int(y1), int(x2), int(y2)\n \n # クラスとスコアの取得\n cls = int(box.cls[0]) # クラスID\n score = float(box.conf[0]) # 信頼度\n\n # クラス名の取得(モデルによって異なります)\n class_name = model.names[cls]\n\n # 座標の表示\n cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)\n label = f\"{class_name} {score:.2f}\"\n cv2.putText(frame, label, (x1, y1 - 10), \n cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 2)\n\n # 座標の出力(必要に応じて利用)\n print(f\"検出: {class_name}, 座標: ({x1}, {y1}), ({x2}, {y2}), 信頼度: {score}\")\n\n # フレームの表示\n cv2.imshow(\"YOLO Real-Time Detection\", frame)\n\n # 'q' キーで終了\n if cv2.waitKey(1) & 0xFF == ord('q'):\n break\n\n# リソースの解放\ncap.release()\ncv2.destroyAllWindows()\n","continuous":true,"x":790,"y":3120,"wires":[["4ce42358d13464f6"]]},{"id":"d70969095d48b329","type":"inject","z":"790506c326ae6cc7","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":620,"y":3120,"wires":[["7e90aba4fd463188"]]},{"id":"4ce42358d13464f6","type":"debug","z":"790506c326ae6cc7","name":"debug 168","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":950,"y":3120,"wires":[]},{"id":"0d7f40cf0fc91a4e","type":"pip","z":"790506c326ae6cc7","venvconfig":"a567c3477dd0b46c","name":"","arg":"ultralytics","action":"install","tail":false,"x":790,"y":3060,"wires":[["793ea04a919a8d51"]]},{"id":"7d15423c58236d08","type":"inject","z":"790506c326ae6cc7","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":620,"y":3060,"wires":[["0d7f40cf0fc91a4e"]]},{"id":"793ea04a919a8d51","type":"debug","z":"790506c326ae6cc7","name":"debug 169","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":950,"y":3060,"wires":[]},{"id":"67008fa0e8463cf5","type":"comment","z":"790506c326ae6cc7","name":"YOLO Stream","info":"","x":610,"y":3000,"wires":[]},{"id":"a567c3477dd0b46c","type":"venv-config","venvname":"YOLO","version":"3.10"}]
▼pipノードでは、ultralyticsだけインストールしています。
▼連続実行モードにすると、出力があればすぐにメッセージとして送信します。
▼実行すると、検出した物体が順次デバッグノードに送信されます。
オブジェクトを追加したUnreal Engineの画面で検出を行ってみました。
▼モニターに表示されているものだと、検出精度は悪いような気がします。リンゴがあまり検出されていません。
最後に
リアルタイムで物体を検出している様子を確認できると面白いですね。ChatGPTで生成したプログラムでも十分実行できたので、要件を追加すれば用途に合ったプログラムになりそうです。
検出した物体の座標と深度カメラの座標を照らし合わせることで、距離を求めるという使い方ができるそうです。深度カメラが手元に無いのですが、入手出来たら試したいなと思っています。