文章目录

  • 0 前言
  • 1 算法细节
    • 1.1 Naive
      • 1.1.1 主要思想
      • 1.1.2 权重计算
      • 1.1.3 融合
    • 1.2 Multi-resolution
  • 2 实验
  • 3 参考

0 前言

在曝光融合(Exposure Fusion)算法问世之前,多曝光序列合成用于显示的HDR需要两个步骤,第一步是将多张不同曝光的低动态范围图像合成为HDR(例如Debevec提出的加权融合方法),通常HDR为12bit或者16bit;第二步是通过tonemapping对高动态范围HDR进行压缩以支持低动态范围显示设备(例如Durand提出的基于双边滤波的tonemapping算法),一般会压缩至8bit。

曝光融合算法的优势在于不需要标定相机响应曲线,并且跳过tonemapping步骤,直接合成用于显示的高动态范围图像。

1 算法细节

1.1 Naive

1.1.1 主要思想

对于多曝光图像序列,取每一张图像中最有价值的部分用于合成。例如,曝光时间长的图像中暗区细节丰富同时噪声水平低,那么暗区就是有价值的部分。显然,需要一个指标来衡量每张图像中哪些像素有价值,然后通过计算每张图每个像素的价值指标当作对应的权重,最终通过加权融合的方式得到HDR。

1.1.2 权重计算

从对比度、饱和度和亮度三个维度对像素的价值进行评估:

  • 对比度
    这里的对比度指的是图像的梯度,对于边缘和纹理等重要的信息分配很大的权重。具体地,对图像的灰度图执行拉普拉斯滤波,结果取绝对值作为对比度指标 C(Ik)C(I_k)C(Ik​)。
    C(Ik)=∣△gray(Ik)∣C(I_k)=|\triangle_{gray}(I_k)|C(Ik​)=∣△gray​(Ik​)∣
  • 饱和度
    RGB三通道之间差异大的可视为饱和度高的区域,反之,对于过曝或者欠曝区域RGB三通道的值趋于一致,饱和度较低。因此,可将RGB三个通道之间的标准差作为饱和度指标。
    Sk,i,j=(Ik,i,jB−Mk,i,j)2+(Ik,i,jG−Mk,i,j)2+(Ik,i,jR−Mk,i,j)23S_{k,i,j}=\sqrt{\frac{(I_{k,i,j}^{B}-M_{k,i,j})^2+(I_{k,i,j}^{G}-M_{k,i,j})^2+(I_{k,i,j}^{R}-M_{k,i,j})^2}{3}}Sk,i,j​=3(Ik,i,jB​−Mk,i,j​)2+(Ik,i,jG​−Mk,i,j​)2+(Ik,i,jR​−Mk,i,j​)2​​Mk,i,j=Ik,i,jB+Ik,i,jG+Ik,i,jR3M_{k,i,j}=\frac{I_{k,i,j}^{B}+I_{k,i,j}^{G}+I_{k,i,j}^{R}}{3}Mk,i,j​=3Ik,i,jB​+Ik,i,jG​+Ik,i,jR​​
  • 亮度
    对于归一化至0~1范围的图像,将取值在0.5左右的像素视为曝光良好,应该分配很大的权重;接近0和1的分别为欠曝和过曝应该分配很小的权重。像素值与其对应权重的关系符合均值为0.5的高斯分布:
    Ek.i,j=e−(Ik,i,jB−0.5)22σ2⋅e−(Ik,i,jG−0.5)22σ2⋅e−(Ik,i,jR−0.5)22σ2E_{k.i,j}=e^{-\frac{(I_{k,i,j}^{B}-0.5)^2}{2\sigma^2}} \cdot e^{-\frac{(I_{k,i,j}^{G}-0.5)^2}{2\sigma^2}} \cdot e^{-\frac{(I_{k,i,j}^{R}-0.5)^2}{2\sigma^2}}Ek.i,j​=e−2σ2(Ik,i,jB​−0.5)2​⋅e−2σ2(Ik,i,jG​−0.5)2​⋅e−2σ2(Ik,i,jR​−0.5)2​

获取以上3个指标后,就能计算每个像素对应的权重:
Wi,j,k=(Ci,j,k)wc⋅(Si,j,k)ws⋅(Ei,j,k)weW_{i,j,k}=(C_{i,j,k})^{w_c}\cdot (S_{i,j,k})^{w_s}\cdot (E_{i,j,k})^{w_e}Wi,j,k​=(Ci,j,k​)wc​⋅(Si,j,k​)ws​⋅(Ei,j,k​)we​默认wc=ws=we=1w_c=w_s=w_e=1wc​=ws​=we​=1;另外,为了保证多张图像在同一位置的权重和为1,需要在图像数量维度上对权重进行归一化:
W^ij,k=Wij,k∑k′=1NWij,k′\hat{W}_{{ij,k}}=\frac{{W}_{{ij,k}}}{\sum_{k^{\prime}=1}^{N}{W}_{{ij,k^{\prime}}}}W^ij,k​=∑k′=1N​Wij,k′​Wij,k​​

1.1.3 融合

根据计算的权重对原始图像进行加权求和,即可得到融合后的图像:
Hij=∑k=1NW^ij,k⋅Iij,kH_{ij}=\sum_{k=1}^{N}\hat{W}_{{ij,k}}\cdot I_{ij,k}Hij​=k=1∑N​W^ij,k​⋅Iij,k​

这样粗糙融合的结果存在一个问题,在权重尖锐过渡的区域,由于每张图像的曝光时间不同,绝对强度也不同,导致融合后灰度跳变太大,图像中呈现很多黑色和白色斑点,与噪声形态类似。

权重尖锐过渡区会出现问题,那么可以让其平滑一点,提到平滑自然而然就想到了高斯滤波。作者对权重图做高斯滤波后再进行合成,虽然斑点问题得到了缓解,但是在边缘处会出现光晕现象

既然光晕是由于边缘处的权重被平滑所导致的,可以考虑使用保边滤波代替高斯滤波。

1.2 Multi-resolution

由于naive版本的融合方式不能完全解决黑白斑点的问题,并且会引入光晕这样的新问题。因此,作者提出了使用拉普拉斯金字塔融合的方式,其流程如下图所示:

简单来说,就是从不同曝光的原始图像中分解出拉普拉斯金字塔,对应的权重图中分解出高斯金字塔,然后分别在每个尺度下进行融合,得到融合后的拉普拉斯金字塔。最后,从拉普拉斯金字塔的顶层开始向上采样,叠加同尺度的拉普拉斯细节,再向上采样和叠加细节,递归至最高分辨率,得到最终的结果。(此处有一个点需要注意,拉普拉斯金字塔的顶层就是原始图像高斯金字塔的顶层)

  • 为什么拉普拉斯金字塔融合效果好?
    将平坦区和尖锐过渡区(如边缘)分开融合,平坦区融合使用的是经过多次高斯滤波和下采样后的权重图,仅在比较大的边缘纹理处变化剧烈;由于拉普拉斯金字塔中只保留了边缘等高频信息,因此在拉普拉斯金字塔上对尖锐过渡区进行融合不会影响平坦区。

2 实验

import os
import sys
import glob
import numpy as np
import cv2
import argparsedef show_image(message, src):cv2.namedWindow(message, 0)cv2.imshow(message, src)cv2.waitKey(0)cv2.destroyAllWindows()def gauss_curve(src, mean, sigma):dst = np.exp(-(src - mean)**2 / (2 * sigma**2))return dstclass ExposureFusion(object):def __init__(self, sequence, best_exposedness=0.5, sigma=0.2, eps=1e-12, exponents=(1.0, 1.0, 1.0), layers=7):self.sequence = sequence  # [N, H, W, 3], (0..1), float32self.img_num = sequence.shape[0]self.best_exposedness = best_exposednessself.sigma = sigmaself.eps = epsself.exponents = exponentsself.layers = layers@staticmethoddef cal_contrast(src):gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)laplace_kernel = np.array([[0, 1, 0], [1, -4, 1], [0, 1, 0]], dtype=np.float32)contrast = cv2.filter2D(gray, -1, laplace_kernel, borderType=cv2.BORDER_REPLICATE)return np.abs(contrast)@staticmethoddef cal_saturation(src):mean = np.mean(src, axis=-1)channels = [(src[:, :, c] - mean)**2 for c in range(3)]saturation = np.sqrt(np.mean(channels, axis=0))return saturation@staticmethoddef cal_exposedness(src, best_exposedness, sigma):exposedness = [gauss_curve(src[:, :, c], best_exposedness, sigma) for c in range(3)]exposedness = np.prod(exposedness, axis=0)return exposednessdef cal_weight_map(self):weights = []for idx in range(self.sequence.shape[0]):contrast = self.cal_contrast(self.sequence[idx])saturation = self.cal_saturation(self.sequence[idx])exposedness = self.cal_exposedness(self.sequence[idx], self.best_exposedness, self.sigma)weight = np.power(contrast, self.exponents[0]) * np.power(saturation, self.exponents[1]) * np.power(exposedness, self.exponents[2])# Gauss Blur# weight = cv2.GaussianBlur(weight, (21, 21), 2.1)weights.append(weight)weights = np.stack(weights, 0) + self.eps# normalizeweights = weights / np.expand_dims(np.sum(weights, axis=0), axis=0)return weightsdef naive_fusion(self):weights = self.cal_weight_map()  # [N, H, W]weights = np.stack([weights, weights, weights], axis=-1)  # [N, H, W, 3]naive_fusion = np.sum(weights * self.sequence * 255, axis=0)naive_fusion = np.clip(naive_fusion, 0, 255).astype(np.uint8)return naive_fusiondef build_gaussian_pyramid(self, high_res):gaussian_pyramid = [high_res]for idx in range(1, self.layers):gaussian_pyramid.append(cv2.GaussianBlur(gaussian_pyramid[-1], (5, 5), 0.83)[::2, ::2])return gaussian_pyramiddef build_laplace_pyramid(self, gaussian_pyramid):laplace_pyramid = [gaussian_pyramid[-1]]for idx in range(1, self.layers):size = (gaussian_pyramid[self.layers - idx - 1].shape[1], gaussian_pyramid[self.layers - idx - 1].shape[0])upsampled = cv2.resize(gaussian_pyramid[self.layers - idx], size, interpolation=cv2.INTER_LINEAR)laplace_pyramid.append(gaussian_pyramid[self.layers - idx - 1] - upsampled)laplace_pyramid.reverse()return laplace_pyramiddef multi_resolution_fusion(self):weights = self.cal_weight_map()  # [N, H, W]weights = np.stack([weights, weights, weights], axis=-1)  # [N, H, W, 3]image_gaussian_pyramid = [self.build_gaussian_pyramid(self.sequence[i] * 255) for i in range(self.img_num)]image_laplace_pyramid = [self.build_laplace_pyramid(image_gaussian_pyramid[i]) for i in range(self.img_num)]weights_gaussian_pyramid = [self.build_gaussian_pyramid(weights[i]) for i in range(self.img_num)]fused_laplace_pyramid = [np.sum([image_laplace_pyramid[n][l] *weights_gaussian_pyramid[n][l] for n in range(self.img_num)], axis=0) for l in range(self.layers)]result = fused_laplace_pyramid[-1]for k in range(1, self.layers):size = (fused_laplace_pyramid[self.layers - k - 1].shape[1], fused_laplace_pyramid[self.layers - k - 1].shape[0])upsampled = cv2.resize(result, size, interpolation=cv2.INTER_LINEAR)result = upsampled + fused_laplace_pyramid[self.layers - k - 1]result = np.clip(result, 0, 255).astype(np.uint8)return resultif __name__ == '__main__':root_path = sys.argv[1]sequence_path = [os.path.join(root_path, fname) for fname in os.listdir(root_path)]sequence = np.stack([cv2.imread(path) for path in sequence_path], axis=0)mef = ExposureFusion(sequence.astype(np.float32) / 255.0)naive_fusion_result = mef.naive_fusion()multi_res_fusion = mef.multi_resolution_fusion()show_image('muti-resolution', multi_res_fusion)

3 参考

Mertens T, Kautz J, Van Reeth F. Exposure fusion[C]//15th Pacific Conference on Computer Graphics and Applications (PG’07). IEEE, 2007: 382-390.
https://zhuanlan.zhihu.com/p/455674916

【HDR】曝光融合(Exposure Fusion)相关推荐

  1. 曝光融合Exposure Fusion 与ghost

    1.概述 说到EF那我们不得不谈谈HDR.高动态范围(HDR)图像可以表示动态范围跨度很大的真实场景.图像的动态范围(dynamic range)是指一幅图像中可见区域最大亮度与最小亮度的比值.同样的 ...

  2. 3.5.4 Exposure Fusion(曝光融合)—Mertens TMO

    3.5.4 Exposure Fusion(曝光融合) HDR图像通常由一系列LDR图像组成,这些图像最终可以进行色调映射. Mertens等人提出了一种可以避免色调映射步骤的新颖方法. . 这是受到 ...

  3. opencv曝光过度_使用 OpenCV 进行曝光融合(Exposure Fusion)成像

    在本教程中,我们将学习使用OpenCV的Exposure Fusion.我们将使用C ++和Python共享代码. 什么是曝光融合?曝光融合是一种将使用不同曝光设置拍摄的图像组合成一个看起来像色调映射 ...

  4. 图像融合:Exposure Fusion

    Exposure Fusion 文章目录 Exposure Fusion Quality Measures Fusion 本篇文章提出了一种将多曝光序列融合成一帧包含更多细节.内容,更高质量的图像,是 ...

  5. HDR多帧曝光融合Python代码实现

    HDR算法Python实现:Github.先看结果: 使用三脚架用自己手机分别设置-3.0.1 EV拍摄的输入图像: 输出结果(左:0ev,右:HDR结果): main.py: from image_ ...

  6. ISP-长短曝光融合生成HDR图像

    1.高动态范围图像相关 图像的动态范围是指一幅图像中量化的最大亮度与最小噪声的比值.高动态范围HDR(high dynamic range)图像,能够完整表示真实场景中跨度很大的动态范围.采用普通CM ...

  7. [OpenCV实战]24 使用OpenCV进行曝光融合

    目录 1 什么是曝光融合 2 曝光融合的原理 3 代码与结果 4 参考 本教程中,我们将了解使用OpenCV的Exposure Fusion(曝光融合). 1 什么是曝光融合 曝光融合是一种将使用不同 ...

  8. 论文笔记:DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs

    论文笔记:DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs co ...

  9. DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs 阅读笔记

    DeepFuse: A Deep Unsupervised Approach for Exposure Fusion with Extreme Exposure Image Pairs 阅读笔记 引言 ...

最新文章

  1. 关于计算机网络传输介质 下列叙述正确的是,《计算机基础》习题1-7
  2. 2017甲骨文JavaOne参会感想
  3. 大牛书单 | 腾讯运维大咖陪你过724
  4. .net中如何发送HTTP请求网络资源
  5. 5 kvm虚拟磁盘扩容
  6. 语音识别中的CTC算法的基本原理解释
  7. 跟我一起写 Makefile ---转
  8. CPU虚拟化技术解析
  9. 物联网通信技术课程安排
  10. js基础之六种继承方式
  11. Tuna Scope 金枪鱼猎手:当顶级吃货,搞起图像识别
  12. 什么是搜索引擎???搜索引擎的介绍
  13. markdown流程图语法
  14. 微信小程序搜索关键词高亮效果(转)
  15. 谷歌Cartographer的论文研读(一)
  16. 全国计算机等级分为几级,全国计算机等级考试2级分几类?
  17. 授予数据库账号dba权限_oracle数据库限制dba权限
  18. pythons_pythons是什么意思_pythons怎么读_pythons翻译_用法_发音_词组_同反义词_巨蛇_大蟒( python的名词复数 )-新东方在线英语词典...
  19. iOS-无痕埋点设计
  20. 对领导集体提出意见和建议

热门文章

  1. 20180529-C · Comic book characters · ggplot2 geom_bar 柱状图 条形图 facet_wrap 分面 · R 语言数据可视化 案例 源码
  2. 【图文详细】用HBuilder X写PHP并且能够在浏览器运行打开
  3. Javascript实现AES算法
  4. Linux极速上手,超全面总结
  5. vue的组件化+父子组件的通信
  6. 快手视频伪原创工具 视频怎么修改m5d
  7. JAVA-调用第三方接口获取数据的例子
  8. javaSE之static关键字之共享数据
  9. 改造你的网站,变身 PWA
  10. JUC并发编程小总结