عکاسی محاسباتی

دامنه دینامیکی بالا (HDR)

۶ دقیقه مطالعهOpenCV 5.xبه‌روزرسانی:

هدف#

در این فصل ما:

  • یاد می‌گیریم چگونه تصویر HDR را از یک توالی نوردهی (Exposure Sequence) تولید و نمایش دهیم.
  • از ترکیب نوردهی (Exposure Fusion) برای ادغام توالی نوردهی استفاده کنیم.

تئوری#

تصویربرداری با دامنه دینامیکی بالا (HDRI یا HDR) تکنیکی است که در تصویربرداری و عکاسی برای بازتولید دامنه دینامیکی بزرگ‌تری از روشنایی نسبت به آنچه با تکنیک‌های استاندارد عکاسی یا تصویربرداری دیجیتال امکان‌پذیر است، استفاده می‌شود. در حالی که چشم انسان می‌تواند با طیف وسیعی از شرایط نوری سازگار شود، اکثر دستگاه‌های تصویربرداری از ۸ بیت در هر کانال استفاده می‌کنند، بنابراین ما تنها به ۲۵۶ سطح روشنایی محدود هستیم. هنگامی که از صحنه‌ای در دنیای واقعی عکس می‌گیریم، نواحی روشن ممکن است بیش از حد نوردهی شوند (Overexposed)، در حالی که نواحی تاریک ممکن است نوردهی کم داشته باشند (Underexposed)؛ بنابراین نمی‌توانیم تمام جزئیات را با استفاده از یک نوردهی واحد ثبت کنیم. تصویربرداری HDR با تصاویری کار می‌کند که از بیش از ۸ بیت در هر کانال استفاده می‌کنند (معمولاً مقادیر ممیز شناور ۳۲ بیتی)، که امکان دامنه دینامیکی بسیار وسیع‌تری را فراهم می‌سازد.

راه‌های مختلفی برای به دست آوردن تصاویر HDR وجود دارد، اما متداول‌ترین روش استفاده از عکس‌های گرفته‌شده از صحنه با مقادیر مختلف نوردهی است. برای ترکیب این نوردهی‌ها، دانستن تابع پاسخ دوربین (Camera Response Function یا CRF) مفید است و الگوریتم‌هایی برای تخمین آن وجود دارد. پس از ادغام تصویر HDR، باید آن را مجدداً به ۸ بیتی تبدیل کرد تا در نمایشگرهای معمولی قابل مشاهده باشد. این فرآیند نگاشت تُن (Tonemapping) نامیده می‌شود. پیچیدگی‌های بیشتری زمانی ایجاد می‌شود که اشیاء موجود در صحنه یا خود دوربین بین عکس‌ها حرکت کنند، زیرا تصاویر با نوردهی‌های مختلف باید ثبت و هم‌تراز (Aligned) شوند.

در این آموزش ۲ الگوریتم (Debevec و Robertson) را برای تولید و نمایش تصویر HDR از توالی نوردهی نشان می‌دهیم و یک رویکرد جایگزین به نام ترکیب نوردهی یا فیوژن (Mertens) را به نمایش می‌گذاریم که تصویر با دامنه دینامیکی پایین (LDR) تولید می‌کند و نیازی به داده‌های زمان نوردهی ندارد. علاوه بر این، تابع پاسخ دوربین (CRF) را تخمین می‌زنیم که برای بسیاری از الگوریتم‌های بینایی کامپیوتر ارزش بالایی دارد. هر مرحله از خط لوله HDR را می‌توان با استفاده از الگوریتم‌ها و پارامترهای مختلفی پیاده‌سازی کرد، بنابراین برای دیدن همه آن‌ها به کتابچه راهنمای مرجع نگاهی بیندازید.

توالی نوردهی HDR#

در این آموزش به صحنه زیر نگاه خواهیم کرد، جایی که ما ۴ تصویر با زمان‌های نوردهی ۱۵، ۲.۵، ۱/۴ و ۱/۳۰ ثانیه داریم. (می‌توانید تصاویر را از ویکی‌پدیا دانلود کنید)

دامنه دینامیکی بالا (HDR) — تصویر ۱

۱. بارگذاری تصاویر نوردهی در یک لیست#

مرحله اول صرفاً بارگذاری تمام تصاویر در یک لیست است. علاوه بر این، ما به زمان‌های نوردهی برای الگوریتم‌های منظم HDR نیاز خواهیم داشت. به انواع داده‌ها دقت کنید؛ تصاویر باید تک‌کاناله یا ۳‌کاناله ۸ بیتی (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)

۲. ادغام نوردهی‌ها در یک تصویر HDR#

در این مرحله ما توالی نوردهی را در یک تصویر HDR ادغام می‌کنیم و ۲ امکانی را که در OpenCV داریم نشان می‌دهیم. روش اول Debevec و روش دوم Robertson است. توجه داشته باشید که تصویر HDR از نوع float32 است و نه uint8، زیرا شامل دامنه دینامیکی کامل تمام تصاویر نوردهی است.

# 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())

۳. نگاشت تُن (Tonemap) تصویر HDR#

ما داده‌های ۳۲ بیتی اعشاری HDR را در بازه [0..1] نگاشت می‌کنیم. در واقع، در برخی موارد مقادیر می‌توانند بزرگ‌تر از ۱ یا کمتر از ۰ باشند، بنابراین توجه داشته باشید که بعداً باید داده‌ها را برش (clip) دهیم تا از سرریز (overflow) جلوگیری شود.

# 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())

۴. ادغام نوردهی‌ها با استفاده از فیوژن Mertens#

در اینجا ما یک الگوریتم جایگزین برای ادغام تصاویر نوردهی نشان می‌دهیم که در آن نیازی به زمان‌های نوردهی نداریم. ما همچنین نیازی به استفاده از هیچ الگوریتم نگاشت تُن نداریم، زیرا الگوریتم Mertens از قبل نتیجه را در محدوده [0..1] به ما می‌دهد.

# Exposure fusion using Mertens
merge_mertens = cv.createMergeMertens()
res_mertens = merge_mertens.process(img_list)

۵. تبدیل به ۸ بیتی و ذخیره‌سازی#

به منظور ذخیره یا نمایش نتایج، باید داده‌ها را به اعداد صحیح ۸ بیتی در محدوده [0..255] تبدیل کنیم.

# 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)

نتایج#

می‌توانید نتایج مختلف را مشاهده کنید، اما در نظر داشته باشید که هر الگوریتم دارای پارامترهای اضافی است که باید آن‌ها را متناسب با نتیجه دلخواه خود تنظیم کنید. بهترین شیوه این است که روش‌های مختلف را امتحان کنید و ببینید کدام‌یک برای صحنه شما بهترین عملکرد را دارد.

نتایج زیر با مقدار گامای ۲.۲ در طول نگاشت تُن تولید شده‌اند:

Debevec:#

دامنه دینامیکی بالا (HDR) — تصویر ۲

Robertson:#

دامنه دینامیکی بالا (HDR) — تصویر ۳

Mertenes Fusion:#

دامنه دینامیکی بالا (HDR) — تصویر ۴

تخمین تابع پاسخ دوربین#

تابع پاسخ دوربین (CRF) ارتباط بین درخشندگی صحنه (Scene Radiance) و مقادیر شدت اندازه‌گیری‌شده را به ما می‌دهد. CRF در برخی الگوریتم‌های بینایی کامپیوتر از جمله الگوریتم‌های HDR از اهمیت بالایی برخوردار است. در اینجا ما تابع معکوس پاسخ دوربین را تخمین می‌زنیم و از آن برای ادغام HDR استفاده می‌کنیم.

# Estimate camera response function (CRF)
cal_debevec = cv.createCalibrateDebevec()
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())

تابع پاسخ دوربین با یک بردار به طول ۲۵۶ برای هر کانال رنگی نمایش داده می‌شود. برای این توالی ما تخمین زیر را به دست آوردیم:

دامنه دینامیکی بالا (HDR) — تصویر ۵

منابع اضافی#

  1. Paul E Debevec and Jitendra Malik. Recovering high dynamic range radiance maps from photographs. In ACM SIGGRAPH 2008 classes, page 31. ACM, 2008. [DM97]
  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. [RB99]
  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. [MK07]
  4. تصاویر از Wikipedia-HDR

تمرین‌ها#

  1. تمام الگوریتم‌های نگاشت تُن را امتحان کنید: cv::TonemapDrago، cv::TonemapMantiuk و cv::TonemapReinhard
  2. تغییر پارامترها در روش‌های کالیبراسیون HDR و روش‌های نگاشت تُن را امتحان نمایید.