1
High Dynamic Range (HDR) {#tutorial_py_hdr}
2
========================
7
In this chapter, we will
9
- Learn how to generate and display HDR image from an exposure sequence.
10
- Use exposure fusion to merge an exposure sequence.
15
High-dynamic-range imaging (HDRI or HDR) is a technique used in imaging and photography to reproduce
16
a greater dynamic range of luminosity than is possible with standard digital imaging or photographic
17
techniques. While the human eye can adjust to a wide range of light conditions, most imaging devices use 8-bits
18
per channel, so we are limited to only 256 levels. When we take photographs of a real
19
world scene, bright regions may be overexposed, while the dark ones may be underexposed, so we
20
can’t capture all details using a single exposure. HDR imaging works with images that use more
21
than 8 bits per channel (usually 32-bit float values), allowing much wider dynamic range.
23
There are different ways to obtain HDR images, but the most common one is to use photographs of
24
the scene taken with different exposure values. To combine these exposures it is useful to know your
25
camera’s response function and there are algorithms to estimate it. After the HDR image has been
26
merged, it has to be converted back to 8-bit to view it on usual displays. This process is called
27
tonemapping. Additional complexities arise when objects of the scene or camera move between shots,
28
since images with different exposures should be registered and aligned.
30
In this tutorial we show 2 algorithms (Debvec, Robertson) to generate and display HDR image from an
31
exposure sequence, and demonstrate an alternative approach called exposure fusion (Mertens), that
32
produces low dynamic range image and does not need the exposure times data.
33
Furthermore, we estimate the camera response function (CRF) which is of great value for many computer
35
Each step of HDR pipeline can be implemented using different algorithms and parameters, so take a
36
look at the reference manual to see them all.
42
In this tutorial we will look on the following scene, where we have 4 exposure
43
images, with exposure times of: 15, 2.5, 1/4 and 1/30 seconds. (You can download
44
the images from [Wikipedia](https://en.wikipedia.org/wiki/High-dynamic-range_imaging))
46
![image](images/exposures.jpg)
48
### 1. Loading exposure images into a list
50
The first stage is simply loading all images into a list.
51
In addition, we will need the exposure times for the regular HDR algorithms.
52
Pay attention for the data types, as the images should be 1-channel or 3-channels
53
8-bit (np.uint8) and the exposure times need to be float32 and in seconds.
59
# Loading exposure images into a list
60
img_fn = ["img0.jpg", "img1.jpg", "img2.jpg", "img3.jpg"]
61
img_list = [cv2.imread(fn) for fn in img_fn]
62
exposure_times = np.array([15.0, 2.5, 0.25, 0.0333], dtype=np.float32)
65
### 2. Merge exposures into HDR image
67
In this stage we merge the exposure sequence into one HDR image, showing 2 possibilities
68
which we have in OpenCV. The first method is Debvec and the second one is Robertson.
69
Notice that the HDR image is of type float32, and not uint8, as it contains the
70
full dynamic range of all exposure images.
73
# Merge exposures to HDR image
74
merge_debvec = cv2.createMergeDebevec()
75
hdr_debvec = merge_debvec.process(img_list, times=exposure_times.copy())
76
merge_robertson = cv2.createMergeRobertson()
77
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy())
80
### 3. Tonemap HDR image
82
We map the 32-bit float HDR data into the range [0..1].
83
Actually, in some cases the values can be larger than 1 or lower the 0, so notice
84
we will later have to clip the data in order to avoid overflow.
88
tonemap1 = cv2.createTonemapDurand(gamma=2.2)
89
res_debvec = tonemap1.process(hdr_debvec.copy())
90
tonemap2 = cv2.createTonemapDurand(gamma=1.3)
91
res_robertson = tonemap2.process(hdr_robertson.copy())
94
### 4. Merge exposures using Mertens fusion
96
Here we show an alternative algorithm to merge the exposure images, where
97
we do not need the exposure times. We also do not need to use any tonemap
98
algorithm because the Mertens algorithm already gives us the result in the
102
# Exposure fusion using Mertens
103
merge_mertens = cv2.createMergeMertens()
104
res_mertens = merge_mertens.process(img_list)
107
### 5. Convert to 8-bit and save
109
In order to save or display the results, we need to convert the data into 8-bit
110
integers in the range of [0..255].
113
# Convert datatype to 8-bit and save
114
res_debvec_8bit = np.clip(res_debvec*255, 0, 255).astype('uint8')
115
res_robertson_8bit = np.clip(res_robertson*255, 0, 255).astype('uint8')
116
res_mertens_8bit = np.clip(res_mertens*255, 0, 255).astype('uint8')
118
cv2.imwrite("ldr_debvec.jpg", res_debvec_8bit)
119
cv2.imwrite("ldr_robertson.jpg", res_robertson_8bit)
120
cv2.imwrite("fusion_mertens.jpg", res_mertens_8bit)
126
You can see the different results but consider that each algorithm have additional
127
extra parameters that you should fit to get your desired outcome. Best practice is
128
to try the different methods and see which one performs best for your scene.
132
![image](images/ldr_debvec.jpg)
136
![image](images/ldr_robertson.jpg)
140
![image](images/fusion_mertens.jpg)
143
Estimating Camera Response Function
144
-----------------------------------
146
The camera response function (CRF) gives us the connection between the scene radiance
147
to the measured intensity values. The CRF if of great importance in some computer vision
148
algorithms, including HDR algorithms. Here we estimate the inverse camera response
149
function and use it for the HDR merge.
152
# Estimate camera response function (CRF)
153
cal_debvec = cv2.createCalibrateDebevec()
154
crf_debvec = cal_debvec.process(img_list, times=exposure_times)
155
hdr_debvec = merge_debvec.process(img_list, times=exposure_times.copy(), response=crf_debvec.copy())
156
cal_robertson = cv2.createCalibrateRobertson()
157
crf_robertson = cal_robertson.process(img_list, times=exposure_times)
158
hdr_robertson = merge_robertson.process(img_list, times=exposure_times.copy(), response=crf_robertson.copy())
161
The camera response function is represented by a 256-length vector for each color channel.
162
For this sequence we got the following estimation:
164
![image](images/crf.jpg)
169
1. Paul E Debevec and Jitendra Malik. Recovering high dynamic range radiance maps from photographs. In ACM SIGGRAPH 2008 classes, page 31. ACM, 2008.
170
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.
171
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.
172
4. Images from [Wikipedia-HDR](https://en.wikipedia.org/wiki/High-dynamic-range_imaging)
176
1. Try all tonemap algorithms: [Drago](http://docs.opencv.org/master/da/d53/classcv_1_1TonemapDrago.html), [Durand](http://docs.opencv.org/master/da/d3d/classcv_1_1TonemapDurand.html), [Mantiuk](http://docs.opencv.org/master/de/d76/classcv_1_1TonemapMantiuk.html) and [Reinhard](http://docs.opencv.org/master/d0/dec/classcv_1_1TonemapReinhard.html).
177
2. Try changing the parameters in the HDR calibration and tonemap methods.
b'\\ No newline at end of file'