Skip to main content
2 of 2
deleted 2 characters in body
paw
  • 5.7k
  • 24
  • 31

Exposure fusion is often a powerful alternative to the reconstruction of HDR images if we are only interested in the tone mapped result. HDR reconstructions are often not possible because exposure times are not available or hard to estimate from the given set of images. Many exposure fusion algorithms are also very fast and therefore suitable for real time applications. So I thought it can't hurt to post another such algorithm.

The idea behind the algorithm is to blend the images in a way so that the level of detail in the final image is maximal.

First each image is weighted by three quality measures, well-exposedness, saturation and contrast. The three weight maps are combined and normalised over all images. Three example weights are shown.

images = Image[Import["..\\memorial00" <> ToString@# <> ".png"], "Real"]
    & /@ Range[61, 76];

WellExposedness[img_] := Module[
  {ch = ColorSeparate@img},
  ImageMultiply @@ (ImageApply[Exp[-(# - 0.5)^2/0.08] &, #] & /@ ch)]
SaturationMeasure[img_] := 
 ImageApply[StandardDeviation@# + 10^-10 &, img]
ContrastMeasure[img_] := 
 ImageApply[Abs@# + 10^-10 &, 
  LaplacianFilter[ColorConvert[img, "Grayscale"], 10]]

ImageWeightMap[img_, {a_, b_, c_}] := ImageMultiply @@ {
   ImageApply[#^a &, WellExposedness[img]],
   ImageApply[#^a &, SaturationMeasure[img]],
   ImageApply[#^a &, ContrastMeasure[img]]}

NormalizeWeightmaps[weightmaps_] := Module[
  {weightmapSum = ImageApply[1/# &, ImageAdd @@ weightmaps]},
  ImageMultiply[weightmapSum, #] & /@ weightmaps]

ImageAssemble[{{WellExposedness@images[[1]],
   SaturationMeasure@images[[1]],
   ContrastMeasure@images[[1]]}}]

enter image description here

Simple blending of the images using the generated weight maps can introduce ugly boundaries between the image parts as shown in the following example where only 2 images of the set are blended.

SimpleExposureBlend[images_, {exp_, sat_, con_}] := Module[
  {imgN = Length@images,
   wm = ImageWeightMap[#, {exp, sat, con}] & /@ images,
   wmNorm},
  wmNorm = NormalizeWeightmaps[wm];
  ImageAdd @@ MapThread[ImageMultiply[#1, #2] &, {images, wmNorm}]
  ]

SimpleExposureBlend[images, {1, 1, 1}]

enter image description here

This issue can be avoided by using multi-scale pyramid blending to combine the images.

ImagePyramidDepth = Length[NestWhileList[Ceiling[#/2] &, #, Ceiling[#] != 1 &]] - 1 &;
GaussPyramid[img_, depth_, kernel_: GaussianMatrix[2], re_: "Gaussian"] := 
    Module[{},NestList[ImageResize[ImageConvolve[#, kernel], Scaled[1/2], 
    Resampling -> re] &, img, depth]];
LaplacePyramid[img_, depth_, kernel_: GaussianMatrix[2], re_: "Gaussian"] := 
 Module[{Gpy = GaussPyramid[img, depth, kernel, re]},
    Join[ImageSubtract[#1, 
      ImageResize[#2, Scaled[2], Resampling -> re]] & @@@ 
    Partition[Gpy, 2, 1], {Last@Gpy}]]
CollapseLaplacePyramid[lapPyr_, re_: "Gaussian"] :=
    ImageAdjust@Fold[ImageAdd[#1, 
    ImageResize[#2, ImageDimensions@#1, Resampling -> re]] &, 
    First@lapPyr, Rest@lapPyr]

MultiLevelExposureBlend[images_, {exp_, sat_, con_}] := Module[
  {imgN = Length@images,
   wm = ImageWeightMap[#, {exp, sat, con}] & /@ images,
   pyrDepth = ImagePyramidDepth@Min@ImageDimensions@images[[1]],
   wmNorm, gp, lp, blend},
  wmNorm = NormalizeWeightmaps[wm];

  gp = GaussPyramid[#, pyrDepth] & /@ wmNorm;
  lp = LaplacePyramid[#, pyrDepth] & /@ images;
  blend = ImageAdd @@ # & /@ Transpose[
     ImageMultiply @@@ # & /@ (Transpose[{#1, #2}] & @@@ Transpose[{gp, lp}])];
  CollapseLaplacePyramid@blend
  ]

i1 = MultiLevelExposureBlend[images, {1, 1, 1}];
i2 = ImageAdjust[ColorCombine[{#1, ImageAdjust[#2, {0, 0, 0.5}],
       Nest[Sharpen[#, 1] &, #3, 3]} & @@ ColorSeparate[ColorConvert[i1, "HSB"]], "HSB"]];
ImageAssemble[{i1, i2}]

enter image description here

relevant paper

paw
  • 5.7k
  • 24
  • 31