IoUの計算、BBOXの描画について
解決したいこと
BBOXの描画がうまくいきません
python初心者です。
画像認識で駐車場の空きスペースをウェブに表示するシステムの開発を行なっています。
下記のように事前にOpenCVで手動で描画した駐車場の座標(BBOX)とYOLOで画像認識した
車の座標(BBOX)のIoUを計算し、IoUが0.5未満のBBOXは緑色で描画し、IoUが0.5以上であれば車が駐車されていると判断し、赤色でBBOXを描画するコードの作成を行っていましたが
下記のエラーが出てしまい、うまく動きません。
解決方法を教えて下さい。コードの生成はChatGPTを元に行っています。
発生している問題・エラー
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
Cell In[1], line 55
53 image_path = 'photo01.jpg'
54 model = YOLO("yolov8x.pt")
---> 55 yolo_bboxes = get_yolo_bboxes(image_path, model)
57 # 手動で指定したバウンディングボックス情報
58 manual_bboxes = [
59 (350.1588, 29.0863, 415.2158, 130.1341), # 水色の左
60 (250.1588, 32.0863, 325.2158, 136.1341), # 水色の左の左
61 (650.1588, 18.0863, 725.2158, 112.1341), # 黄色の右
62 # 他のバウンディングボックス情報も追加
63 ]
Cell In[1], line 18, in get_yolo_bboxes(image_path, yolo_model)
16 # 予測を行い、バウンディングボックスを取得
17 results = yolo_model(input_image)
---> 18 bboxes = results.xyxy[0].cpu().numpy() # バウンディングボックス情報をNumpy配列に変換
19 return bboxes
AttributeError: 'list' object has no attribute 'xyxy'
コード
manual_bboxes内の座標に関して、上4つは車の座標、その他は駐車場の座標です。
import cv2
import numpy as np
from ultralytics import YOLO
# 画像を0から1の範囲に正規化する前処理関数の例
def preprocess_image(image):
# 画像を0から255の範囲から0から1の範囲に正規化
return image.astype(np.float32) / 255.0
# YOLOを使用して画像からバウンディングボックスを取得する関数
def get_yolo_bboxes(image_path, yolo_model):
image = cv2.imread(image_path)
# 画像をYOLOモデルに適切な形式に変換
# YOLOモデルによっては前処理が必要です
input_image = preprocess_image(image)
# 予測を行い、バウンディングボックスを取得
results = yolo_model(input_image)
bboxes = results.xyxy[0].cpu().numpy() # バウンディングボックス情報をNumpy配列に変換
return bboxes
# IoUを計算する関数
def calculate_iou(bbox1, bbox2):
# bbox1とbbox2の座標情報を取得
x1, y1, x2, y2 = bbox1
x3, y3, x4, y4 = bbox2
# bbox1とbbox2の交差領域の座標を計算
x_left = max(x1, x3)
y_top = max(y1, y3)
x_right = min(x2, x4)
y_bottom = min(y2, y4)
if x_right < x_left or y_bottom < y_top:
return 0.0 # 重なっていない場合
# 交差領域の幅と高さを計算
intersection_width = x_right - x_left
intersection_height = y_bottom - y_top
# 交差領域の面積と各bboxの面積を計算
intersection_area = intersection_width * intersection_height
bbox1_area = (x2 - x1) * (y2 - y1)
bbox2_area = (x4 - x3) * (y4 - y3)
# IoUを計算
iou = intersection_area / float(bbox1_area + bbox2_area - intersection_area)
return iou
# しきい値を設定
iou_threshold = 0.5
# 画像からバウンディングボックスを取得
image_path = 'photo01.jpg'
model = YOLO("yolov8x.pt")
yolo_bboxes = get_yolo_bboxes(image_path, model)
# 手動で指定したバウンディングボックス情報
manual_bboxes = [
(9.8447, 143.9799, 182.9339, 231.6702),# 入口白
(540.3762, 23.9367, 618.1284, 118.5072),# 右上黄色
(590.2038, 272.3892, 710.4009, 427.5425),# 右下赤
(450.1588, 26.0863, 525.2158, 124.1341),# 水色
(350.1588, 29.0863, 415.2158, 130.1341),# 水色の左
(250.1588, 32.0863, 325.2158, 136.1341),# 水色の左の左
(650.1588, 18.0863, 725.2158, 112.1341),# 黄色の右
(470.2038, 280.3892, 570.4009, 440.5425),# 右下赤の左
(350.2038, 288.3892, 440.4009, 447.5425),# 右下赤の左の左
(300.2038, 296.3892, 210.4009, 454.5425),# 右下赤の左の左の左
(550.3762, 25.9367, 628.1284, 128.5072),# 右上黄色オーバーラップ
(600.2038, 282.3892, 720.4009, 437.5425),# 右下赤オーバーラップ
(440.1588, 32.0863, 510.2158, 150.1341),# 水色オーバーラップ
]
# 画像を読み込み
image = cv2.imread(image_path)
# YOLOで取得したバウンディングボックスと手動で指定したバウンディングボックスのIoUを計算し、
# しきい値を超えた場合に赤色でBBOXを描画
for yolo_bbox in yolo_bboxes:
for manual_bbox in manual_bboxes:
iou = calculate_iou(yolo_bbox, manual_bbox)
if iou > iou_threshold:
x1, y1, x2, y2 = manual_bbox
# 赤色のBBOXを描画
cv2.rectangle(image, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)
# 画像を表示または保存
cv2.imshow('Image with BBOX', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
自分で試したこと
YOLOを使わない類似のコードも作成したのですが、複数のIoUが0.5を超えるBBOXがあるのに一台しか赤色のBBOXで描画されませんでした(上ページの画像)。また、今後はリアルタイムの検出に近づけるために動画での物体検出を行う予定のため、できれば物体検出にはYOLOを使用したいと考えています。
# YOLO不使用、水色のみ変更可能
import cv2
import numpy as np
# IoU計算関数
def calculate_iou(box1, box2):
x1, y1, x2, y2 = box1
x3, y3, x4, y4 = box2
intersection_x1 = max(x1, x3)
intersection_y1 = max(y1, y3)
intersection_x2 = min(x2, x4)
intersection_y2 = min(y2, y4)
intersection_area = max(0, intersection_x2 - intersection_x1) * max(0, intersection_y2 - intersection_y1)
box1_area = (x2 - x1) * (y2 - y1)
box2_area = (x4 - x3) * (y4 - y3)
iou = intersection_area / float(box1_area + box2_area - intersection_area)
return iou
# 画像読み込み
image = cv2.imread('/Users/adachikouki/yolov8/photo01.jpg')
# YOLOv5の出力結果からBBOX情報を取得
bboxes = [
(9.8447, 143.9799, 182.9339, 231.6702),# 入口白
(540.3762, 23.9367, 618.1284, 118.5072),# 右上黄色
(590.2038, 272.3892, 710.4009, 427.5425),# 右下赤
(450.1588, 26.0863, 525.2158, 124.1341),# 水色
(350.1588, 29.0863, 415.2158, 130.1341),# 水色の左
(250.1588, 32.0863, 325.2158, 136.1341),# 水色の左の左
(650.1588, 18.0863, 725.2158, 112.1341),# 黄色の右
(470.2038, 280.3892, 570.4009, 440.5425),# 右下赤の左
(350.2038, 288.3892, 440.4009, 447.5425),# 右下赤の左の左
(300.2038, 296.3892, 210.4009, 454.5425),# 右下赤の左の左の左
(550.3762, 25.9367, 628.1284, 128.5072),# 右上黄色オーバーラップ
(600.2038, 282.3892, 720.4009, 437.5425),# 右下赤オーバーラップ
(440.1588, 32.0863, 510.2158, 150.1341),# 水色オーバーラップ
]
# BBOXの色を変えて出力
for i in range(len(bboxes)):
for j in range(i + 1, len(bboxes)):
box1 = bboxes[i]
box2 = bboxes[j]
iou = calculate_iou(box1, box2)
# IoUをターミナルに出力
print(f"IoU between bbox {i+1} and bbox {j+1}: {iou:.4f}")
if iou >= 0.5:
color = (0, 0, 255) # IoUが0.5以上の場合、赤色に設定
else:
color = (0, 255, 0) # IoUが0.5未満の場合、緑色に設定
x1, y1, x2, y2 = map(int, box1) # 座標を整数に変換
cv2.rectangle(image, (x1, y1), (x2, y2), color, 2)
# 結果の画像を表示
cv2.imshow('Result', image)
cv2.waitKey(0)
cv2.destroyAllWindows()