基于直方图调整曝光(亮度/对比度)使用Python
我正在尝试用Python制作一个带图形界面的程序(很可能会用到Kivy),目的是让两张图片的曝光度匹配。我想把这两张图片(可以是彩色的RGB格式或灰度图)并排显示,并且显示它们各自的直方图,还想加一个滑块来控制选中图片的曝光度。我希望能得到一些建议,看看该怎么做。
到目前为止,我已经读了好几篇帖子,发现有几种方法可以计算图片的直方图(比如用numpy、matplotlib、openCV和PIL),不过我对哪种方法最好(也就是安装的库和依赖最少)感到有些困惑。我还读到了一些关于改变图片曝光度的内容,有些人提到要调整亮度和对比度,所以说要改变曝光度就需要同时调整这两个参数吗?我知道openCV有equalizeHist这个功能,但它是自动处理的,而我希望两张图片的整体曝光度尽可能接近;所以我在考虑手动调整。如果能自动处理那当然很好,但我还在想怎么实现。
我知道你们非常重视自己的时间,所以如果你们没有时间深入回答这个问题,我也能理解。
2 个回答
让我们先来定义一下你所使用的曝光参数。你可以对明亮和黑暗的场景进行1秒的曝光,这样得到的图像会完全不同。或者,你可能希望这些图像的对比度相似,但这并不一定意味着它们的曝光是相同的。
既然你提到了直方图均衡化,你可以将两幅图像的直方图进行比较,使它们看起来对比度相等。直方图均衡化会让每幅图像的直方图看起来比较平坦,这样所有的亮度值都有相同的可能性(因此可以优化分布以最大化对比度——但并不总是让人觉得好看)。你可能想为每幅图像单独创建自己的非平坦直方图,并对其进行调整,直到图像看起来符合你的要求。
下面是一个伪代码的步骤:
- 计算每幅图像的直方图:H[i]
- 将直方图相加,得到累积直方图:C[i] = H[i]+C[i-1],每幅图像单独处理;
- 对每个直方图进行归一化:C[i]/=sum(C(i))
- 在传统的直方图均衡化中,你会重新映射像素的亮度值,使它们的可能性相等:i2 = 255*C[i1];而在你的情况下,你只是希望它们遵循你自己的直方图配置(这样你可以用滑块调整每幅图像的亮度分布,然后进行视觉比较);例如,可以尝试这样的映射:i2 = i1*k + 255*C[i1]*(1-k),其中滑块会改变k的值,从0.0到1.0。当k=1时,你得到的是原始图像,当k=0时,你得到的是完全的直方图均衡化。下面是我用来对Kinect深度图进行颜色映射的直方图均衡化函数,它将10000个级别映射到255个灰度级别。你需要根据自己的情况修改和调整这个函数。
// histogram equalization float histEq[10000]; void histEqualize(unsigned short* src, unsigned short* dst, int sz) { if (sz==0) return; memset(histEq, 0, DEPTH_LEVELS*sizeof(float)); // 1. depth histogram for (int i=0; i<sz; ++i) { unsigned short val = src[i]; if (val!=0 && val < DEPTH_LEVELS) histEq[val]++; } // 2. cumulative histogram for (int i=1; i<DEPTH_LEVELS; ++i) histEq[i] += histEq[i-1]; long max_cumul = histEq[DEPTH_LEVELS-1]; if (max_cumul==0) return; // 3. mapping function for (int i=1; i<DEPTH_LEVELS; ++i) histEq[i] = (255*histEq[i])/max_cumul; // remap src for (int i=0; i<sz; ++i) { unsigned short z = src[i]; if (z>0) dst[i] = (unsigned char)(255-histEq[src[i]]+0.5f); else dst[i] = 0; } } // histEqualization()
我们这里有一个直方图调整的例子,可以查看。
不过听起来你更想了解的是直方图匹配。我这里有一些代码可以用来做这个,可以在这里找到,不过这个代码测试得不太充分。
如果你觉得这段代码有用,欢迎你向scikit-image提交一个请求,我们可以尝试把它整合进这个软件包里。
编辑于2019-04-29:现在直方图匹配已经包含在scikit-image里了。