16bit静止画保存
概要
OpenCVのライブラリを使用して16bitのモノクロ画像(Y16)を保存します。
なお、RGB64(カラー16bit)には対応しておりませんのでご注意ください。
サンプルプログラム
サンプル(Python) | image-save-16bit-py.zip |
---|
サンプルツールの出力
コード全体
###############
# 解説1
###############
import ctypes
import tisgrabber as tis
import cv2
import numpy as np
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
###############
# 解説2
###############
#新しいグラバーハンドルを作成します。
hGrabber = ic.IC_CreateGrabber()
#デバイスを選択
ic.IC_OpenDevByUniqueName(hGrabber, tis.T("DMK 33UX250 1220128"))
#640x480のモノクロ16bitに設定
ic.IC_SetVideoFormat(hGrabber, tis.T("Y16 (640x480)"))
#フレームレートを10fpsに設定
ic.IC_SetFrameRate(hGrabber, ctypes.c_float( 10.0))
ic.IC_SetPropertySwitch(hGrabber, tis.T("Trigger"), tis.T("Enable"), 0)
###############
# 解説3
###############
#### 解説3-1
if(ic.IC_IsDevValid(hGrabber)):
# メモリ内で受け取るピクセルフォーマットを設定します。
ic.IC_SetFormat(hGrabber, tis.SinkFormats.Y16)
ic.IC_StartLive(hGrabber, 0)
key = ""
while key != "q":
print("p: 画像保存")
print("q: プログラム終了")
#### 解説3-2
key = input('pかqか入力してください。:')
if key == "p":
#2秒間カメラから画像を送られてくるのを待つ
if ic.IC_SnapImage(hGrabber, 2000) == tis.IC_SUCCESS:
#### 解説3-3
# 画像の解像度・フォーマットを宣言
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# 画像の解像度・フォーマットを取得する
ic.IC_GetImageDescription(hGrabber, Width, Height,
BitsPerPixel, colorformat)
#### 解説3-4
# バッファサイズを計算し、ピクセルあたりのバイト数と
# numpy配列のデータ型を取得します。
elementsperpixel = 1
dtype = np.uint8
if colorformat.value == tis.SinkFormats.Y800:
elementsperpixel = 1 # 1 byte per pixel
if colorformat.value == tis.SinkFormats.Y16:
dtype = np.uint16
elementsperpixel = 1 # 1 uint16 per pixel
if colorformat.value == tis.SinkFormats.RGB24:
elementsperpixel = 3 # BGR format, 3 bytes
if colorformat.value == tis.SinkFormats.RGB32:
elementsperpixel = 4 # BGRA format, 4 bytes
#### 解説3-5
buffer_size = Width.value * Height.value * int(float(BitsPerPixel.value) / 8.0)
# イメージデータ取得
imagePtr = ic.IC_GetImagePtr(hGrabber)
imagedata = ctypes.cast(imagePtr,
ctypes.POINTER(ctypes.c_ubyte *
buffer_size))
# バイトデータ(最初の16バイト)
byte_data = np.array(imagedata.contents[:16])
print("Raw data (16byte):", byte_data)
##出力例
##Raw data (byte): [ 28 146 0 144 228 144 32 145 116 148 0 142 100 144 128 145]
# バイトデータを2進数(8ビット)で表示
print("Raw data (16byte, binary):",
[format(x, '08b') for x in byte_data])
##出力例
##Raw data (byte, binary): ['00011100', '10010010', '00000000', '10010000', '11100100', '10010000', '00100000', '10010001', '01110100', '10010100', '00000000', '10001110', '01100100', '10010000', '10000000', '10010001']
# 16ビットデータとして解釈したもの(最初の8ピクセル)
data = np.frombuffer(bytearray(imagedata.contents), dtype=np.uint16)
print("Interpreted 16-bit data:", data[:8])
##出力例
##Interpreted 16-bit data: [37404 36864 37092 37152 38004 36352 36964 37248]
# 16ビットデータを2進数(16ビット)で表示
print("Interpreted 16-bit data (binary):",
[format(x, '016b') for x in data[:8]])
##出力例
##Interpreted 16-bit data (binary): ['1001001000011100', '1001000000000000', '1001000011100100', '1001000100100000', '1001010001110100', '1000111000000000', '1001000001100100', '1001000110000000']
#### 解説3-6
# numpy配列取得
image = np.ndarray(buffer=imagedata.contents,
dtype=dtype,
shape=(Height.value,
Width.value,
elementsperpixel))
# 最初のピクセルの輝度値を取得
first_pixel_value = image[0, 0, 0]
print(f"最初のピクセルの輝度値: {first_pixel_value}")
##出力例
##最初のピクセルの輝度値: 37404
#### 解説3-7
# OpenCVの関数
image = cv2.flip(image, 0)
cv2.imwrite("image.png",image)
print("画像を保存しました。")
cv2.waitKey(10)
else:
print("2秒間フレームを受け取っていません。")
ic.IC_StopLive(hGrabber)
else:
ic.IC_MsgBox(tis.T("デバイスを開けませんでした。"), tis.T("Simple Live Video"))
ic.IC_ReleaseGrabber(hGrabber)
使用するファイルについて
【callback_python.py】を動かす為に必ず下記の【dll(ライブラリ)】や【tisgrabber.py】が必要となります。dll(Dynamic Link Library、ダイナミックリンクライブラリ)とは、Windowsにおける共有ライブラリのことで、メーカーがユーザレベルで操作できるように管理しています。これらのdllを【tisgrabber.py】経由で読み込むことで、TheImagingSource社のカメラに簡単にアクセスできます。
tisgrabber.py | TheImagingSource社が提供するAPIであるIC Imaging Controlをカプセル化するtisgrabber.dllのラッパープログラム(IC Imaging Controlを使いやすくするためのもの)となっています。IC Imaging Control DLLの32ビットバージョン(tisgrabber.dll、TIS_UDSHL11.dll)と64ビットバージョン(tisgrabber_x64.dll、TIS_UDSHL11_64.dll)のどちらを使用するかが自動的にチェックしたり、IC Imaging Controlで使用されているメソッドを呼び出すために使われています。 |
---|---|
tisgrabber.dll | IC Imaging ControlをPythonで呼び出すための32 bitバージョンのDLL |
TIS_UDSHL11.dll | IC Imaging Controlの32 bitバージョンのDLL |
tisgrabber_x64.dll | IC Imaging ControlをPythonで呼び出すための64 bitバージョンのDLL |
TIS_UDSHL11_64.dll | IC Imaging Controlの64 bitバージョンのDLL |
callback_python.py (任意のプログラム) |
実行されるメインプログラム。 |
解説1:importで宣言する
import ctypes
import tisgrabber as tis
import cv2
import numpy as np
ic = ctypes.cdll.LoadLibrary("./tisgrabber_x64.dll")
tis.declareFunctions(ic)
ic.IC_InitLibrary(0)
Pythonで下記のライブラリを使用するためにimportを使います。
ctype | このライブラリは C と互換性のあるデータ型を提供し、共有ライブラリ(Windowsでは*.dll)の関数を呼び出すことができます。 |
---|---|
tisgrabber | IC Imaging Controlを制御するラッパーファイルを読み込むために使用します。 |
cv2 | OpenCVで画像処理を行うために使用します。 |
numpy | Pythonでの機械学習の計算をより速く、効率的に行えるようにするために使用します。 |
上記のライブラリを使用するため、あらかじめターミナルにて下記のコマンドを入力し、各ライブラリをインストールしておいてください。
pip install opencv-python
pip install numpy
解説2:カメラをオープンする
#########
# 解説2
#########
#新しいグラバーハンドルを作成
hGrabber = ic.IC_CreateGrabber()
#デバイスを選択
ic.IC_OpenDevByUniqueName(hGrabber, tis.T("DMK 33UX250 1220128"))
#640x480のモノクロ16bitに設定
ic.IC_SetVideoFormat(hGrabber, tis.T("Y16 (640x480)"))
#フレームレートを10fpsに設定
ic.IC_SetFrameRate(hGrabber, ctypes.c_float( 10.0))
カメラを開くためにまず「ic.IC_CreateGrabber」でオブジェクトを作成し、「IC_OpenDevByUniqueName」でデバイスの型番と8桁のシリアル番号からカメラを開きます。デバイスモデルと8桁シリアル番号をスペースでサンプルのように区切って作成してください。なお、シリアル番号の頭文字がゼロの場合、先頭のゼロは省略されますので注意してください。 その後、「IC_SetVideoFormat」で解像度、「IC_SetFrameRate」でフレームレートを設定します。
ここでは、カメラの型番、シリアル番号、解像度、フレームレートが既知の状態でプログラム上で決め打ちしていますが、下記の関数によってデバイスダイアログを呼んでカメラを指定することもできます。
hGrabber = tis.openDevice(ic)
設定方法に関しては下記を参照ください。
解説3:ライブスタート開始し、画像を保存する
###############
# 解説3
###############
#### 解説3-1
if(ic.IC_IsDevValid(hGrabber)):
# メモリ内で受け取るピクセルフォーマットを設定します。
ic.IC_SetFormat(hGrabber, tis.SinkFormats.Y16)
ic.IC_StartLive(hGrabber, 0)
key = ""
while key != "q":
print("p: 画像保存")
print("q: プログラム終了")
#### 解説3-2
key = input('pかqか入力してください。:')
if key == "p":
#2秒間カメラから画像を送られてくるのを待つ
if ic.IC_SnapImage(hGrabber, 2000) == tis.IC_SUCCESS:
#### 解説3-3
# 画像の解像度・フォーマットを宣言
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# 画像の解像度・フォーマットを取得する
ic.IC_GetImageDescription(hGrabber, Width, Height,
BitsPerPixel, colorformat)
#### 解説3-4
# バッファサイズを計算し、ピクセルあたりのバイト数と
# numpy配列のデータ型を取得します。
elementsperpixel = 1
dtype = np.uint8
if colorformat.value == tis.SinkFormats.Y800:
elementsperpixel = 1 # 1 byte per pixel
if colorformat.value == tis.SinkFormats.Y16:
dtype = np.uint16
elementsperpixel = 1 # 1 uint16 per pixel
if colorformat.value == tis.SinkFormats.RGB24:
elementsperpixel = 3 # BGR format, 3 bytes
if colorformat.value == tis.SinkFormats.RGB32:
elementsperpixel = 4 # BGRA format, 4 bytes
#### 解説3-5
buffer_size = Width.value * Height.value * int(float(BitsPerPixel.value) / 8.0)
# イメージデータ取得
imagePtr = ic.IC_GetImagePtr(hGrabber)
imagedata = ctypes.cast(imagePtr,
ctypes.POINTER(ctypes.c_ubyte *
buffer_size))
# バイトデータ(最初の16バイト)
byte_data = np.array(imagedata.contents[:16])
print("Raw data (16byte):", byte_data)
##出力例
##Raw data (byte): [ 28 146 0 144 228 144 32 145 116 148 0 142 100 144 128 145]
# バイトデータを2進数(8ビット)で表示
print("Raw data (16byte, binary):",
[format(x, '08b') for x in byte_data])
##出力例
##Raw data (byte, binary): ['00011100', '10010010', '00000000', '10010000', '11100100', '10010000', '00100000', '10010001', '01110100', '10010100', '00000000', '10001110', '01100100', '10010000', '10000000', '10010001']
# 16ビットデータとして解釈したもの(最初の8ピクセル)
data = np.frombuffer(bytearray(imagedata.contents), dtype=np.uint16)
print("Interpreted 16-bit data:", data[:8])
##出力例
##Interpreted 16-bit data: [37404 36864 37092 37152 38004 36352 36964 37248]
# 16ビットデータを2進数(16ビット)で表示
print("Interpreted 16-bit data (binary):",
[format(x, '016b') for x in data[:8]])
##出力例
##Interpreted 16-bit data (binary): ['1001001000011100', '1001000000000000', '1001000011100100', '1001000100100000', '1001010001110100', '1000111000000000', '1001000001100100', '1001000110000000']
#### 解説3-6
# numpy配列取得
image = np.ndarray(buffer=imagedata.contents,
dtype=dtype,
shape=(Height.value,
Width.value,
elementsperpixel))
# 最初のピクセルの輝度値を取得
first_pixel_value = image[0, 0, 0]
print(f"最初のピクセルの輝度値: {first_pixel_value}")
##出力例
##最初のピクセルの輝度値: 37404
#### 解説3-7
# OpenCVの関数
image = cv2.flip(image, 0)
cv2.imwrite("image.png",image)
print("画像を保存しました。")
cv2.waitKey(10)
else:
print("2秒間フレームを受け取っていません。")
ic.IC_StopLive(hGrabber)
else:
ic.IC_MsgBox(tis.T("デバイスを開けませんでした。"), tis.T("Simple Live Video"))
ic.IC_ReleaseGrabber(hGrabber)
TISカメラから送られてくる画像を2秒間待ち、取得したら画像を保存しています。
主な手順は下記の通りです。
解説3-1:カメラのライブスタート開始
if(ic.IC_IsDevValid(hGrabber)):
# メモリ内で受け取るピクセルフォーマットを設定します。
ic.IC_SetFormat(hGrabber, tis.SinkFormats.Y16)
#ライブスタート開始 引数:0の時非表示、引数:1の時表示
ic.IC_StartLive(hGrabber, 0)
key = ""
while key != "q":
print("p: 画像保存")
print("q: プログラム終了")
解説2で「ic.IC_SetVideoFormat(hGrabber, tis.T("Y16 (640x480)"))」を設定していますので、メモリ内で受け取るピクセルフォーマットも同じカラーフォーマット"Y16"(モノクロ16bit)に設定します。「IC_SetVideoFormat」で設定したカラーフォーマットと「IC_SetFormat」は同じ必要があります。
「IC_StartLive」でライブストリーミングを開始しカメラから画像を送られてきます。
解説3-2:カメラから送られた画像を取得
key = input('pかqを入力してください。:')
if key == "p":
#2秒間カメラから画像を送られてくるのを待つ
if ic.IC_SnapImage(hGrabber, 2000) == tis.IC_SUCCESS:
任意のタイミングで保存処理できるようにするために、ターミナルにて【画像保存】するのか、【プログラム終了】するのか選択できるようにします。
【画像保存】する場合、「IC_SnapImage(hGrabber, 2000)」で2000ms(2秒間)カメラから画像を送られてくるのを待ちます。画像が送られてこなかったらエラーを返します。なお、IC_SnapImageの第2引数はカメラからの応答を待つタイムアウトの時間ですので、値を小さくしても処理が早くなることはありません。任意のタイミングではなく、カメラから送られてくるすべての画像を取得するのであれば、下記のコールバック関数を使った処理の方が向いています。
コールバック関数の設定方法(OpenCVで二値化)
IC Imaging Control(Python) サンプルプログラム
解説3-3:カメラから送られた画像を取得
# 画像の解像度・フォーマットを宣言
Width = ctypes.c_long()
Height = ctypes.c_long()
BitsPerPixel = ctypes.c_int()
colorformat = ctypes.c_int()
# 画像の解像度・フォーマットを取得する
ic.IC_GetImageDescription(hGrabber, Width, Height,
BitsPerPixel, colorformat)
カメラからデータを受け取り、IC_GetImageDescription関数で受け取った画像の横幅、高さ、1ピクセル当たりのデータ量、カラーフォーマットを取得します。
解説3-4:バッファサイズを計算
# バッファサイズを計算し、ピクセルあたりのバイト数と
# numpy配列のデータ型を取得します。
elementsperpixel = 1
dtype = np.uint8
if colorformat.value == tis.SinkFormats.Y800:
elementsperpixel = 1 # 1 byte per pixel
if colorformat.value == tis.SinkFormats.Y16:
dtype = np.uint16
elementsperpixel = 1 # 1 uint16 per pixel
if colorformat.value == tis.SinkFormats.RGB24:
elementsperpixel = 3 # BGR format, 3 bytes
if colorformat.value == tis.SinkFormats.RGB32:
elementsperpixel = 4 # BGRA format, 4 bytes
データ量をカラーフォーマットによって変換しています。データ量はそれぞれ下記の通りです。
Y800 | モノクロ8bit(1Byte) |
---|---|
Y16 | モノクロ16bit(2Byteだが16 ビット符号なし(uint16)として処理するため、uint16の1Byteとして処理) |
RGB24 | カラー8bit(3Byte RGBそれぞれ8bit) |
RGB32 | カラー8bit(4Byte RGBAそれぞれ8bit)アルファ値(透過度)はIC Imaging Controlでは使っていないため常に"0"です。 |
なお、RGB64はIC Imaging Control3.4ではサポートしていませんが、IC Imaging Control3.5ではサポートしており、Tiff形式で保存することも可能です。
露光時間・ゲインを設定し、静止画保存をする(pythonnet編)
IC Imaging Control(Python) サンプルプログラム
解説3-5:イメージデータを取得
buffer_size = Width.value * Height.value * int(float(BitsPerPixel.value) / 8.0)
# イメージデータ取得
imagePtr = ic.IC_GetImagePtr(hGrabber)
imagedata = ctypes.cast(imagePtr,
ctypes.POINTER(ctypes.c_ubyte *
buffer_size))
# バイトデータ(最初の16バイト)
byte_data = np.array(imagedata.contents[:16])
print("Raw data (16byte):", byte_data)
##出力例
##Raw data (byte): [ 28 146 0 144 228 144 32 145 116 148 0 142 100 144 128 145]
# バイトデータを2進数(8bit)で表示
print("Raw data (16byte, binary):",
[format(x, '08b') for x in byte_data])
##出力例
##Raw data (byte, binary): ['00011100', '10010010', '00000000', '10010000', '11100100', '10010000', '00100000', '10010001', '01110100', '10010100', '00000000', '10001110', '01100100', '10010000', '10000000', '10010001']
# 16bitデータとして解釈したもの(最初の8ピクセル)
data = np.frombuffer(bytearray(imagedata.contents), dtype=np.uint16)
print("Interpreted 16-bit data:", data[:8])
##出力例
##Interpreted 16-bit data: [37404 36864 37092 37152 38004 36352 36964 37248]
# 16bitデータを2進数(16bit)で表示
print("Interpreted 16-bit data (binary):",
[format(x, '016b') for x in data[:8]])
##出力例
##Interpreted 16-bit data (binary): ['1001001000011100', '1001000000000000', '1001000011100100', '1001000100100000', '1001010001110100', '1000111000000000', '1001000001100100', '1001000110000000']
IC_GetImagePtrでbit形式のイメージデータのポインタimagePtrを取得しています。
imagePtrのポインタ型を使って、画像データの先頭から画像の最後のアドレスを指定し、ポインタ型に変換(ctypes.cast) しています。ポインタ変数imagedata が格納している画像データが入っているアドレスそのものですので、imagedataには画像データが格納されていることになります。
次にカメラから取得したimagedataを16バイト分取得し、2進数表記で画像データの中身を確認してみます。
先頭16バイトを10進数で表示(8bit表記)
byte_data = np.array(imagedata.contents[:16])
print("Raw data (16byte):", byte_data)
imagedata.contents はキャストした後の「buffer_size 個のバイト配列」を示します。
そのうち先頭16バイトを [:16] で抜き出し、それを np.arrayで NumPy 配列に変換しています。print("Raw data (16byte):", byte_data) では、たとえば [28, 146, 0, 144, ...] のように10進数でバイトの値を表示します。
出力例:
Raw data (byte): [ 28 146 0 144 228 144 32 145 116 148 0 142 100 144 128 145]
先頭16バイトを2進数で表示(8bit表記)
print("Raw data (16byte, binary):", [format(x, '08b') for x in byte_data])
リスト内包表記 [format(x, '08b') for x in byte_data] により、x(各バイト)を 08b(8bit、先頭ゼロ埋め)でフォーマットしています。たとえば 28 というバイトは00011100 として表示されます。
出力例:
Raw data (byte, binary): ['00011100', '10010010', '00000000', '10010000', '11100100', '10010000', '00100000', '10010001', '01110100', '10010100', '00000000', '10001110', '01100100', '10010000', '10000000', '10010001']
先頭16バイトを10進数で表示(16bit表記)
data = np.frombuffer(bytearray(imagedata.contents), dtype=np.uint16) print("Interpreted 16-bit data:", data[:8])
imagedata.contents はバイト列ですが、これを bytearrayで Python のバイト配列型に変換し、換し、それを np.frombuffer(..., dtype=np.uint16) で 「16bit(2バイト)ごとに一つの要素」 として NumPy 配列に取り込みます。
たとえば [28, 146] の2バイトが合わさって一つの16bit整数(10進数で37404)などとして解釈されます。次にdata[:8] で配列の先頭8要素(8ピクセル相当)を 10進数の形で表示します。
出力例:
Interpreted 16-bit data: [37404 36864 37092 37152 38004 36352 36964 37248]
先頭16バイトを2進数で表示(16bit表記)
print("Interpreted 16-bit data (binary):", [format(x, '016b') for x in data[:8]])
先ほどの data[:8] の各要素 x を、format(x, '016b') で 16bit表記(先頭をゼロ埋めし16桁の2進数)にフォーマットします。たとえば10進数 37404 は 2進数で 1001001000011100 と出力されます。
出力例:
Interpreted 16-bit data (binary): ['1001001000011100', '1001000000000000', '1001000011100100', '1001000100100000', '1001010001110100', '1000111000000000', '1001000001100100', '1001000110000000']
カラーフォーマットY16にした場合、カメラの階調(ADC)が 12 bit(あるいは10bit)にもかかわらず、実際には 16 bitのメモリ領域にデータを格納されています。その際、下位 4bit(あるいは6bit)がパディング(意味のないデータ) として扱われ、値が常に 0 である場合や、ランダムな値が入る場合もあります(カメラによって異なる)。
例えば、上記の例で得られる 16 bitデータの一つ'1001001000011100'に着目したとき、最初の12bit'1001 0010 0001'が有効な画像データ(12 bit分)で、残りの'1100'がパディング(ここではランダム値が入っていると想定)になっています。実際に画素値として利用すべき情報は 12 bit分のみなので、処理系によっては下位 4 bitを無視(またはシフト)して使うことが必要となります。
解説3-6:NumPy配列化する
# numpy配列取得
image = np.ndarray(buffer=imagedata.contents,
dtype=dtype,
shape=(Height.value,
Width.value,
elementsperpixel))
# 最初のピクセルの輝度値を取得
first_pixel_value = image[0, 0, 0]
print(f"最初のピクセルの輝度値: {first_pixel_value}")
カメラなどから取得したイメージの生データ(バッファ)をNumPy 配列として扱い、先頭ピクセル(画面左上)に含まれる輝度値を取り出しています。
first_pixel_value = image[0, 0, 0]はimage[行, 列, チャンネル] の形式で下記の通りデータにアクセスできます。
行 = 0 | 画像の一番上の行 |
---|---|
列 = 0 | 左端の列 |
チャンネル = 0 | ピクセル内で 0 番目の要素(例えば RGB の R 成分、またはグレースケールの場合はそのまま輝度) |
最後にprint(f"最初のピクセルの輝度値: {first_pixel_value}")で正しく画像バッファを NumPy 配列にマッピングできているか数値を確認することができます。
出力例:
最初のピクセルの輝度値: 37404
解説3-7:リアルタイムでOpenCVの画像処理し16bitで保存する
# OpenCVの関数
image = cv2.flip(image, 0)
cv2.imwrite("image.png",image)
print("画像を保存しました。")
cv2.waitKey(10)
else:
print("2秒間フレームを受け取っていません。")
ic.IC_StopLive(hGrabber)
else:
ic.IC_MsgBox(tis.T("デバイスを開けませんでした。"), tis.T("Simple Live Video"))
ic.IC_ReleaseGrabber(hGrabber)
NumPyの配列で取得した画像をOpenCVの画像反転メソッドflipを使って反転させてからimwriteで16bit形式のモノクロ画像を保存しています。
【プログラム終了】する場合、「ic.IC_StopLive(hGrabber)」が実行され、「ic.IC_ReleaseGrabber(hGrabber)」でhGrabberオブジェクトが解放され処理が終了します。