前回の記事では、Rspberry Pi 5 + USBカメラで撮影した画像を、PythonのFlaskパッケージを利用することによりHTTPでストリーミング配信できることを紹介しました。今回は、 撮影した画像に、その画像のRGBヒストグラムを重ねて出力する プログラムの例を紹介します。本プログラムにより、以下のような表示を得ることができます。
プログラムの実行方法
ヒストグラム付きのストリーミング映像を配信するWebサーバーのPythonコードをGitHubへpushしました。下記の手順でお試しいただけます。実装は、「 opencv-web-sandbox/opencv-web-sandbox/web_root/views/streams.py 」に埋め込んでいます。Pythonでapp.pyを実行した後はRaspberry Piの「 [IPアドレス]:5000/streams/stream 」へアクセスすることにより、ヒストグラムを含んだWebカメラの映像のストリーミング配信を見ることができます。
# ソースコードをGitHubより取得する
$ git clone https://github.com/xtrizeShino/opencv-web-sandbox.git
$ cd opencv-web-sandbox
# v0.1.0のタグのソースコードを取得
$ git checkout -b v0.2.0 refs/tags/v0.2.0
# 作業ディレクトリへ移動する
$ cd opencv-web-sandbox
# uvの仮想環境をセットアップする
$ uv sync
# 仮想環境に入る
$ . .venv/bin/activate
# app.pyを実行
(opencv-web-sandbox) $ python app.py
# * Serving Flask app 'web_root'
# * Debug mode : off
# * Running on akk addresses (0.0.0.0)
# * Running on http://127.0.0.1:5000
# * Running on http://192.168.10.140:5000
# Press CTRL+C to quit
############
###【終了するにはCtrl+c】
############
パッケージ管理ツール「uv」については以下をご参照ください。
ソースコードの概略
OpenCVは、cv2.calcHistによりヒストグラムを生成することができます。ただし、撮影した画像はRGBの3要素から構成されていますので、まず、撮影した画像をRGBチャンネルに分解します。分解したそれぞれの要素からヒストグラムを生成し、matplotlib.pyplotを利用してグラフを作成しました。
作成したグラフはio.BytesIO()を利用して、描画データを画像データへと変換し、元画像と同じ形式であるnumpy.array型に変換しました。最後にちょうど良い埋め込みサイズへとリサイズしています。こうして作られた画像データを、imageproc_resize_and_histのcv2.warpAffineにより、元画像に結合します。
# generate Histgram Image from input image
def generate_hist_image(image):
# divide Image to RGB channels
b, g, r = image[:,:,0], image[:,:,1], image[:,:, 2]
# generate Histgram as each RGB
hist_b = cv2.calcHist([b],[0],None,[256],[0,256])
hist_g = cv2.calcHist([g],[0],None,[256],[0,256])
hist_r = cv2.calcHist([r],[0],None,[256],[0,256])
# write Plot
plt.plot(hist_r, color='r', label="r")
plt.plot(hist_g, color='g', label="g")
plt.plot(hist_b, color='b', label="b")
# set xlim, ylim
plt.xlim(0, 255)
# convert Histgram to numpy array
buf = io.BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
enc = np.array(Image.open(buf).convert('RGB'))
plt.gca().clear()
# resize
enc = cv2.resize(enc, (320, 240))
return enc
# Callback function for Image Proc
def imageproc_resize_and_hist(image):
# resize Image
image = cv2.resize(image, (1024, 768))
h, w = image.shape[:2]
# put Histgram on Image
enc = generate_hist_image(image)
# set offset
dx=10
dy=10
# set Matrix for affine
M = np.array([[1, 0, dx],[0, 1, dy]], dtype=float)
# put Image by warpAffine
image = cv2.warpAffine(enc, M, (w, h), image, borderMode=cv2.BORDER_TRANSPARENT)
# output image
return image
簡単でしたね!何かの際にお役立てください!
参考にさせていただきました記事