Skip to content

Commit e672ab2

Browse files
committed
Proper Laplacian and f64 framebuffers
Fixed the pesky bug that led to overexposed outputs on deep pyramid levels, now they are just beautiful outputs! The problem was that the laplacian implementation was simply WRONG and that many of the buffers used during calculations used uint8, which has insufficient precision all this is now fixed.
1 parent bdfa4ca commit e672ab2

File tree

1 file changed

+67
-28
lines changed

1 file changed

+67
-28
lines changed

gui/exposure_fusion.py

+67-28
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,8 @@ def create_image_pyramids(self, images: "list[np.ndarray]",
409409
g_pyramids = []
410410
l_pyramids = []
411411

412+
actual_pyramid_levels = self.pyramid_levels
413+
412414
for image, weight in zip(images, weights):
413415
gaussian_pyramid = []
414416

@@ -418,31 +420,35 @@ def create_image_pyramids(self, images: "list[np.ndarray]",
418420
# Create gaussian pyramid
419421
for i in range(self.pyramid_levels):
420422
if i == 0:
421-
gaussian_pyramid.append(weight)
423+
gaussian_pyramid.append(np.float64(weight/255))
422424
else:
423425
gaussian_pyramid.append(cv2.pyrDown(gaussian_pyramid[-1]))
426+
if gaussian_pyramid[-1].shape[0] < 8 or gaussian_pyramid[-1].shape[1] < 8:
427+
actual_pyramid_levels = i + 1
428+
break
424429

425-
# Display gaussian pyramid
430+
# Create image gaussians
431+
for i in range(actual_pyramid_levels):
426432

427-
# Create gaussian pyramid for image
428-
for i in range(self.pyramid_levels):
429433
if i == 0:
430-
image_gaussians.append(image)
434+
image_gaussians.append(np.float64(image/255))
431435
else:
432436
image_gaussians.append(cv2.pyrDown(image_gaussians[-1]))
433437

434-
# Create laplacian pyramid
435-
for i in range(self.pyramid_levels - 1, -1, -1):
438+
# Create Laplacian Pyramid
439+
for i in range(actual_pyramid_levels):
440+
441+
this_gaussian = image_gaussians[i]
442+
next_gaussian = image_gaussians[i+1] if i < actual_pyramid_levels - \
443+
1 else np.zeros_like(image_gaussians[i])
436444

437-
if i == self.pyramid_levels - 1:
438-
laplacian_pyramid.append(image_gaussians[i])
445+
if i == actual_pyramid_levels - 1:
446+
laplacian_pyramid.append(this_gaussian)
439447
else:
440-
size = (image_gaussians[i].shape[1],
441-
image_gaussians[i].shape[0])
442-
gaussian_expanded = cv2.pyrUp(
443-
image_gaussians[i+1], dstsize=size)
444-
laplacian_pyramid.append(cv2.subtract(
445-
image_gaussians[i], gaussian_expanded))
448+
laplacian_pyramid.append(
449+
cv2.subtract(this_gaussian, cv2.pyrUp(
450+
next_gaussian, dstsize=this_gaussian.shape[:2][::-1]))
451+
)
446452

447453
g_pyramids.append(gaussian_pyramid)
448454
l_pyramids.append(laplacian_pyramid)
@@ -467,23 +473,20 @@ def blend_pyramids(self, gaussian_pyramids: "list[list[np.ndarray]]",
467473
"""
468474
res_laplacian = []
469475

470-
for level in range(self.pyramid_levels):
476+
for level in range(len(gaussian_pyramids[0])):
471477

472-
reverse_level = self.pyramid_levels - (1 + level)
473-
474-
res_plevel = np.zeros(laplacian_pyramids[0][reverse_level].shape,
475-
dtype=np.uint8)
478+
res_plevel = np.zeros(laplacian_pyramids[0][level].shape,
479+
dtype=np.float64)
476480

477481
for img_idx in range(len(gaussian_pyramids)):
478482

479483
gaussian = gaussian_pyramids[img_idx][level]
480-
laplacian = laplacian_pyramids[img_idx][reverse_level]
481-
482-
gaussian = np.float32(gaussian/255)
484+
laplacian = laplacian_pyramids[img_idx][level]
483485

484486
gaussian = np.repeat(gaussian[:, :, np.newaxis], 3, axis=2)
485487
combination = cv2.multiply(
486-
gaussian, laplacian, dtype=cv2.CV_8UC3)
488+
gaussian, laplacian, dtype=cv2.CV_64FC3)
489+
487490
res_plevel = cv2.add(res_plevel, combination)
488491

489492
res_laplacian.append(res_plevel)
@@ -505,14 +508,50 @@ def reconstruct_image(self, laplacian_pyramid: "list[np.ndarray]") -> np.ndarray
505508
The final HDR image
506509
"""
507510

508-
laplacian_pyramid = laplacian_pyramid[::-1]
511+
res = laplacian_pyramid[-1]
509512

510-
res = laplacian_pyramid[0]
513+
for i in range(len(laplacian_pyramid) - 2, 0, -1):
511514

512-
for i in range(1, len(laplacian_pyramid)):
515+
# Display laplacian pyramid
513516
size = (laplacian_pyramid[i].shape[1],
514517
laplacian_pyramid[i].shape[0])
515518
res = cv2.pyrUp(res, dstsize=size)
516-
res = cv2.add(res, laplacian_pyramid[i])
519+
res = cv2.add(res, laplacian_pyramid[i], dtype=cv2.CV_64FC3)
520+
521+
res = cv2.normalize(res, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC3)
517522

518523
return res
524+
525+
526+
"""
527+
Quick test of the ExposureFusion class
528+
"""
529+
if __name__ == "__main__":
530+
531+
images = [
532+
"images\statues\one\HDR_test_scene_1__1.1.1.png",
533+
"images\statues\one\HDR_test_scene_1__1.1.2.png",
534+
"images\statues\one\HDR_test_scene_1__1.1.3.png",
535+
"images\statues\one\HDR_test_scene_1__1.1.4.png",
536+
"images\statues\one\HDR_test_scene_1__1.1.5.png"
537+
]
538+
539+
images = [cv2.imread(image) for image in images]
540+
541+
fuser = ExposureFusion(pyramid_levels=7)
542+
543+
width, height = images[0].shape[:2]
544+
aspect = width / height
545+
width_new = 600
546+
height_new = int(width_new / aspect)
547+
548+
try:
549+
HDR = fuser(images)
550+
cv2.namedWindow("HDR", cv2.WINDOW_NORMAL)
551+
cv2.resizeWindow("HDR", width_new, height_new)
552+
cv2.imshow("HDR", HDR)
553+
cv2.waitKey(0)
554+
except:
555+
print("Error displaying final img.")
556+
finally:
557+
cv2.destroyAllWindows()

0 commit comments

Comments
 (0)