
目次
目的
①下記の記事を参考にTP-Linkのネットワークカメラ4台をLAN内・OBSで視聴していたが、頻繁に画面が停止し、都度クリックして受信を再開させる必要があった。
https://qiita.com/takashima-yoi/items/4ae7bd535ddee91a33a9
②ispyなどのほかのソフトでは再現しないのでOBSの問題と思われる。
③全画面/4分割レイアウトでRTSP映像を常時表示できる既存ソフトはないため、自分でなんとかしたい。
概要
以下の要件を満たすexeファイルをChatGPTで生成する。
- 1920*1080の映像を4つ受信し、左上・右上・左下・右下にレイアウト・リアルタイム再生する。
- RTSPのURLはiniファイルで管理し、必要に応じて変更できるようにする。
生成プロンプト
GPT-4oで生成した。
以下のEXEファイルを生成したいです。できますか。
■機能
・rtspの映像データを受信し、ウィンドウ上に表示させる。
・映像データは4系統で受信する。
・受信するRTSPのURLは同ファイル下のiniファイルに入力する。
・映像データはFHD(1920*1080)で受信する。以下のレイアウトで表示したい。
①②
③④
・ウィンドウサイズに合わせて、①②③④はリサイズされること。
完成したもの・使い方
STEP
Pythonの公式サイトからPythonをインストール
以下からDL・インストールする。
インストール時に「Add Python to PATH」にチェックを入れることを忘れないように。
https://www.python.org/downloads
STEP
ライブラリのインストール
cmd>こちらのコマンドを実行する。
pip install opencv-python configparser pyinstaller
STEP
スクリプトの作成
任意の箇所にファイル名:display_rtsp.pyを作成し、以下をコピー。
import cv2
import configparser
import numpy as np
# INIファイルからRTSP URLを読み取る
config = configparser.ConfigParser()
config.read('streams.ini')
streams = [
config.get('RTSP', 'stream1'),
config.get('RTSP', 'stream2'),
config.get('RTSP', 'stream3'),
config.get('RTSP', 'stream4')
]
# 各ストリームに対応するVideoCaptureオブジェクトを作成
caps = [cv2.VideoCapture(url) for url in streams]
for i, cap in enumerate(caps):
if not cap.isOpened():
print(f"Failed to open stream {i+1}: {streams[i]}")
exit(1)
def resize_frame(frame, size):
return cv2.resize(frame, size)
# ウィンドウを作成(修正点1)
cv2.namedWindow('RTSP Streams', cv2.WINDOW_NORMAL)
cv2.resizeWindow('RTSP Streams', 1920, 1080)
while True:
frames = []
for cap in caps:
ret, frame = cap.read()
if not ret:
continue
frames.append(frame)
if len(frames) < 4:
continue
# ウィンドウサイズを取得(修正点2)
screen_width = cv2.getWindowImageRect('RTSP Streams')[2]
screen_height = cv2.getWindowImageRect('RTSP Streams')[3]
frame_width = screen_width // 2
frame_height = screen_height // 2
# 各フレームをリサイズ
frames_resized = [resize_frame(frame, (frame_width, frame_height)) for frame in frames]
# 4フレームを1つのウィンドウに配置
top_row = np.hstack((frames_resized[0], frames_resized[1]))
bottom_row = np.hstack((frames_resized[2], frames_resized[3]))
combined_frame = np.vstack((top_row, bottom_row))
cv2.imshow('RTSP Streams', combined_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
for cap in caps:
cap.release()
cv2.destroyAllWindows()
※Geminiによるコードレビュー:OK
コードレビュー
1. 全体的な印象
- 要件を満たすコードであることは確認できました。
- iniファイルからRTSP URLを読み込み、4つのストリームを別々のVideoCaptureオブジェクトで取得している点、フレームサイズをウィンドウサイズに合わせてリサイズしている点など、良い実装です。
- いくつかの改善点と、潜在的な問題点が見受けられますので、以下に詳細を説明します。
2. 具体的な改善点
2.1 エラー処理
VideoCapture.isOpened()
を使用して、ストリームが開けなかった場合のエラー処理を行っていますが、エラーメッセージにストリーム番号が含まれていません。- どのストリームが開けなかったのかを特定できるように、エラーメッセージにストリーム番号を含めるようにしましょう。
- 例:
print(f"Failed to open stream {i+1}: {streams[i]}")
2.2 フレーム取得のエラー処理
cap.read()
でフレームを取得していますが、ret
がFalseの場合のエラー処理がありません。- フレーム取得に失敗した場合も、適切なエラー処理を行うようにしましょう。
- 例:
if not ret: continue
2.3 ウィンドウサイズの取得
cv2.getWindowImageRect('RTSP Streams')
を使用してウィンドウサイズを取得していますが、この関数は非推奨です。- 代わりに、
cv2.getWinProperty('RTSP Streams', cv2.WND_PROP_FRAME_WIDTH)
とcv2.getWinProperty('RTSP Streams', cv2.WND_PROP_FRAME_HEIGHT)
を使用して、フレームの幅と高さを取得するようにしましょう。
- 代わりに、
2.4 フレームのリサイズ
- フレームのリサイズ処理で、
np.hstack
とnp.vstack
を使用していますが、この方法ではメモリ使用量が多くなります。cv2.resize
とcv2.hconcat
を使用して、直接結合する方法の方が効率的です。
3. 潜在的な問題点
3.1 マルチスレッド処理
- 4つのストリームを同時に処理していますが、マルチスレッド処理ではないため、CPU使用率が高くなる可能性があります。
- 処理速度が遅くなる場合、マルチスレッド処理を検討しましょう。
3.2 フレームレート
- フレームレートの制限が設けられていません。
- 必要に応じて、
cap.set(cv2.CAP_PROP_FPS
, fps)`を使用してフレームレートを設定するようにしましょう。
- 必要に応じて、
4. その他
- コード全体の可読性を向上させるために、コメントを追加することをおすすめします。
- コードフォーマットを統一すると、読みやすくなります。
5. 改善例
Python
import cv2
import configparser
import numpy as np
# INIファイルからRTSP URLを読み取る
config = configparser.ConfigParser()
config.read('streams.ini')
streams = [
config.get('RTSP', 'stream1'),
config.get('RTSP', 'stream2'),
config.get('RTSP', 'stream3'),
config.get('RTSP', 'stream4')
]
def get_frame_size(window_name):
width = cv2.getWinProperty(window_name, cv2.WND_PROP_FRAME_WIDTH)
height = cv2.getWinProperty(window_name, cv2.WND_PROP_FRAME_HEIGHT)
return width, height
def resize_frame(frame, size):
return cv2.resize(frame, size)
def combine_frames(frames, frame_width, frame_height):
top_row = np.hstack((resize_frame(frames[0], (frame_width, frame_height)),
resize_frame(frames[1], (frame_width, frame_height))))
bottom_row = np.hstack((resize_frame(frames[2], (frame_width, frame_height)),
resize_frame(frames[3], (frame_width, frame_height))))
combined_frame = np.vstack((top_row, bottom_row))
return combined_frame
# 各ストリームに対応するVideoCaptureオブジェクトを作成
caps = [cv2.VideoCapture(url) for url in streams]
for i, cap in enumerate(caps):
if not cap.isOpened():
print(f"Failed to open stream {i+1}: {streams[i]}")
exit(1)
# ウィンドウを作成
cv2.namedWindow('RTSP Streams', cv2.WINDOW_NORMAL)
while True:
frames = []
STEP
exeファイル生成
cmdから下記コマンドを入力し、exeファイルを生成する。
※cmd上でディレクトリ指定してから行うこと。
pyinstaller --onefile display_rtsp.py
STEP
iniファイル作成
distフォルダ中にdisplay_rtsp.exeが生成される。
exeファイルと同層にファイル:streams.iniを作成し、以下を入力。
[RTSP]
stream1 = rtsp://example.com/stream1
stream2 = rtsp://example.com/stream2
stream3 = rtsp://example.com/stream3
stream4 = rtsp://example.com/stream4
※RTSPのURLは任意で修正
STEP
実行
クリックすると動作する。
感想
要件がクリアできて大満足。
ChatGPTの恩恵を感じる。
コメント
コメント一覧 (1件)
1台のカメラが落ちると全台止まる状態になっていた。要修正。