自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

如何使用OpenCV進(jìn)行高動(dòng)態(tài)范圍(HDR)成像

開發(fā) 后端
在本教程中,我們將學(xué)習(xí)如何使用由不同曝光設(shè)置拍攝的多張圖像創(chuàng)建高動(dòng)態(tài)范圍(HDR)圖像。 我們將以 C++ 和 Python 兩種形式分享代碼。

[[233297]]

在本教程中,我們將學(xué)習(xí)如何使用由不同曝光設(shè)置拍攝的多張圖像創(chuàng)建高動(dòng)態(tài)范圍High Dynamic Range(HDR)圖像。 我們將以 C++ 和 Python 兩種形式分享代碼。 

什么是高動(dòng)態(tài)范圍成像?

大多數(shù)數(shù)碼相機(jī)和顯示器都是按照 24 位矩陣捕獲或者顯示彩色圖像。 每個(gè)顏色通道有 8 位,因此每個(gè)通道的像素值在 0-255 范圍內(nèi)。 換句話說,普通的相機(jī)或者顯示器的動(dòng)態(tài)范圍是有限的。

但是,我們周圍世界動(dòng)態(tài)范圍極大。 在車庫內(nèi)關(guān)燈就會(huì)變黑,直接看著太陽就會(huì)變得非常亮。 即使不考慮這些極端,在日常情況下,8 位的通道勉強(qiáng)可以捕捉到現(xiàn)場場景。 因此,相機(jī)會(huì)嘗試去評(píng)估光照并且自動(dòng)設(shè)置曝光,這樣圖像的最關(guān)注區(qū)域就會(huì)有良好的動(dòng)態(tài)范圍,并且太暗和太亮的部分會(huì)被相應(yīng)截取為 0 和 255。

在下圖中,左側(cè)的圖像是正常曝光的圖像。 請(qǐng)注意,由于相機(jī)決定使用拍攝主體(我的兒子)的設(shè)置,所以背景中的天空已經(jīng)完全流失了,但是明亮的天空也因此被刷掉了。 右側(cè)的圖像是由 iPhone 生成的HDR圖像。

High Dynamic Range (HDR)

High Dynamic Range (HDR)

iPhone 是如何拍攝 HDR 圖像的呢? 它實(shí)際上采用三種不同的曝光度拍攝了 3 張圖像,3 張圖像拍攝非常迅速,在 3 張圖像之間幾乎沒有產(chǎn)生位移。然后組合三幅圖像來產(chǎn)生 HDR 圖像。 我們將在下一節(jié)看到一些細(xì)節(jié)。

將在不同曝光設(shè)置下獲取的相同場景的不同圖像組合的過程稱為高動(dòng)態(tài)范圍(HDR)成像。 

高動(dòng)態(tài)范圍(HDR)成像是如何工作的?

在本節(jié)中,我們來看下使用 OpenCV 創(chuàng)建 HDR 圖像的步驟。

要想輕松學(xué)習(xí)本教程,請(qǐng)點(diǎn)擊此處下載 C++ 和 Python 代碼還有圖像。 如果您有興趣了解更多關(guān)于人工智能,計(jì)算機(jī)視覺和機(jī)器學(xué)習(xí)的信息,請(qǐng)訂閱我們的電子雜志。 

第 1 步:捕獲不同曝光度的多張圖像

當(dāng)我們使用相機(jī)拍照時(shí),每個(gè)通道只有 8 位來表示場景的動(dòng)態(tài)范圍(亮度范圍)。 但是,通過改變快門速度,我們可以在不同的曝光條件下拍攝多個(gè)場景圖像。 大多數(shù)單反相機(jī)(SLR)有一個(gè)功能稱為自動(dòng)包圍式曝光Auto Exposure Bracketing(AEB),只需按一下按鈕,我們就可以在不同的曝光下拍攝多張照片。 如果你正在使用 iPhone,你可以使用這個(gè)自動(dòng)包圍式 HDR 應(yīng)用程序,如果你是一個(gè) Android 用戶,你可以嘗試一個(gè)更好的相機(jī)應(yīng)用程序。

場景沒有變化時(shí),在相機(jī)上使用自動(dòng)包圍式曝光或在手機(jī)上使用自動(dòng)包圍式應(yīng)用程序,我們可以一張接一張地快速拍攝多張照片。 當(dāng)我們?cè)?iPhone 中使用 HDR 模式時(shí),會(huì)拍攝三張照片。

  1. 曝光不足的圖像:該圖像比正確曝光的圖像更暗。 目標(biāo)是捕捉非常明亮的圖像部分。
  2. 正確曝光的圖像:這是相機(jī)將根據(jù)其估計(jì)的照明拍攝的常規(guī)圖像。
  3. 曝光過度的圖像:該圖像比正確曝光的圖像更亮。 目標(biāo)是拍攝非常黑暗的圖像部分。

但是,如果場景的動(dòng)態(tài)范圍很大,我們可以拍攝三張以上的圖片來合成 HDR 圖像。 在本教程中,我們將使用曝光時(shí)間為1/30 秒,0.25 秒,2.5 秒和 15 秒的 4 張圖像。 縮略圖如下所示。

Auto Exposure Bracketed  HDR image sequence

Auto Exposure Bracketed HDR image sequence

單反相機(jī)或手機(jī)的曝光時(shí)間和其他設(shè)置的信息通常存儲(chǔ)在 JPEG 文件的 EXIF 元數(shù)據(jù)中。 查看此鏈接可在 Windows 和 Mac 中查看存儲(chǔ)在 JPEG 文件中的 EXIF 元數(shù)據(jù)。 或者,您可以使用我最喜歡的名為 EXIFTOOL 的查看 EXIF 的命令行工具。

我們先從讀取分配到不同曝光時(shí)間的圖像開始。

C++

  1. void readImagesAndTimes(vector<Mat> &images, vector<float> &times)
  2. {
  3.  
  4. int numImages = 4;
  5.  
  6. // 曝光時(shí)間列表
  7. static const float timesArray[] = {1/30.0f,0.25,2.5,15.0};
  8. times.assign(timesArray, timesArray + numImages);
  9.  
  10. // 圖像文件名稱列表
  11. static const char* filenames[] = {"img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"};
  12. for(int i=0; i < numImages; i++)
  13. {
  14. Mat im = imread(filenames[i]);
  15. images.push_back(im);
  16. }
  17.  
  18. }

Python

  1. def readImagesAndTimes():
  2. # 曝光時(shí)間列表
  3. times = np.array([ 1/30.0, 0.25, 2.5, 15.0 ], dtype=np.float32)
  4.  
  5. # 圖像文件名稱列表
  6. filenames = ["img_0.033.jpg", "img_0.25.jpg", "img_2.5.jpg", "img_15.jpg"]
  7. images = []
  8. for filename in filenames:
  9. im = cv2.imread(filename)
  10. images.append(im)
  11.  
  12. return images, times 

第 2 步:對(duì)齊圖像

合成 HDR 圖像時(shí)使用的圖像如果未對(duì)齊可能會(huì)導(dǎo)致嚴(yán)重的偽影。 在下圖中,左側(cè)的圖像是使用未對(duì)齊的圖像組成的 HDR 圖像,右側(cè)的圖像是使用對(duì)齊的圖像的圖像。 通過放大圖像的一部分(使用紅色圓圈顯示的)我們會(huì)在左側(cè)圖像中看到嚴(yán)重的鬼影。

Misalignment problem in HDR

Misalignment problem in HDR

在拍攝照片制作 HDR 圖像時(shí),專業(yè)攝影師自然是將相機(jī)安裝在三腳架上。 他們還使用稱為鏡像鎖定功能來減少額外的振動(dòng)。 即使如此,圖像可能仍然沒有完美對(duì)齊,因?yàn)闆]有辦法保證無振動(dòng)的環(huán)境。 使用手持相機(jī)或手機(jī)拍攝圖像時(shí),對(duì)齊問題會(huì)變得更糟。

幸運(yùn)的是,OpenCV 提供了一種簡單的方法,使用 AlignMTB 對(duì)齊這些圖像。 該算法將所有圖像轉(zhuǎn)換為中值閾值位圖median threshold bitmaps(MTB)。 圖像的 MTB 生成方式為將比中值亮度的更亮的分配為 1,其余為 0。 MTB 不隨曝光時(shí)間的改變而改變。 因此不需要我們指定曝光時(shí)間就可以對(duì)齊 MTB。

基于 MTB 的對(duì)齊方式的代碼如下。

C++

  1. // 對(duì)齊輸入圖像
  2. Ptr<AlignMTB> alignMTB = createAlignMTB();
  3. alignMTB->process(images, images);

Python

  1. # 對(duì)齊輸入圖像
  2. alignMTB = cv2.createAlignMTB()
  3. alignMTB.process(images, images) 

第 3 步:提取相機(jī)響應(yīng)函數(shù)

典型相機(jī)的響應(yīng)與場景亮度不成線性關(guān)系。 那是什么意思呢? 假設(shè)有兩個(gè)物體由同一個(gè)相機(jī)拍攝,在現(xiàn)實(shí)世界中其中一個(gè)物體是另一個(gè)物體亮度的兩倍。 當(dāng)您測(cè)量照片中兩個(gè)物體的像素亮度時(shí),較亮物體的像素值將不會(huì)是較暗物體的兩倍。 在不估計(jì)相機(jī)響應(yīng)函數(shù)Camera Response Function(CRF)的情況下,我們將無法將圖像合并到一個(gè)HDR圖像中。

將多個(gè)曝光圖像合并為 HDR 圖像意味著什么?

只考慮圖像的某個(gè)位置 (x,y) 一個(gè)像素。 如果 CRF 是線性的,則像素值將直接與曝光時(shí)間成比例,除非像素在特定圖像中太暗(即接近 0)或太亮(即接近 255)。 我們可以過濾出這些不好的像素(太暗或太亮),并且將像素值除以曝光時(shí)間來估計(jì)像素的亮度,然后在像素不差的(太暗或太亮)所有圖像上對(duì)亮度值取平均。我們可以對(duì)所有像素進(jìn)行這樣的處理,并通過對(duì)“好”像素進(jìn)行平均來獲得所有像素的單張圖像。

但是 CRF 不是線性的, 我們需要評(píng)估 CRF 把圖像強(qiáng)度變成線性,然后才能合并或者平均它們。

好消息是,如果我們知道每個(gè)圖像的曝光時(shí)間,則可以從圖像估計(jì) CRF。 與計(jì)算機(jī)視覺中的許多問題一樣,找到 CRF 的問題本質(zhì)是一個(gè)最優(yōu)解問題,其目標(biāo)是使由數(shù)據(jù)項(xiàng)和平滑項(xiàng)組成的目標(biāo)函數(shù)最小化。 這些問題通常會(huì)降維到線性最小二乘問題,這些問題可以使用奇異值分解Singular Value Decomposition(SVD)來解決,奇異值分解是所有線性代數(shù)包的一部分。 CRF 提取算法的細(xì)節(jié)在從照片提取高動(dòng)態(tài)范圍輻射圖這篇論文中可以找到。

使用 OpenCV 的 CalibrateDebevec 或者 CalibrateRobertson 就可以用 2 行代碼找到 CRF。本篇教程中我們使用 CalibrateDebevec

C++

  1. // 獲取圖像響應(yīng)函數(shù) (CRF)
  2. Mat responseDebevec;
  3. Ptr<CalibrateDebevec> calibrateDebevec = createCalibrateDebevec();
  4. calibrateDebevec->process(images, responseDebevec, times);
  5.  

Python

  1. # 獲取圖像響應(yīng)函數(shù) (CRF)
  2. calibrateDebevec = cv2.createCalibrateDebevec()
  3. responseDebevec = calibrateDebevec.process(images, times)

下圖顯示了使用紅綠藍(lán)通道的圖像提取的 CRF。

Camera Response Function

Camera Response Function

 

第 4 步:合并圖像

一旦 CRF 評(píng)估結(jié)束,我們可以使用 MergeDebevec 將曝光圖像合并成一個(gè)HDR圖像。 C++ 和 Python 代碼如下所示。

C++

  1. // 將圖像合并為HDR線性圖像
  2. Mat hdrDebevec;
  3. Ptr<MergeDebevec> mergeDebevec = createMergeDebevec();
  4. mergeDebevec->process(images, hdrDebevec, times, responseDebevec);
  5. // 保存圖像
  6. imwrite("hdrDebevec.hdr", hdrDebevec);

Python

  1. # 將圖像合并為HDR線性圖像
  2. mergeDebevec = cv2.createMergeDebevec()
  3. hdrDebevec = mergeDebevec.process(images, times, responseDebevec)
  4. # 保存圖像
  5. cv2.imwrite("hdrDebevec.hdr", hdrDebevec)

上面保存的 HDR 圖像可以在 Photoshop 中加載并進(jìn)行色調(diào)映射。示例圖像如下所示。

HDR Photoshop 色調(diào)映射

HDR Photoshop 色調(diào)映射

 

第 5 步:色調(diào)映射

現(xiàn)在我們已經(jīng)將我們的曝光圖像合并到一個(gè) HDR 圖像中。 你能猜出這個(gè)圖像的最小和最大像素值嗎? 對(duì)于黑色條件,最小值顯然為 0。 理論最大值是什么? 無限大! 在實(shí)踐中,不同情況下的最大值是不同的。 如果場景包含非常明亮的光源,那么最大值就會(huì)非常大。

盡管我們已經(jīng)使用多個(gè)圖像恢復(fù)了相對(duì)亮度信息,但是我們現(xiàn)在又面臨了新的挑戰(zhàn):將這些信息保存為 24 位圖像用于顯示。

將高動(dòng)態(tài)范圍(HDR)圖像轉(zhuǎn)換為 8 位單通道圖像的過程稱為色調(diào)映射。這個(gè)過程的同時(shí)還需要保留盡可能多的細(xì)節(jié)。

有幾種色調(diào)映射算法。 OpenCV 實(shí)現(xiàn)了其中的四個(gè)。 要記住的是沒有一個(gè)絕對(duì)正確的方法來做色調(diào)映射。 通常,我們希望在色調(diào)映射圖像中看到比任何一個(gè)曝光圖像更多的細(xì)節(jié)。 有時(shí)色調(diào)映射的目標(biāo)是產(chǎn)生逼真的圖像,而且往往是產(chǎn)生超現(xiàn)實(shí)圖像的目標(biāo)。 在 OpenCV 中實(shí)現(xiàn)的算法傾向于產(chǎn)生現(xiàn)實(shí)的并不那么生動(dòng)的結(jié)果。

我們來看看各種選項(xiàng)。 以下列出了不同色調(diào)映射算法的一些常見參數(shù)。

  1. 伽馬gamma:該參數(shù)通過應(yīng)用伽馬校正來壓縮動(dòng)態(tài)范圍。 當(dāng)伽馬等于 1 時(shí),不應(yīng)用修正。 小于 1 的伽瑪會(huì)使圖像變暗,而大于 1 的伽馬會(huì)使圖像變亮。
  2. 飽和度saturation:該參數(shù)用于增加或減少飽和度。 飽和度高時(shí),色彩更豐富,更濃。 飽和度值接近零,使顏色逐漸消失為灰度。
  3. 對(duì)比度contrast:控制輸出圖像的對(duì)比度(即 log(maxPixelValue/minPixelValue))。

讓我們來探索 OpenCV 中可用的四種色調(diào)映射算法。 

Drago 色調(diào)映射

Drago 色調(diào)映射的參數(shù)如下所示:

  1. createTonemapDrago
  2. (
  3. float gamma = 1.0f,
  4. float saturation = 1.0f,
  5. float bias = 0.85f
  6. )

這里,bias[0, 1] 范圍內(nèi)偏差函數(shù)的值。 從 0.7 到 0.9 的值通常效果較好。 默認(rèn)值是 0.85。 有關(guān)更多技術(shù)細(xì)節(jié),請(qǐng)參閱這篇論文。

C++ 和 Python 代碼如下所示。 參數(shù)是通過反復(fù)試驗(yàn)獲得的。 最后的結(jié)果乘以 3 只是因?yàn)樗o出了最令人滿意的結(jié)果。

C++

  1. // 使用Drago色調(diào)映射算法獲得24位彩色圖像
  2. Mat ldrDrago;
  3. Ptr<TonemapDrago> tonemapDrago = createTonemapDrago(1.0, 0.7);
  4. tonemapDrago->process(hdrDebevec, ldrDrago);
  5. ldrDrago = 3 * ldrDrago;
  6. imwrite("ldr-Drago.jpg", ldrDrago * 255);

Python

  1. # 使用Drago色調(diào)映射算法獲得24位彩色圖像
  2. tonemapDrago = cv2.createTonemapDrago(1.0, 0.7)
  3. ldrDrago = tonemapDrago.process(hdrDebevec)
  4. ldrDrago = 3 * ldrDrago
  5. cv2.imwrite("ldr-Drago.jpg", ldrDrago * 255)

結(jié)果如下:

使用Drago算法的HDR色調(diào)映射

使用Drago算法的HDR色調(diào)映射

 

Durand 色調(diào)映射

Durand 色調(diào)映射的參數(shù)如下所示:

  1. createTonemapDurand
  2. (
  3. float gamma = 1.0f,
  4. float contrast = 4.0f,
  5. float saturation = 1.0f,
  6. float sigma_space = 2.0f,
  7. float sigma_color = 2.0f
  8. );

該算法基于將圖像分解為基礎(chǔ)層和細(xì)節(jié)層。 使用稱為雙邊濾波器的邊緣保留濾波器來獲得基本層。 sigma_spacesigma_color 是雙邊濾波器的參數(shù),分別控制空間域和彩色域中的平滑量。

有關(guān)更多詳細(xì)信息,請(qǐng)查看這篇論文。

C++

  1. // 使用Durand色調(diào)映射算法獲得24位彩色圖像
  2. Mat ldrDurand;
  3. Ptr<TonemapDurand> tonemapDurand = createTonemapDurand(1.5,4,1.0,1,1);
  4. tonemapDurand->process(hdrDebevec, ldrDurand);
  5. ldrDurand = 3 * ldrDurand;
  6. imwrite("ldr-Durand.jpg", ldrDurand * 255);

Python

  1. # 使用Durand色調(diào)映射算法獲得24位彩色圖像
  2. tonemapDurand = cv2.createTonemapDurand(1.5,4,1.0,1,1)
  3. ldrDurand = tonemapDurand.process(hdrDebevec)
  4. ldrDurand = 3 * ldrDurand
  5. cv2.imwrite("ldr-Durand.jpg", ldrDurand * 255)

結(jié)果如下:

使用Durand算法的HDR色調(diào)映射

使用Durand算法的HDR色調(diào)映射

 

Reinhard 色調(diào)映射

  1.  
  2. createTonemapReinhard
  3. (
  4. float gamma = 1.0f,
  5. float intensity = 0.0f,
  6. float light_adapt = 1.0f,
  7. float color_adapt = 0.0f
  8. )

intensity 參數(shù)應(yīng)在 [-8, 8] 范圍內(nèi)。 更高的亮度值會(huì)產(chǎn)生更明亮的結(jié)果。 light_adapt 控制燈光,范圍為 [0, 1]。 值 1 表示僅基于像素值的自適應(yīng),而值 0 表示全局自適應(yīng)。 中間值可以用于兩者的加權(quán)組合。 參數(shù) color_adapt 控制色彩,范圍為 [0, 1]。 如果值被設(shè)置為 1,則通道被獨(dú)立處理,如果該值被設(shè)置為 0,則每個(gè)通道的適應(yīng)級(jí)別相同。中間值可以用于兩者的加權(quán)組合。

有關(guān)更多詳細(xì)信息,請(qǐng)查看這篇論文

C++

  1. // 使用Reinhard色調(diào)映射算法獲得24位彩色圖像
  2. Mat ldrReinhard;
  3. Ptr<TonemapReinhard> tonemapReinhard = createTonemapReinhard(1.5, 0,0,0);
  4. tonemapReinhard->process(hdrDebevec, ldrReinhard);
  5. imwrite("ldr-Reinhard.jpg", ldrReinhard * 255);

Python

  1. # 使用Reinhard色調(diào)映射算法獲得24位彩色圖像
  2. tonemapReinhard = cv2.createTonemapReinhard(1.5, 0,0,0)
  3. ldrReinhard = tonemapReinhard.process(hdrDebevec)
  4. cv2.imwrite("ldr-Reinhard.jpg", ldrReinhard * 255)

結(jié)果如下:

使用Reinhard算法的HDR色調(diào)映射

使用Reinhard算法的HDR色調(diào)映射

 

Mantiuk 色調(diào)映射

  1. createTonemapMantiuk
  2. (
  3. float gamma = 1.0f,
  4. float scale = 0.7f,
  5. float saturation = 1.0f
  6. )

參數(shù) scale 是對(duì)比度比例因子。 從 0.7 到 0.9 的值通常效果較好

有關(guān)更多詳細(xì)信息,請(qǐng)查看這篇論文。

C++

  1. // 使用Mantiuk色調(diào)映射算法獲得24位彩色圖像
  2. Mat ldrMantiuk;
  3. Ptr<TonemapMantiuk> tonemapMantiuk = createTonemapMantiuk(2.2,0.85, 1.2);
  4. tonemapMantiuk->process(hdrDebevec, ldrMantiuk);
  5. ldrMantiuk = 3 * ldrMantiuk;
  6. imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255);

Python

  1. # 使用Mantiuk色調(diào)映射算法獲得24位彩色圖像
  2. tonemapMantiuk = cv2.createTonemapMantiuk(2.2,0.85, 1.2)
  3. ldrMantiuk = tonemapMantiuk.process(hdrDebevec)
  4. ldrMantiuk = 3 * ldrMantiuk
  5. cv2.imwrite("ldr-Mantiuk.jpg", ldrMantiuk * 255)

結(jié)果如下:

使用Mantiuk算法的HDR色調(diào)映射

使用Mantiuk算法的HDR色調(diào)映射

責(zé)任編輯:龐桂玉 來源: Linux中國
相關(guān)推薦

2022-12-15 21:59:56

AIVR場景

2024-12-10 15:50:00

AI生成

2021-06-25 12:00:20

快手HDR

2021-06-27 17:42:35

全鏈路

2017-09-25 15:43:24

圖像模板Python+Open

2024-01-09 08:20:23

OpenCV二值化灰度化

2024-12-31 12:30:00

OpenCV計(jì)算機(jī)視覺

2017-06-09 15:30:26

HDR

2023-04-12 08:00:00

人工智能ChatGPTPython

2021-02-22 13:44:41

開發(fā)Python金融

2014-12-25 10:15:37

DockerJava

2025-01-27 11:52:23

2018-04-16 09:42:25

編程顏色ncurses

2020-10-15 12:00:01

Python 開發(fā)編程語言

2010-03-25 16:31:55

Python代碼

2021-12-29 10:28:27

鴻蒙HarmonyOS應(yīng)用

2018-03-27 13:50:59

意圖識(shí)別

2009-08-21 16:44:01

2010-06-03 10:04:26

Hadoop安裝
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)