1. はじめに
最近のマシンビジョンカメラでは、10bitや12bitのグレースケール画像出力に対応する製品が増えてきました。
これは微細な濃淡の変化を捉えたり、高ダイナミックレンジ(HDR)画像処理に対応するために有効です。
しかし、10bit/12bit画像には3つの大きな落とし穴があります:
- 画像が正しく表示できない
- 適切な保存や読込ができない
- 画像処理ライブラリが対応していない
この記事では、これらの問題に対する実践的な解決策を、Python + OpenCV環境を中心に紹介します。
2. なぜ10bit/12bit画像は扱いが難しいのか?
通常の8bitグレースケール画像(0〜255)に比べ、
10bit画像は 0〜1023、12bit画像は 0〜4095 というより広い階調を持ちます。
このため、従来の8bit前提のソフトウェアでは、正しく表示・保存・処理ができないという問題が発生します。
3. 表示:OpenCVで10bit/12bit画像を明るく表示するには?
❌ 問題点:そのまま表示すると「真っ暗」に見える
OpenCVのcv2.imshow()
で16bit画像を表示すると、ダイナミックレンジが0〜65535として扱われます。
そのため、10bit(最大1023)や12bit(最大4095)画像は、非常に暗く表示されてしまいます。
✅ 対処法:ビットシフトで8bitに変換
import cv2
import numpy as np
img_16bit = cv2.imread("image_10bit.tiff", cv2.IMREAD_UNCHANGED) # dtype=uint16
img_8bit = (img_16bit >> 2).astype(np.uint8) # 10bit → 8bit(右に2bitシフト)
cv2.imshow("10bit image (converted)", img_8bit)
cv2.waitKey(0)
cv2.destroyAllWindows()
📌 10bit画像 → 2bitシフト
📌 12bit画像 → 4bitシフト
(10bitグレースケール画像を2bitシフトした画像表示)
これは表示専用の変換です。元のbit深度のまま処理したい場合は、uint16
のまま扱います。
4. 保存・読込:10bit/12bit画像は保存できない? → TIFFを使う
❌ 問題点:そもそも対応していないフォーマットが多い。
- Bitmap(*.bmp)ファイルでBitfieldを使って10bit/12bitとして保存することもできますが、画像を開くアプリが対応していないことが多い。
- 保存時に自動で他bitに変換されることがあります。(ライブラリ、アプリ依存です。)
✅ 対処法:TIFF形式で保存(MinSampleValue/MaxSampleValueタグ付き)
TIFF形式は柔軟性が高く、以下のようなタグで階調情報を保持できます:
BitsPerSample = 16
MinSampleValue = 0
MaxSampleValue = 1023 or 4095
🧪 保存例:tifffileで10bit TIFF保存
import tifffile
import numpy as np
width = 1024
height = 256
gradient_line = np.linspace(0, 1023, width, dtype=np.uint16)
img_10bit = np.tile(gradient_line, (height, 1))
tifffile.imwrite(
"output_10bit.tiff",
img_10bit,
metadata={"MinSampleValue": 0, "MaxSampleValue": 1023},
)
⚠ ほとんどの画像表示ソフトではMinSampleValue/MaxSampleValueタグを無視します。
画像解析ソフトをして有名なImageJでは、初期設定で、最小輝度値を黒、最大輝度値を白となるように明るさが調整され表示されます。
📥 読込例:TIFF画像の読込と確認
import tifffile
import cv2
import numpy as np
# 読込
img = tifffile.imread("output_10bit.tiff") # dtype=np.uint16
# 情報確認
print("dtype:", img.dtype)
print("shape:", img.shape)
# 表示用に8bit変換
img_8bit = (img >> 2).astype(np.uint8)
cv2.imshow("TIFF 10bit image (converted)", img_8bit)
cv2.waitKey(0)
cv2.destroyAllWindows()
5. 処理:OpenCVなどのライブラリで対応できる?
❌ 問題点:多くの関数は8bit専用
以下のOpenCV関数は、uint16
には非対応です:
関数名 | 対応bit深度 |
---|---|
cv2.Canny() |
uint8 のみ |
cv2.equalizeHist() |
uint8 のみ |
cv2.findContours() |
uint8 のみ |
✅ 対処法:処理前に一時的に8bitへ変換する
# 10bit画像(uint16)→ 8bit に変換して処理
img_8bit = (img_16bit >> 2).astype(np.uint8)
edges = cv2.Canny(img_8bit, 50, 150)
6. おわりに
10bitや12bitグレースケール画像は、高精細な画像処理を実現する強力な手段です。
しかし、次のような落とし穴に注意が必要です:
課題 | 対処方法 |
---|---|
表示が真っ暗になる | → ビットシフトして8bitに変換 |
保存ができない | → TIFF形式+タグ付きで保存 |
処理できない関数が多い | → 処理前に一時的に8bit変換 or ライブラリ選定 or 自作 |