IPカメラのRTSP映像から人物とQRコードを検知してMQTTでおくるコードを作成しています。
GPU付のWindowsマシンで、MQTT Brokerをローカルで動かしPythonで作成したコードがこちら。
import cv2
import time
import json
import paho.mqtt.client as mqtt
import torch
from pyzbar.pyzbar import decode
from ultralytics import YOLO
# MQTT設定
BROKER = "localhost"
PORT = 1883
TOPIC = "detected"
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqtt_client.connect(BROKER, PORT, 60)
# RTSP カメラURL(低遅延化パラメータ付き)
RTSP_URL = "rtsp://Smartlight:smartlight@192.168.1.240:554/stream1?fflags=nobuffer&rtsp_transport=tcp"
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FPS, 15)
# 解像度を明示的に上げる(必要に応じて)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if not cap.isOpened():
print("カメラに接続できませんでした。")
exit()
# YOLOv8 モデルをGPUにロード
model = YOLO("yolov8n.pt")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f"YOLOv8 running on device: {device}")
# 状態管理
last_sent_time = 0
qr_display_expire = 0
last_qr_data = None
while True:
# 古いフレームを読み捨てて最新フレームだけ使う
for _ in range(5):
cap.read()
ret, frame = cap.read()
if not ret:
print("フレーム取得失敗。再接続中...")
cap.release()
time.sleep(3)
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
continue
current_time = time.time()
send_payload = {}
# ---------- QRコード検出(スケーリング付き) ----------
scale_factor = 2.0
scaled_frame = cv2.resize(frame, None, fx=scale_factor, fy=scale_factor)
qr_codes = decode(scaled_frame)
if qr_codes:
for qr in qr_codes:
data = qr.data.decode("utf-8")
x, y, w, h = qr.rect
# スケーリングされた座標を元に戻す
x = int(x / scale_factor)
y = int(y / scale_factor)
w = int(w / scale_factor)
h = int(h / scale_factor)
x_center = x + w // 2
y_center = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, data, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
send_payload["qr_code"] = {
"data": data,
"x": x_center,
"y": y_center
}
last_qr_data = data
qr_display_expire = current_time + 1.5
elif current_time > qr_display_expire:
last_qr_data = None
# ---------- 人物検出(YOLOv8 + GPU) ----------
results = model.predict(source=frame, classes=[0], conf=0.4, verbose=False, device=0)
person_data = {}
person_count = 0
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
conf = float(box.conf[0])
x_center = int((x1 + x2) / 2)
y_center = int((y1 + y2) / 2)
person_count += 1
person_data[f"person{person_count}"] = {
"x": x_center,
"y": y_center,
"confidence": round(conf, 2)
}
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
label = f"person ({x_center},{y_center}) {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 0), 2)
if person_data:
send_payload["persons"] = person_data
# ---------- MQTT送信(1秒に1回) ----------
if send_payload and (current_time - last_sent_time >= 1.0):
mqtt_client.publish(TOPIC, json.dumps(send_payload))
print(f"[MQTT] Sent: {json.dumps(send_payload, indent=2)}")
last_sent_time = current_time
# 表示
cv2.imshow("QR + Person Detection (Low Latency)", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 終了処理
cap.release()
cv2.destroyAllWindows()
mqtt_client.disconnect()
実行結果がこちら

人物についてはカメラの角度や大きさも含めて認識しますが、QRコードについては大きさやカメラに対して正面に表示&大きさで認識率が悪くなります。
ちなみに上記写真で認識しているQRコードの大きさが7.5cmx7.5㎝で、たまに6cmx6cmのQRコードも認識するような状況です。
現状はQRコードに角度をつけていますが、これが普通に机の上に置いていても認識されるように修正します。
QRコードンイン式をpyzbarからopencv-QRCodeDetectorに変更
QRコードが「カメラの正面でないと認識されない」という問題の原因は、現在使用している pyzbar
ライブラリが歪んだ(傾き・角度あり)QRコードの検出に弱いためです。斜めや遠距離、光の反射などにより正常に読み取れないケースが多くあります。
cv2.QRCodeDetector()であれば角度対応もしているということでコードを修正。

すると、先ほどまで認識していたQRコードも認識しなくなりました。また、少しスピードも遅くなっています。
pyzbar
を使ったまま、斜め方向や歪んだQRコードでも認識できるようにするための前処理として射影変換(透視補正)を実装
pyzbar
を使ったまま、斜め方向や歪んだQRコードでも認識できるようにするための前処理として、射影変換(透視補正)を加えたコードに修正。
import cv2
import time
import json
import paho.mqtt.client as mqtt
import torch
from pyzbar.pyzbar import decode
from ultralytics import YOLO
import numpy as np
# MQTT設定
BROKER = "localhost"
PORT = 1883
TOPIC = "detected"
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqtt_client.connect(BROKER, PORT, 60)
# RTSP カメラURL(低遅延化パラメータ付き)
RTSP_URL = "rtsp://Smartlight:smartlight@192.168.1.240:554/stream1?fflags=nobuffer&rtsp_transport=tcp"
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FPS, 15)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if not cap.isOpened():
print("カメラに接続できませんでした。")
exit()
# YOLOv8 モデルをGPUにロード
model = YOLO("yolov8n.pt")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f"YOLOv8 running on device: {device}")
last_sent_time = 0
qr_display_expire = 0
last_qr_data = None
def four_point_transform(image, rect):
(x, y, w, h) = rect
src_pts = np.array([
[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h]
], dtype="float32")
dst_pts = np.array([
[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]
], dtype="float32")
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(image, M, (w, h))
return warped
while True:
for _ in range(5):
cap.read()
ret, frame = cap.read()
if not ret:
print("フレーム取得失敗。再接続中...")
cap.release()
time.sleep(3)
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
continue
current_time = time.time()
send_payload = {}
# ---------- QRコード検出(スケーリングと補正あり) ----------
scale_factor = 2.0
scaled_frame = cv2.resize(frame, None, fx=scale_factor, fy=scale_factor)
qr_codes = decode(scaled_frame)
if qr_codes:
for qr in qr_codes:
data = qr.data.decode("utf-8")
x, y, w, h = qr.rect
x = int(x / scale_factor)
y = int(y / scale_factor)
w = int(w / scale_factor)
h = int(h / scale_factor)
roi = frame[y:y+h, x:x+w]
if roi.shape[0] > 0 and roi.shape[1] > 0:
# 補正した領域に再デコード
warped = four_point_transform(frame, (x, y, w, h))
decoded = decode(warped)
if decoded:
qr = decoded[0]
data = qr.data.decode("utf-8")
x_center = x + w // 2
y_center = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, data, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
send_payload["qr_code"] = {
"data": data,
"x": x_center,
"y": y_center
}
last_qr_data = data
qr_display_expire = current_time + 1.5
elif current_time > qr_display_expire:
last_qr_data = None
# ---------- 人物検出(YOLOv8 + GPU) ----------
results = model.predict(source=frame, classes=[0], conf=0.4, verbose=False, device=0)
person_data = {}
person_count = 0
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
conf = float(box.conf[0])
x_center = int((x1 + x2) / 2)
y_center = int((y1 + y2) / 2)
person_count += 1
person_data[f"person{person_count}"] = {
"x": x_center,
"y": y_center,
"confidence": round(conf, 2)
}
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
label = f"person ({x_center},{y_center}) {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 0), 2)
if person_data:
send_payload["persons"] = person_data
# ---------- MQTT送信(1秒に1回) ----------
if send_payload and (current_time - last_sent_time >= 1.0):
mqtt_client.publish(TOPIC, json.dumps(send_payload))
print(f"[MQTT] Sent: {json.dumps(send_payload, indent=2)}")
last_sent_time = current_time
# 表示
cv2.imshow("QR + Person Detection (Low Latency)", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 終了処理
cap.release()
cv2.destroyAllWindows()
mqtt_client.disconnect()

このようにカメラに正面でないQRコードも認識するようになりましたが、カメラの遅延が大きくなりました。
遅延を抑えるため検出頻度を1FPSに変更
フレームレートを制御し、人物検出・QR検出を1秒間に1回(=1 FPS)に制限するようコードを修正。
修正したコードを以下に提示します。
import cv2
import time
import json
import paho.mqtt.client as mqtt
import torch
from pyzbar.pyzbar import decode
from ultralytics import YOLO
import numpy as np
# MQTT設定
BROKER = "localhost"
PORT = 1883
TOPIC = "detected"
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqtt_client.connect(BROKER, PORT, 60)
# RTSP カメラURL
RTSP_URL = "rtsp://Smartlight:smartlight@192.168.1.240:554/stream1?fflags=nobuffer&rtsp_transport=tcp"
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FPS, 15)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)
if not cap.isOpened():
print("カメラに接続できませんでした。")
exit()
# YOLOv8 モデル
model = YOLO("yolov8n.pt")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f"YOLOv8 running on device: {device}")
last_sent_time = 0
qr_display_expire = 0
last_qr_data = None
def four_point_transform(image, rect):
(x, y, w, h) = rect
src_pts = np.array([
[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h]
], dtype="float32")
dst_pts = np.array([
[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]
], dtype="float32")
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(image, M, (w, h))
return warped
while True:
ret, frame = cap.read()
if not ret:
print("フレーム取得失敗。再接続中...")
cap.release()
time.sleep(3)
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
continue
current_time = time.time()
send_payload = {}
# ---------- 1秒ごとに処理 ----------
if current_time - last_sent_time >= 1.0:
# QRコード検出(pyzbar + 補正)
scale_factor = 2.0
scaled_frame = cv2.resize(frame, None, fx=scale_factor, fy=scale_factor)
qr_codes = decode(scaled_frame)
if qr_codes:
for qr in qr_codes:
data = qr.data.decode("utf-8")
x, y, w, h = qr.rect
x = int(x / scale_factor)
y = int(y / scale_factor)
w = int(w / scale_factor)
h = int(h / scale_factor)
roi = frame[y:y+h, x:x+w]
if roi.shape[0] > 0 and roi.shape[1] > 0:
warped = four_point_transform(frame, (x, y, w, h))
decoded = decode(warped)
if decoded:
qr = decoded[0]
data = qr.data.decode("utf-8")
x_center = x + w // 2
y_center = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, data, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
send_payload["qr_code"] = {
"data": data,
"x": x_center,
"y": y_center
}
last_qr_data = data
qr_display_expire = current_time + 1.5
elif current_time > qr_display_expire:
last_qr_data = None
# 人物検出(YOLO)
results = model.predict(source=frame, classes=[0], conf=0.4, verbose=False, device=0)
person_data = {}
person_count = 0
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
conf = float(box.conf[0])
x_center = int((x1 + x2) / 2)
y_center = int((y1 + y2) / 2)
person_count += 1
person_data[f"person{person_count}"] = {
"x": x_center,
"y": y_center,
"confidence": round(conf, 2)
}
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
label = f"person ({x_center},{y_center}) {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 0), 2)
if person_data:
send_payload["persons"] = person_data
# MQTT送信
if send_payload:
mqtt_client.publish(TOPIC, json.dumps(send_payload))
print(f"[MQTT] Sent: {json.dumps(send_payload, indent=2)}")
last_sent_time = current_time
# 表示(毎フレーム更新)
if last_qr_data:
cv2.putText(frame, f"Last QR: {last_qr_data}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
cv2.imshow("QR + Person Detection (1FPS)", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 終了処理
cap.release()
cv2.destroyAllWindows()
mqtt_client.disconnect()
上記の変更で遅延は改善されました。次は6cmx6cmのQRコードも検出できるように修正します。
対策 | 内容 |
---|---|
① 解像度を向上 | cap.set() で1920×1080などに設定 |
② QR検出時のスケーリング強化 | scale_factor を2.0→3.0や4.0に変更 |
③ 画像の鮮明化(前処理) | グレースケール → ヒストグラム均等化 or シャープ化 |
④ 読み取り範囲の補正強化 | 射影変換(既対応済)で傾き補正を継続 |
import cv2
import time
import json
import paho.mqtt.client as mqtt
import torch
from pyzbar.pyzbar import decode
from ultralytics import YOLO
import numpy as np
# MQTT設定
BROKER = "localhost"
PORT = 1883
TOPIC = "detected"
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqtt_client.connect(BROKER, PORT, 60)
# RTSP カメラURL
RTSP_URL = "rtsp://Smartlight:smartlight@192.168.1.240:554/stream1?fflags=nobuffer&rtsp_transport=tcp"
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FPS, 15)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) # 高解像度に変更
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
if not cap.isOpened():
print("カメラに接続できませんでした。")
exit()
# YOLOv8 モデル
model = YOLO("yolov8n.pt")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f"YOLOv8 running on device: {device}")
last_sent_time = 0
qr_display_expire = 0
last_qr_data = None
def four_point_transform(image, rect):
(x, y, w, h) = rect
src_pts = np.array([
[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h]
], dtype="float32")
dst_pts = np.array([
[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]
], dtype="float32")
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(image, M, (w, h))
return warped
while True:
ret, frame = cap.read()
if not ret:
print("フレーム取得失敗。再接続中...")
cap.release()
time.sleep(3)
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
continue
current_time = time.time()
send_payload = {}
# ---------- 1FPS制御 ----------
if current_time - last_sent_time >= 1.0:
# 小さなQRコードにも対応するためスケーリング強化
scale_factor = 3.0
scaled_frame = cv2.resize(frame, None, fx=scale_factor, fy=scale_factor)
gray_scaled = cv2.cvtColor(scaled_frame, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray_scaled)
qr_codes = decode(equalized)
if qr_codes:
for qr in qr_codes:
data = qr.data.decode("utf-8")
x, y, w, h = qr.rect
x = int(x / scale_factor)
y = int(y / scale_factor)
w = int(w / scale_factor)
h = int(h / scale_factor)
roi = frame[y:y+h, x:x+w]
if roi.shape[0] > 0 and roi.shape[1] > 0:
warped = four_point_transform(frame, (x, y, w, h))
warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
sharpened = cv2.equalizeHist(warped_gray)
decoded = decode(sharpened)
if decoded:
qr = decoded[0]
data = qr.data.decode("utf-8")
x_center = x + w // 2
y_center = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, data, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
send_payload["qr_code"] = {
"data": data,
"x": x_center,
"y": y_center
}
last_qr_data = data
qr_display_expire = current_time + 1.5
elif current_time > qr_display_expire:
last_qr_data = None
# 人物検出
results = model.predict(source=frame, classes=[0], conf=0.4, verbose=False, device=0)
person_data = {}
person_count = 0
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
conf = float(box.conf[0])
x_center = int((x1 + x2) / 2)
y_center = int((y1 + y2) / 2)
person_count += 1
person_data[f"person{person_count}"] = {
"x": x_center,
"y": y_center,
"confidence": round(conf, 2)
}
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
label = f"person ({x_center},{y_center}) {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 0), 2)
if person_data:
send_payload["persons"] = person_data
# MQTT送信
if send_payload:
mqtt_client.publish(TOPIC, json.dumps(send_payload))
print(f"[MQTT] Sent: {json.dumps(send_payload, indent=2)}")
last_sent_time = current_time
# 表示
if last_qr_data:
cv2.putText(frame, f"Last QR: {last_qr_data}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
cv2.imshow("QR + Person Detection (Enhanced)", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 終了処理
cap.release()
cv2.destroyAllWindows()
mqtt_client.disconnect()
今日の成果
小さいQRコードについては認識が悪く。それがカメラの性能なのかもしれませんが、本日の成果は最終的にこちら。
import cv2
import time
import json
import paho.mqtt.client as mqtt
import torch
from pyzbar.pyzbar import decode
from ultralytics import YOLO
import numpy as np
# MQTT設定
BROKER = "localhost"
PORT = 1883
TOPIC = "detected"
mqtt_client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
mqtt_client.connect(BROKER, PORT, 60)
# RTSP カメラURL
RTSP_URL = "rtsp://Smartlight:smartlight@192.168.1.240:554/stream1?fflags=nobuffer&rtsp_transport=tcp"
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
cap.set(cv2.CAP_PROP_FPS, 15)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080)
if not cap.isOpened():
print("カメラに接続できませんでした。")
exit()
# YOLOv8 モデル
model = YOLO("yolov8n.pt")
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model.to(device)
print(f"YOLOv8 running on device: {device}")
last_sent_time = 0
qr_display_expire = 0
last_qr_data = None
def four_point_transform(image, rect):
(x, y, w, h) = rect
src_pts = np.array([
[x, y],
[x + w, y],
[x + w, y + h],
[x, y + h]
], dtype="float32")
dst_pts = np.array([
[0, 0],
[w - 1, 0],
[w - 1, h - 1],
[0, h - 1]
], dtype="float32")
M = cv2.getPerspectiveTransform(src_pts, dst_pts)
warped = cv2.warpPerspective(image, M, (w, h))
return warped
while True:
for _ in range(4): cap.read()
ret, frame = cap.read()
if not ret:
print("フレーム取得失敗。再接続中...")
cap.release()
time.sleep(3)
cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
continue
current_time = time.time()
send_payload = {}
if current_time - last_sent_time >= 2.0:
scale_factor = 4.0
scaled = cv2.resize(frame, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_CUBIC)
gray = cv2.cvtColor(scaled, cv2.COLOR_BGR2GRAY)
eq = cv2.equalizeHist(gray)
blur = cv2.GaussianBlur(eq, (5, 5), 0)
sharp = cv2.addWeighted(eq, 2.0, blur, -1.0, 0) # 強シャープ化
qr_codes = decode(sharp)
if qr_codes:
for qr in qr_codes:
data = qr.data.decode("utf-8")
x, y, w, h = qr.rect
# スケールを戻す
x = int(x / scale_factor)
y = int(y / scale_factor)
w = int(w / scale_factor)
h = int(h / scale_factor)
# 再検出用に余白を広げて拡大
margin = 30
x_cut = max(0, x - margin)
y_cut = max(0, y - margin)
w_cut = w + margin * 2
h_cut = h + margin * 2
roi = frame[y_cut:y_cut + h_cut, x_cut:x_cut + w_cut]
if roi.shape[0] > 0 and roi.shape[1] > 0:
warped = four_point_transform(frame, (x_cut, y_cut, w_cut, h_cut))
warped_gray = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
sharp_roi = cv2.addWeighted(warped_gray, 2.0, cv2.GaussianBlur(warped_gray, (5, 5), 0), -1.0, 0)
decoded = decode(sharp_roi)
if decoded:
data = decoded[0].data.decode("utf-8")
x_center = x + w // 2
y_center = y + h // 2
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, data, (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2)
send_payload["qr_code"] = {
"data": data,
"x": x_center,
"y": y_center
}
last_qr_data = data
qr_display_expire = current_time + 4.0
elif current_time > qr_display_expire:
last_qr_data = None
# 人物検出(YOLOv8)
results = model.predict(source=frame, classes=[0], conf=0.5, verbose=False, device=0)
person_data = {}
person_count = 0
for box in results[0].boxes:
x1, y1, x2, y2 = map(int, box.xyxy[0])
conf = float(box.conf[0])
x_center = int((x1 + x2) / 2)
y_center = int((y1 + y2) / 2)
person_count += 1
person_data[f"person{person_count}"] = {
"x": x_center,
"y": y_center,
"confidence": round(conf, 2)
}
cv2.rectangle(frame, (x1, y1), (x2, y2), (255, 0, 0), 2)
label = f"person ({x_center},{y_center}) {conf:.2f}"
cv2.putText(frame, label, (x1, y1 - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (200, 200, 0), 2)
if person_data:
send_payload["persons"] = person_data
if send_payload:
mqtt_client.publish(TOPIC, json.dumps(send_payload), qos=0, retain=False)
print(f"[MQTT] Sent: {json.dumps(send_payload, indent=2)}")
last_sent_time = current_time
if last_qr_data:
cv2.putText(frame, f"Last QR: {last_qr_data}", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
display_frame = cv2.resize(frame, (960, 540))
cv2.imshow("QR + Person Detection (Small QR Enhanced)", display_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
mqtt_client.disconnect()
あとは、YOLOR8nでQRコードを抽出するモデルをつくって、検出する方法を調べます。