はじめに
OMRON SETNECカメラのStAPiとOpenCVを利用した制御の進め方を説明します。
Line Scan CameraからFrame IDを取得する方法
この記事では、Pythonを使用してLine Scan CameraからFrame IDを取得する方法について解説します。具体的には、stapipy
ライブラリを用いたコールバック関数の設定と、取得した画像データの表示方法を説明します。このサンプルコードを通じて、Line Scan Cameraの操作と、画像データの管理方法を学びましょう。
使用するライブラリのインポート
まずは必要なライブラリをインポートします。cv2
はOpenCV、threading
はスレッド処理、numpy
は数値計算、stapipy
はカメラ操作に使用します。
import cv2
import threading
import numpy as np
import stapipy as st
画像の表示サイズを調整するためのスケールファクター
画像の表示サイズを調整するために、スケールファクターを設定します。これは表示する画像を縮小するために使用されます。
DISPLAY_RESIZE_FACTOR = 0.3
コールバック関数を含むクラスの定義
ここでは、データストリームから取得した画像データを処理するためのコールバック関数を含むクラスを定義します。このクラスは、画像データとFrame IDを保持し、必要に応じてそれらを提供します。
class CMyCallback:
def __init__(self, max_frames):
self._image = None
self._frame_id = None
self._lock = threading.Lock()
self._frame_count = 0
self._max_frames = max_frames
@property
def image(self):
duplicate = None
self._lock.acquire()
if self._image is not None:
duplicate = self._image.copy()
self._lock.release()
return duplicate
@property
def frame_id(self):
fid = None
self._lock.acquire()
if self._frame_id is not None:
fid = self._frame_id
self._lock.release()
return fid
def datastream_callback(self, handle=None, context=None):
st_datastream = handle.module
if st_datastream:
with st_datastream.retrieve_buffer() as st_buffer:
if st_buffer.info.is_image_present:
st_image = st_buffer.get_image()
print("BlockID={0} Size={1} x {2} First Byte={3}".format(
st_buffer.info.frame_id,
st_image.width, st_image.height,
st_image.get_image_data()[0]))
data = st_image.get_image_data()
nparr = np.frombuffer(data, np.uint8)
nparr = nparr.reshape(st_image.height, st_image.width)
nparr = cv2.resize(nparr, None, fx=DISPLAY_RESIZE_FACTOR, fy=DISPLAY_RESIZE_FACTOR)
self._lock.acquire()
self._image = nparr
self._frame_id = st_buffer.info.frame_id
self._lock.release()
self._frame_count += 1
if self._frame_count >= self._max_frames:
st_datastream.stop_acquisition()
メイン関数
ここでは、メイン関数内でカメラの初期化、データストリームの設定、コールバック関数の登録を行います。最後に、取得した画像データを表示します。
if __name__ == "__main__":
max_frames_to_grab = 100
my_callback = CMyCallback(max_frames=max_frames_to_grab)
cb_func = my_callback.datastream_callback
last_frame_id = None
images = []
try:
st.initialize()
st_system = st.create_system()
st_device = st_system.create_first_device()
print('Device=', st_device.info.display_name)
st_datastream = st_device.create_datastream()
callback = st_datastream.register_callback(cb_func)
st_datastream.start_acquisition(max_frames_to_grab)
st_device.acquisition_start()
print("To terminate, focus on the OpenCV window and press any key.")
while st_datastream.is_grabbing:
output_image = my_callback.image
frame_id = my_callback.frame_id
if output_image is not None:
cv2.imshow('image', output_image)
if frame_id is not None and frame_id != last_frame_id:
print(f'Frame ID: {frame_id}')
last_frame_id = frame_id
images.append(output_image)
key_input = cv2.waitKey(1)
if key_input != -1:
break
st_device.acquisition_stop()
st_datastream.stop_acquisition()
if images:
stacked_images = np.vstack(images)
filename = f'captured_images_frame_{last_frame_id}.png'
cv2.imwrite(filename, stacked_images)
print(f'Saved images to {filename}')
except Exception as exception:
print(exception)
解説
-
初期化と接続:
-
st.initialize()
でStApiを初期化し、st.create_system()
でシステムオブジェクトを作成します。 -
st.create_first_device()
で最初に検出されたデバイスに接続します。
-
-
データストリームとコールバックの設定:
-
st_device.create_datastream()
でデータストリームオブジェクトを作成します。 -
st_datastream.register_callback(cb_func)
でコールバック関数をデータストリームに登録します。
-
-
画像取得と表示:
-
st_datastream.start_acquisition(max_frames_to_grab)
でホスト側の画像取得を開始し、st_device.acquisition_start()
でカメラ側の画像取得を開始します。 - ループ内でコールバック関数から取得した画像とフレームIDを表示し、任意のキーが押されたら画像取得を終了します。
-
結果
Device= FS-B4KU7GES-F(22LB459)
To terminate, focus on the OpenCV window and press any key.
BlockID=1 Size=4096 x 1254 First Byte=7
Frame ID: 1
BlockID=2 Size=4096 x 4096 First Byte=4
Frame ID: 2
BlockID=3 Size=4096 x 4096 First Byte=4
Frame ID: 3
BlockID=4 Size=4096 x 4096 First Byte=4
Frame ID: 4
BlockID=5 Size=4096 x 4096 First Byte=4
Frame ID: 5
BlockID=6 Size=4096 x 4096 First Byte=4
Frame ID: 6
BlockID=7 Size=4096 x 567 First Byte=4
Saved images to captured_images_frame_6.png
まとめ
このコードでは、Line Scan Cameraから取得した画像データとフレームIDをコールバック関数を通じて処理し、OpenCVを用いて表示しています。このサンプルを基に、自分のプロジェクトに応じたカスタマイズを行うことで、より高度な画像処理を実現することができます。ぜひ試してみてください。
関連資料
更新資料
コードをrefactoringしました。反応性が良くなります。
import cv2
import threading
import numpy as np
import stapipy as st
class CMyCallback:
def __init__(self, display_resize_factor=1):
self._image = None
self._frame_id = None
self._lock = threading.Lock()
self._display_resize_factor = display_resize_factor # DISPLAY_RESIZE_FACTORを引数で指定
@property
def image(self):
with self._lock:
if self._image is not None:
return self._image.copy()
return None
@property
def frame_id(self):
with self._lock:
return self._frame_id
def datastream_callback(self, handle=None, context=None):
st_datastream = handle.module
if st_datastream:
with st_datastream.retrieve_buffer() as st_buffer:
# 取得したデータに画像データが含まれているか確認
if st_buffer.info.is_image_present:
# 画像オブジェクトを作成
st_image = st_buffer.get_image()
# 取得した画像データの情報を表示
print("BlockID={0} Size={1} x {2} First Byte={3}".format(
st_buffer.info.frame_id,
st_image.width, st_image.height,
st_image.get_image_data()[0]))
# 画像データを取得
data = st_image.get_image_data()
# データをNumPy配列に変換
nparr = np.frombuffer(data, np.uint8)
# 画像を表示用に処理
nparr = nparr.reshape(st_image.height, st_image.width)
# インスタンス変数を用いてリサイズ
nparr = cv2.resize(nparr, None,
fx=self._display_resize_factor,
fy=self._display_resize_factor)
with self._lock:
self._image = nparr
self._frame_id = st_buffer.info.frame_id
if __name__ == "__main__":
display_resize_factor = 1 # 任意の倍率を設定可能
my_callback = CMyCallback(display_resize_factor=display_resize_factor)
cb_func = my_callback.datastream_callback
last_frame_id = None # 前回のフレームIDを保存する変数
try:
# StApiを初期化
st.initialize()
# システムオブジェクトを作成してデバイスをスキャンし接続
st_system = st.create_system()
# 最初に検出されたデバイスに接続
st_device = st_system.create_first_device()
# デバイスの名前を表示
print('Device=', st_device.info.display_name)
# 画像ストリームデータを扱うデータストリームオブジェクトを作成
st_datastream = st_device.create_datastream()
# コールバックをデータストリームに登録
callback = st_datastream.register_callback(cb_func)
# ホスト側の画像取得を開始
st_datastream.start_acquisition()
# カメラ側の画像取得を開始
st_device.acquisition_start()
print("To terminate, focus on the OpenCV window and press any key.")
while st_datastream.is_grabbing:
output_image = my_callback.image
frame_id = my_callback.frame_id
if output_image is not None:
# cv2.imshow('image', output_image)
if frame_id is not None and frame_id != last_frame_id:
print(f'Frame ID: {frame_id}')
last_frame_id = frame_id # 現在のフレームIDを更新
key_input = cv2.waitKey(1)
if key_input != -1:
break
# カメラ側の画像取得を停止
st_device.acquisition_stop()
# ホスト側の画像取得を停止
st_datastream.stop_acquisition()
except Exception as exception:
print(exception)