Skip to content

Commit e8ae9b8

Browse files
authored
Add files via upload
0 parents  commit e8ae9b8

9 files changed

+719
-0
lines changed

compute_irradiance.py

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import numpy as np
2+
3+
def compute_irradiance(crf_channel, w, images, B):
4+
H,W,C = images[0].shape
5+
num_images = len(images)
6+
7+
# irradiance map for each color channel
8+
irradiance_map = np.empty((H*W, C))
9+
for ch in range(C):
10+
crf = crf_channel[ch]
11+
num_ = np.empty((num_images, H*W))
12+
den_ = np.empty((num_images, H*W))
13+
for j in range(num_images):
14+
flat_image = (images[j][:,:,ch].flatten()).astype('int32')
15+
num_[j, :] = np.multiply(w[flat_image], crf[flat_image] - B[j])
16+
den_[j, :] = w[flat_image]
17+
18+
irradiance_map[:, ch] = np.sum(num_, axis=0) / (np.sum(den_, axis=0) + 1e-6)
19+
20+
irradiance_map = np.reshape(np.exp(irradiance_map), (H,W,C))
21+
22+
return irradiance_map

gsolve.py

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Solve for imaging system response function.
2+
3+
Given a set of pixel values observed for several pixels in several
4+
images with different exposure times, this function returns the
5+
imaging system’s response function g as well as the log film irradiance
6+
values for the observed pixels.
7+
8+
Assumes:
9+
10+
Zmin = 0
11+
Zmax = 255
12+
13+
Arguments:
14+
15+
Z(i,j) is the pixel values of pixel location number i in image j
16+
B(j) is the log delta t, or log shutter speed, for image j
17+
l is lamdba, the constant that determines the amount of smoothness
18+
w(z) is the weighting function value for pixel value z
19+
20+
Returns:
21+
22+
g(z) is the log exposure corresponding to pixel value z
23+
lE(i) is the log film irradiance at pixel location i
24+
"""
25+
import numpy as np
26+
27+
def gsolve(Z, B, lambda_, w, Zmin, Zmax):
28+
29+
n = Zmax + 1
30+
num_px, num_im = Z.shape
31+
A = np.zeros((num_px * num_im + n, n + num_px))
32+
b = np.zeros((A.shape[0]))
33+
34+
# include the data fitting equations
35+
k = 0
36+
for i in range(num_px):
37+
for j in range(num_im):
38+
wij = w[Z[i,j]]
39+
A[k, Z[i,j]] = wij
40+
A[k, n+i] = -wij
41+
b[k] = wij * B[j]
42+
k += 1
43+
44+
# fix the curve by setting its middle value to 0
45+
A[k, n//2] = 1
46+
k += 1
47+
48+
# include the smoothness equations
49+
for i in range(n-2):
50+
A[k, i]= lambda_ * w[i+1]
51+
A[k, i+1] = -2 * lambda_ * w[i+1]
52+
A[k, i+2] = lambda_ * w[i+1]
53+
k += 1
54+
55+
# solve the system using LLS
56+
output = np.linalg.lstsq(A, b)
57+
x = output[0]
58+
g = x[:n]
59+
lE = x[n:]
60+
61+
return [g, lE]

hdr_debevec.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import numpy as np
2+
import matplotlib.pyplot as mp_plt
3+
from gsolve import gsolve
4+
5+
def plot_crf(crf_channel, C, Zmax):
6+
mp_plt.figure(figsize=(24,8))
7+
channel_names = ['red', 'green', 'blue']
8+
for ch in range(C):
9+
mp_plt.subplot(1,3,ch+1)
10+
mp_plt.plot(crf_channel[ch], np.arange(Zmax+1), color=channel_names[ch], linewidth=2)
11+
mp_plt.xlabel('log(X)')
12+
mp_plt.ylabel('Pixel intensity')
13+
mp_plt.title('CRF for {} channel'.format(channel_names[ch]))
14+
15+
mp_plt.figure(figsize=(8,8))
16+
for ch in range(C):
17+
mp_plt.plot(crf_channel[ch], np.arange(Zmax+1), color=channel_names[ch], linewidth=2, label=channel_names[ch]+' channel')
18+
mp_plt.xlabel('log(X)')
19+
mp_plt.ylabel('Pixel intensity')
20+
mp_plt.title('Camera Response Function'.format(channel_names[ch]))
21+
22+
mp_plt.legend()
23+
24+
def hdr_debevec(images, B, lambda_ = 50, num_px = 150):
25+
num_images = len(images)
26+
Zmin = 0
27+
Zmax = 255
28+
29+
# image parameters
30+
H,W,C = images[0].shape
31+
32+
# optmization parameters
33+
px_idx = np.random.choice(H*W, (num_px,), replace=False)
34+
35+
# define pixel intensity weighting function w
36+
w = np.concatenate((np.arange(128) - Zmin, Zmax - np.arange(128,256)))
37+
38+
# compute Z matrix
39+
Z = np.empty((num_px, num_images))
40+
crf_channel = []
41+
log_irrad_channel = []
42+
for ch in range(C):
43+
for j, image in enumerate(images):
44+
flat_image = image[:,:,ch].flatten()
45+
Z[:, j] = flat_image[px_idx]
46+
47+
# get crf and irradiance for each color channel
48+
[crf, log_irrad] = gsolve(Z.astype('int32'), B, lambda_, w, Zmin, Zmax)
49+
crf_channel.append(crf)
50+
log_irrad_channel.append(log_irrad)
51+
52+
plot_crf(crf_channel, C, Zmax)
53+
return [crf_channel, log_irrad_channel, w]

load_images.py

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import glob, os
2+
import numpy as np
3+
import cv2
4+
5+
def load_images(image_dir, image_ext, root_dir):
6+
iter_items = glob.iglob(root_dir+image_dir+image_ext)
7+
8+
images = []
9+
exposure_times = []
10+
for item in iter_items:
11+
images.append(cv2.cvtColor(cv2.imread(item), code=cv2.COLOR_BGR2RGB))
12+
fname = os.path.basename(item)
13+
num_ = int(fname[:-4].split('_')[0])
14+
den_ = int(fname[:-4].split('_')[-1])
15+
exposure_times.append(num_ / den_)
16+
17+
images = [img for _,img in sorted(zip(exposure_times, images), key=lambda pair: pair[0], reverse=True)]
18+
B = np.log(sorted(exposure_times, reverse=True))
19+
20+
return [images, B]

localtonemap/clahe.py

+227
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import numpy as np
2+
import localtonemap.util as util
3+
import matplotlib.pyplot as plt
4+
5+
from matplotlib import rc
6+
rc('font', size=30)
7+
8+
def hist_equalize(I, numtiles=(8, 8)):
9+
assert I.shape[0] % numtiles[0] == 0 and I.shape[1] % numtiles[1] == 0
10+
img_range = np.array([0, 1])
11+
tile_size = (I.shape[0] // numtiles[0], I.shape[1] // numtiles[1])
12+
tile_mappings = maketile_mapping(I, numtiles, tile_size, img_range, img_range)
13+
out = make_clahe_image(I, tile_mappings, numtiles, tile_size, img_range)
14+
return out
15+
16+
17+
def maketile_mapping(I, numtiles, tile_size, selected_range, full_range, num_bins=256, norm_clip_limit=0.01):
18+
19+
num_pixel_in_tile = np.prod(tile_size)
20+
min_clip_limit = np.ceil(np.float(num_pixel_in_tile) / num_bins)
21+
clip_limit = min_clip_limit + np.round(norm_clip_limit * (num_pixel_in_tile - min_clip_limit))
22+
23+
tile_mappings = []
24+
# image_col = 0
25+
image_row = 0
26+
print('make tile mappings')
27+
28+
for tile_row in range(numtiles[0]):
29+
tile_mappings.append([])
30+
image_col = 0
31+
# image_row = 0
32+
for tile_col in range(numtiles[1]):
33+
# print('tile ({}, {}):'.format(tile_row, tile_col), end=',')
34+
tile = I[image_row:(image_row + tile_size[0]), image_col:(image_col + tile_size[1])]
35+
# print('\timhist', end=',')
36+
tile_hist = imhist(tile, num_bins, full_range[1])
37+
38+
# print('\tclip hist', end=',')
39+
tile_hist = clip_histogram(tile_hist, clip_limit, num_bins)
40+
41+
""" plot histogram
42+
fig = plt.figure(figsize=(20, 12))
43+
plt.bar(np.arange(256) / 256., tile_hist, width=0.005, edgecolor='b');
44+
plt.xlim(0, 1);
45+
plt.xlabel('intensity');
46+
plt.ylabel('count');
47+
plt.tight_layout()
48+
plt.savefig('../result/intermediate/histogram/hist{}{}.pdf'.format(tile_row, tile_col));
49+
"""
50+
51+
# print('\tmake mapping')
52+
tile_mapping = make_mapping(tile_hist, selected_range, num_pixel_in_tile)
53+
tile_mappings[-1].append(tile_mapping)
54+
55+
""" plot mapping
56+
fig = plt.figure(figsize=(20, 12))
57+
plt.plot(np.arange(256) / 256., tile_mapping, lw=2);
58+
plt.xlim(0, 1);
59+
plt.xlabel('x');
60+
plt.ylabel('f(x)');
61+
plt.tight_layout()
62+
plt.savefig('../result/intermediate/histogram/mapping{}{}.pdf'.format(tile_row, tile_col));
63+
"""
64+
65+
image_col += tile_size[1]
66+
image_row += tile_size[0]
67+
return tile_mappings
68+
69+
70+
def imhist(tile, num_bins, top):
71+
"""
72+
image histogram
73+
@param tile: a rectangular tile cropped from the image
74+
@param num_bins: number of bins
75+
@param top: scale the rightmost bin to the top
76+
"""
77+
s = (num_bins - 1.) / top # scale factor
78+
tile_scaled = np.floor(tile * s + .5)
79+
hist = np.zeros(num_bins, dtype=np.int32)
80+
for i in range(num_bins):
81+
hist[i] = np.sum(tile_scaled == i)
82+
return hist
83+
84+
85+
def clip_histogram(img_hist, clip_limit, num_bins):
86+
"""
87+
clip the histogram according to the clipLimit and redistributes clipped pixels across bins below the clipLimit
88+
@param img_hist: histogram of the image
89+
@param clip_limit: the clipping limit
90+
@param num_bins: number of bins
91+
"""
92+
total_excess = np.sum(np.maximum(img_hist - clip_limit, 0))
93+
94+
avg_bin_incr = np.floor(total_excess / num_bins)
95+
upper_limit = clip_limit - avg_bin_incr
96+
97+
for k in range(num_bins):
98+
if img_hist[k] > clip_limit:
99+
img_hist[k] = clip_limit
100+
else:
101+
if img_hist[k] > upper_limit:
102+
total_excess -= clip_limit - img_hist[k]
103+
img_hist[k] = clip_limit
104+
else:
105+
total_excess -= avg_bin_incr
106+
img_hist[k] += avg_bin_incr
107+
108+
# redistributes the remaining pixels, one pixel at a time
109+
k = 0
110+
# print('total excess={}'.format(total_excess), end=';')
111+
while total_excess != 0:
112+
step_size = max(int(np.floor(num_bins / total_excess)), 1)
113+
for m in range(k, num_bins, step_size):
114+
if img_hist[m] < clip_limit:
115+
img_hist[m] += 1
116+
total_excess -= 1
117+
if total_excess == 0:
118+
break
119+
120+
k += 1
121+
if k == num_bins:
122+
k = 0
123+
return img_hist
124+
125+
126+
def make_mapping(img_hist, selected_range, num_pixel_in_tile):
127+
"""
128+
using uniform distribution
129+
"""
130+
high_sum = np.cumsum(img_hist)
131+
val_spread = selected_range[1] - selected_range[0]
132+
133+
scale = val_spread / num_pixel_in_tile
134+
mapping = np.minimum(selected_range[0] + high_sum * scale, selected_range[1])
135+
return mapping
136+
137+
138+
def make_clahe_image(I, tile_mappings, numtiles, tile_size, selected_range, num_bins=256):
139+
"""
140+
interpolates between neighboring tile mappings to produce a new mapping in order to remove artificially induced tile borders
141+
"""
142+
assert num_bins > 1
143+
# print('make clahe image')
144+
Ic = np.zeros_like(I)
145+
146+
bin_step = 1. / (num_bins - 1)
147+
start = np.ceil(selected_range[0] / bin_step)
148+
stop = np.floor(selected_range[1] / bin_step)
149+
150+
aLut = np.arange(0, 1 + 1e-10, 1.0 / (stop - start))
151+
152+
""" plot discontinuous
153+
imgtile_row = 0
154+
for tile_row in range(numtiles[0]):
155+
imgtile_col = 0
156+
for tile_col in range(numtiles[1]):
157+
mapping = tile_mappings[tile_row][tile_col]
158+
tile = I[imgtile_row:imgtile_row+tile_size[0], imgtile_col: imgtile_col+tile_size[1]];
159+
Ic[imgtile_row:imgtile_row+tile_size[0], imgtile_col: imgtile_col+tile_size[1]] = grayxform(tile, mapping);
160+
imgtile_col += tile_size[1]
161+
imgtile_row += tile_size[0]
162+
fig = plt.figure(figsize=(20, 12))
163+
plt.imshow(Ic, cmap='gray')
164+
plt.tight_layout()
165+
plt.axis('off')
166+
plt.show()
167+
"""
168+
169+
imgtile_row = 0
170+
for k in range(numtiles[0] + 1):
171+
if k == 0: # edge case: top row
172+
imgtile_num_rows = tile_size[0] // 2
173+
maptile_rows = (0, 0)
174+
elif k == numtiles[0]:
175+
imgtile_num_rows = tile_size[0] // 2
176+
maptile_rows = (numtiles[0] - 1, numtiles[0] - 1)
177+
else:
178+
imgtile_num_rows = tile_size[0]
179+
maptile_rows = (k - 1, k)
180+
181+
imgtile_col = 0
182+
for l in range(numtiles[1] + 1):
183+
# print('tile ({}, {})'.format(k, l))
184+
if l == 0:
185+
imgtile_num_cols = tile_size[1] // 2
186+
maptile_cols = (0, 0)
187+
elif l == numtiles[1]:
188+
imgtile_num_cols = tile_size[1] // 2
189+
maptile_cols = (numtiles[1] - 1, numtiles[1] - 1)
190+
else:
191+
imgtile_num_cols = tile_size[1]
192+
maptile_cols = (l - 1, l)
193+
194+
ul_maptile = tile_mappings[maptile_rows[0]][maptile_cols[0]]
195+
ur_maptile = tile_mappings[maptile_rows[0]][maptile_cols[1]]
196+
bl_maptile = tile_mappings[maptile_rows[1]][maptile_cols[0]]
197+
br_maptile = tile_mappings[maptile_rows[1]][maptile_cols[1]]
198+
199+
norm_factor = imgtile_num_rows * imgtile_num_cols
200+
201+
imgpxl_vals = grayxform(I[imgtile_row:(imgtile_row + imgtile_num_rows), imgtile_col:(imgtile_col + imgtile_num_cols)], aLut)
202+
203+
row_w = np.tile(np.expand_dims(np.arange(imgtile_num_rows), axis=1), [1, imgtile_num_cols])
204+
col_w = np.tile(np.expand_dims(np.arange(imgtile_num_cols), axis=0), [imgtile_num_rows, 1])
205+
row_rev_w = np.tile(np.expand_dims(np.arange(imgtile_num_rows, 0, -1), axis=1), [1, imgtile_num_cols])
206+
col_rev_w = np.tile(np.expand_dims(np.arange(imgtile_num_cols, 0, -1), axis=0), [imgtile_num_rows, 1])
207+
208+
Ic[imgtile_row:(imgtile_row + imgtile_num_rows), imgtile_col:(imgtile_col + imgtile_num_cols)] = (row_rev_w * (col_rev_w * grayxform(imgpxl_vals, ul_maptile) + col_w * grayxform(imgpxl_vals, ur_maptile)) + row_w * (col_rev_w * grayxform(imgpxl_vals, bl_maptile) + col_w * grayxform(imgpxl_vals, br_maptile))) / norm_factor
209+
210+
imgtile_col += imgtile_num_cols
211+
212+
imgtile_row += imgtile_num_rows
213+
return Ic
214+
215+
216+
def grayxform(I, aLut):
217+
"""
218+
map I to aLut
219+
@param I: image
220+
@param aLut: look-up table
221+
"""
222+
max_idx = len(aLut) - 1
223+
val = np.copy(I)
224+
val[val < 0] = 0
225+
val[val > 1] = 1
226+
indexes = np.int32(val * max_idx + 0.5)
227+
return aLut[indexes]

0 commit comments

Comments
 (0)