OpenCV 4.13.0
Open Source Computer Vision
読み込み中...
検索中...
見つかりません
🤖 AIによる機械翻訳(非公式) — これは OpenCV 4.13.0 公式リファレンス(英語)を AI (Claude) で自動翻訳したものです。訳に誤りを含む場合があります。正確な情報は 公式英語版(原文) を参照してください。
ハイダイナミックレンジ (HDR)

目的

この章では、次のことを行う。

  • 露光シーケンスからHDR画像を生成して表示する方法を学ぶ。
  • 露光シーケンスを統合するために露光合成(exposure fusion)を使う。

理論

ハイダイナミックレンジイメージング(HDRIまたはHDR)は、標準的なデジタルイメージングや写真技術で可能なものよりも広い輝度のダイナミックレンジを再現するために、画像処理や写真撮影で用いられる技術である。人間の目は広範囲の光条件に適応できるが、ほとんどのイメージング機器はチャンネルあたり8ビットを使用するため、256階調しか扱えない。現実世界のシーンを撮影すると、明るい領域は白飛びし、暗い領域は黒つぶれすることがあり、1枚の露光ではすべてのディテールを捉えることができない。HDRイメージングはチャンネルあたり8ビットより多くのビット(通常は32ビット浮動小数点値)を使う画像を扱い、はるかに広いダイナミックレンジを可能にする。

HDR画像を得る方法はいくつかあるが、最も一般的なのは、異なる露光値で撮影したシーンの写真を用いる方法である。これらの露光を合成するには、カメラの応答関数を知っておくと便利であり、それを推定するアルゴリズムも存在する。HDR画像が統合された後は、通常のディスプレイで表示するために8ビットへ変換し直す必要がある。この処理はトーンマッピングと呼ばれる。撮影の合間にシーン内の物体やカメラが動く場合は、異なる露光の画像を位置合わせして整合させる必要があるため、さらに複雑さが増す。

このチュートリアルでは、露光シーケンスからHDR画像を生成して表示する2つのアルゴリズム(Debevec、Robertson)を示し、さらに露光合成(Mertens)と呼ばれる別の手法を紹介する。これは低ダイナミックレンジ画像を生成し、露光時間のデータを必要としない。さらに、多くのコンピュータビジョンアルゴリズムにとって非常に価値のあるカメラ応答関数(CRF)を推定する。HDRパイプラインの各ステップは異なるアルゴリズムや引数で実装できるため、それらをすべて確認するにはリファレンスマニュアルを参照するとよい。

露光シーケンスによるHDR

このチュートリアルでは、露光時間が 15、2.5、1/4、1/30 秒の4枚の露光画像があるシーンを見ていく。(画像は Wikipedia からダウンロードできる)

image

1. 露光画像をリストに読み込む

最初の段階は、すべての画像を単にリストに読み込むだけである。加えて、通常のHDRアルゴリズムには露光時間が必要になる。画像は1チャンネルまたは3チャンネルの8ビット(np.uint8)、露光時間はfloat32かつ秒単位である必要があるため、データ型に注意すること。

import cv2 as cv
import numpy as np
# Loading exposure images into a list
img_fn = ["img0.jpg", "img1.jpg", "img2.jpg", "img3.jpg"]
img_list = [cv.imread(fn) for fn in img_fn]
exposure_times = np.array([15.0, 2.5, 0.25, 0.0333], dtype=np.float32)
Mat imread(const String &filename, int flags=IMREAD_COLOR_BGR)
Loads an image from a file.

2. 露光をHDR画像に統合する

この段階では、露光シーケンスを1枚のHDR画像に統合する。OpenCVで利用できる2つの方法を示す。1つ目はDebevec、2つ目はRobertsonである。HDR画像はすべての露光画像の全ダイナミックレンジを含むため、uint8ではなくfloat32型であることに注意すること。

# Merge exposures to HDR image
merge_debevec = cv.createMergeDebevec()
hdr_debevec = merge_debevec.process(img_list, times=exposure_times.copy())
merge_robertson = cv.createMergeRobertson()
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy())
Ptr< MergeDebevec > createMergeDebevec()
Creates MergeDebevec object.
Ptr< MergeRobertson > createMergeRobertson()
Creates MergeRobertson object.

3. HDR画像をトーンマッピングする

32ビット浮動小数点のHDRデータを [0..1] の範囲にマッピングする。実際には、場合によっては値が1より大きくなったり0より小さくなったりすることがあるため、オーバーフローを避けるために後でデータをクリップする必要がある点に注意すること。

覚え書き
: 関数 cv.createTonemap() はデフォルトのガンマ値として 1.0 を使用する。標準的なディスプレイの明るさに合わせ、一貫したトーンマッピング結果を得るには、これを明示的に 2.2 に設定すること。
# Tonemap HDR images using gamma correction (set gamma=2.2 for standard display brightness)
tonemap1 = cv.createTonemap(gamma=2.2)
res_debevec = tonemap1.process(hdr_debevec.copy())
res_robertson = tonemap1.process(hdr_robertson.copy())
Ptr< Tonemap > createTonemap(float gamma=1.0f)
Creates simple linear mapper with gamma correction.

4. Mertens合成を用いて露光を統合する

ここでは、露光時間を必要としない別の露光画像統合アルゴリズムを示す。Mertensアルゴリズムはすでに [0..1] の範囲で結果を返すため、トーンマップアルゴリズムを使う必要もない。

# Exposure fusion using Mertens
merge_mertens = cv.createMergeMertens()
res_mertens = merge_mertens.process(img_list)
Ptr< MergeMertens > createMergeMertens(float contrast_weight=1.0f, float saturation_weight=1.0f, float exposure_weight=0.0f)
Creates MergeMertens object.

5. 8ビットに変換して保存する

結果を保存または表示するには、データを [0..255] の範囲の8ビット整数に変換する必要がある。

# Convert datatype to 8-bit and save
res_debevec_8bit = np.clip(res_debevec*255, 0, 255).astype('uint8')
res_robertson_8bit = np.clip(res_robertson*255, 0, 255).astype('uint8')
res_mertens_8bit = np.clip(res_mertens*255, 0, 255).astype('uint8')
cv.imwrite("ldr_debevec.jpg", res_debevec_8bit)
cv.imwrite("ldr_robertson.jpg", res_robertson_8bit)
cv.imwrite("fusion_mertens.jpg", res_mertens_8bit)
bool imwrite(const String &filename, InputArray img, const std::vector< int > &params=std::vector< int >())
Saves an image to a specified file.

結果

さまざまな結果を見ることができるが、各アルゴリズムには望ましい結果を得るために調整すべき追加の引数があることを考慮すること。さまざまな手法を試し、自分のシーンに最も適したものを見極めるのがベストプラクティスである。

以下の結果は、トーンマッピング時にガンマ値 2.2 を用いて生成したものである。

Debevec:

image

Robertson:

image

Mertens合成:

image

カメラ応答関数の推定

カメラ応答関数(CRF)は、シーンの放射輝度と測定される強度値とのつながりを与える。CRFはHDRアルゴリズムを含むいくつかのコンピュータビジョンアルゴリズムで非常に重要である。ここでは逆カメラ応答関数を推定し、それをHDRの統合に用いる。

# Estimate camera response function (CRF)
crf_debevec = cal_debevec.process(img_list, times=exposure_times)
hdr_debevec = merge_debevec.process(img_list, times=exposure_times.copy(), response=crf_debevec.copy())
cal_robertson = cv.createCalibrateRobertson()
crf_robertson = cal_robertson.process(img_list, times=exposure_times)
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy(), response=crf_robertson.copy())
Ptr< CalibrateRobertson > createCalibrateRobertson(int max_iter=30, float threshold=0.01f)
Creates CalibrateRobertson object.
Ptr< CalibrateDebevec > createCalibrateDebevec(int samples=70, float lambda=10.0f, bool random=false)
Creates CalibrateDebevec object.

カメラ応答関数は、各色チャンネルについて長さ256のベクトルで表される。このシーケンスでは、次の推定結果が得られた。

image

追加リソース

  1. Paul E Debevec and Jitendra Malik. Recovering high dynamic range radiance maps from photographs. In ACM SIGGRAPH 2008 classes, page 31. ACM, 2008. [71]
  2. Mark A Robertson, Sean Borman, and Robert L Stevenson. Dynamic range improvement through multiple exposures. In Image Processing, 1999. ICIP 99. Proceedings. 1999 International Conference on, volume 3, pages 159–163. IEEE, 1999. [233]
  3. Tom Mertens, Jan Kautz, and Frank Van Reeth. Exposure fusion. In Computer Graphics and Applications, 2007. PG'07. 15th Pacific Conference on, pages 382–390. IEEE, 2007. [194]
  4. 画像の出典 Wikipedia-HDR

演習

  1. すべてのトーンマップアルゴリズムを試す: cv::TonemapDragocv::TonemapMantiukcv::TonemapReinhard
  2. HDRキャリブレーションとトーンマップの各メソッドで引数を変えてみる。