概要
1つの対象物を異なる角度から撮影したり、複数の対象物を同時撮影したい場合に利用する為に、2カメラ撮影後、撮影画像を連結する python プログラムを作成しましたので記載します。
◆検証環境◆
① OS : Windows10 64bit
② 言語 : python + OpenCV + pypylon
③ 装置 : Basler acA2500-14um(USB3) ※ 2台
結果
(3) (4)にカメラ1,2の単体画像を示します。(1) (2) は単体画像を横方向・縦方向に連結した結果です。
(1)横方向連結(カメラ1・2連結画像)

(2)縦方向連結(カメラ1・2連結画像)

(3)カメラ1画像

(4)カメラ2画像

プログラム① (基本)
このプログラムの私にとってのポイントは、カメラ本体記載のシリアル番号(S/N)で、対象カメラを指定できることです。行番11~12でカメラ1のシリアル番号を設定し、行番13でカメラ1インスタンスを生成します。 同様に行番17~19でカメラ2のインスタンスを生成しています。
以後、GitHubにあるサンプルコード(opencv.py)を参考に2カメラ対応プログラムを作成しました。
2カメラ画像の連結は、横方向連結は画像高さが同じであること。縦方向連結は画像幅が同じであることが条件です。この条件を満たせば連結は簡単で、行番62(横連結),行番68(縦連結)となっています。
# Basler acA2500-14um (USB3) 画像幅・高さ(1944X2592)
from pypylon import pylon
import cv2
# import numpy as np
s_n_1 = "********"
s_n_2 = "********"
# シリアルナンバー指定によるデバイス起動1
inf1 = pylon.DeviceInfo()
inf1.SetSerialNumber(s_n_1)
cmr1 = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice(inf1))
cmr1.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
# シリアルナンバー指定によるデバイス起動2
inf2 = pylon.DeviceInfo()
inf2.SetSerialNumber(s_n_2)
cmr2 = pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateFirstDevice(inf2))
cmr2.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
# 取得データコンバータ設定(opencv bgr format)
converter = pylon.ImageFormatConverter()
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
# 連続撮影
while cmr1.IsGrabbing() and cmr2.IsGrabbing():
# カメラ1
img_1 = None
g_Rslt1 = cmr1.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
if g_Rslt1.GrabSucceeded():
image_1 = converter.Convert(g_Rslt1)
img_1 = image_1.GetArray()
g_Rslt1.Release()
# カメラ2
img_2 = None
g_Rslt2 = cmr2.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
if g_Rslt2.GrabSucceeded():
image_2 = converter.Convert(g_Rslt2)
img_2 = image_2.GetArray()
g_Rslt2.Release()
# カメラ1・2画像合成
if (not img_1 is None) and (not img_2 is None) :
# cv2.namedWindow('camera_1', cv2.WINDOW_NORMAL)
# cv2.imshow('camera_1', img_1)
# cv2.namedWindow('camera_2', cv2.WINDOW_NORMAL)
# cv2.imshow('camera_2', img_2)
h1, w1, c1 = img_1.shape # 画像幅・高さ取得
h2, w2, c2 = img_2.shape # 画像幅・高さ取得
# 横方向合成
if h1 == h2:
img_merge_v = cv2.hconcat([img_1, img_2])
cv2.namedWindow('img_mrg_v', cv2.WINDOW_NORMAL)
cv2.imshow('img_mrg_v', img_merge_v)
# 縦方向合成
# if w1 == w2:
# img_merge_h = cv2.vconcat([img_1, img_2])
# cv2.namedWindow('img_mrg_h', cv2.WINDOW_NORMAL)
# cv2.imshow('img_mrg_h', img_merge_h)
k = cv2.waitKey(1)
if k == 27:
break
# デバイス開放
cmr1.StopGrabbing()
cmr2.StopGrabbing()
cv2.destroyAllWindows()
プログラム② (発展)
基本プログラムでは、 カメラ本体記載のシリアル番号(S/N) をプログラム内に直接記載しましたが、このプログラムでは接続されているカメラを自動認識・撮影します。同じ解像度のカメラであれば、接続数によって、プログラムを書き換える必要はありません。
# Basler acA2500-14um (USB3) 画像幅・高さ(1944X2592)
from pypylon import pylon
import cv2
# 撮影中チェック用
def CAMERA_IsGrabbing(CAMERA_ARY):
chk_rslt = True
for CMR in CAMERA_ARY :
if not CMR.IsGrabbing() :
chk_rslt = False
return chk_rslt
# 撮影停止
def CAMERA_StopGrabbing(CAMERA_ARY):
for CMR in CAMERA_ARY :
CMR.StopGrabbing()
tlFactory = pylon.TlFactory.GetInstance()
devices = tlFactory.EnumerateDevices()
cameras = pylon.InstantCameraArray(len(devices))
MDL_ARY = []
S_N_ARY = []
CMR_ARY = []
# 撮影開始
for i, cam in enumerate(cameras):
# 下記 4行は参考。本プログラム機能上は不要。
cam.Attach(tlFactory.CreateDevice(devices[i]))
MDL_ARY.append(cam.GetDeviceInfo().GetModelName()) # GET DEVICE MODEL
S_N_ARY.append(cam.GetDeviceInfo().GetSerialNumber()) # GET DEVICE Serial Number
# print("No", i ," , Model: ", MDL_ARY[i] , " , S/N: ", S_N_ARY[i])
# 撮影開始
CMR_ARY.append(pylon.InstantCamera(pylon.TlFactory.GetInstance().CreateDevice(devices[i])))
CMR_ARY[i].StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
# 取得データコンバータ設定(opencv bgr format)
converter = pylon.ImageFormatConverter()
converter.OutputPixelFormat = pylon.PixelType_BGR8packed
converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
# 連続撮影
while CAMERA_IsGrabbing(CMR_ARY) :
IMG_ARY = []
# cnt_grab_success = 0
# カメラ画像取得
for idx in range(len(CMR_ARY)):
IMG_ARY.append(None)
tmp_Rslt = CMR_ARY[idx].RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
if tmp_Rslt.GrabSucceeded():
tmp_img = converter.Convert(tmp_Rslt)
IMG_ARY[idx] = tmp_img.GetArray()
# cnt_grab_success += 1
tmp_Rslt.Release()
# 画像マージ実行(全カメラ画像取得成功時)
# 全画像高さが同じであること
if len(CMR_ARY) == len(IMG_ARY) :
for idx in range(len(IMG_ARY)):
if idx == 0 :
img_merged = IMG_ARY[idx]
else :
img_merged = cv2.hconcat([img_merged, IMG_ARY[idx]])
cv2.namedWindow('img_merged', cv2.WINDOW_NORMAL)
cv2.imshow('img_merged', img_merged)
k = cv2.waitKey(1)
if k == 27:
break
CAMERA_StopGrabbing(CMR_ARY) # デバイス解放
cv2.destroyAllWindows()
まとめ
少し離れたところにある複数の7セグLED解析を1つのパソコンで行いたかったので、今回の検討をしています。
また、同じ対象物でも異なる角度から複合的に解析することで、精度向上等に活用出来るかもしれません。