[OpenCV实战]46 在OpenCV下应用图像强度变换实现图像对比度均衡
本文主要介绍基于图像强度变换算法来实现图像对比度均衡。通过图像对比度均衡能够抑制图像中的无效信息,使图像转换为更符合计算机或人处理分析的形式,以提高图像的视觉价值和使用价值。本文主要通过OpenCV contrib中的intensity_transform模块实现图像对比度均衡。如果想了解具体相关方法原理见冈萨雷斯主编的图像处理经典书籍 数字图像处理Digital Image Processing 第四版第三章。
本文需要OpenCV contrib库,OpenCV contrib库的编译安装见:
本文所有代码见:
文章目录
1 相关知识介绍
1.1 图像强度
图像强度的英文名称是image intensity,意思是单通道图像像素的值大小。在灰度图像中,图像强度是就是图像的灰度级。在RGB颜色空间中,可以理解为RGB三个通道的像素灰度值,即RGB包含三种图像强度。其他颜色空间也是同样的道理。
1.2 图像对比度
对比度是指图像中物体在亮度或颜色上的差异,对比度使图像中一个物体区别于同一视场内的其他物体。对比度越大,图像类各个物体的颜色差别就越大,图像也就越鲜艳。
如下图所示。显然,左图像的对比度较低,因为与右图像相比,很难识别图像中存在的细节。
现实生活中的例子可以是晴天和大雾天。在阳光明媚的日子里,我们觉得一切都很清晰,因此与雾天相比,一切看起来几乎都一样强烈(暗淡、灰暗)。晴天的图像代码对比度高,雾天代表对比度低。
一种更有效的检查图像对比度是低还是高的方法是绘制图像直方图,让我们为上面的图像绘制直方图。如下图所示:
很明显,从左边的图像直方图中,我们可以看到图像强度值位于一个狭窄的范围内。因为很难区分几乎相同的强度值,因此左图像的对比度较低。如果不理解可以看看下面灰度范围图,可以看到灰度变化范围越大,可视化区分度越好。因此,对于高对比度,图像直方图应该跨越整个动态范围。
到目前为止,我们讨论了对比度,但没有讨论低对比度图像的原因。低对比度图像可能是由于照明不足、成像传感器缺乏动态范围,甚至在图像采集过程中镜头光圈设置错误等原因造成的。因此我们需要对低对比度的图像进行图像增强。
1.3 OpenCV中基于图像强度的对比度增强算法
OpenCV contrib中的intensity_transform模块包含于图像强度的对比度增强算法。主要包括的算法有:
- 自适应直方图均衡化 Autoscaling
- 对数变换 Log Transformations
- gamma变换 Power-Law (Gamma) Transformations
- 对比度拉伸 Contrast Stretching
- BIMEF, A Bio-Inspired Multi-Exposure Fusion Framework for Low-light Image enhancement
OpenCV contrib的intensity_transform模块官方代码仓库见:intensity_transform
BIMEF算法,是一个C++实现的原始MATLAB算法。与原始代码相比,此实现速度稍慢,并且无法提供相同的结果。特别是,在一定条件下,对于明亮区域,图像增强的质量会降低,而且OpenCV需要engine库才能运行BIMEF算法,所以本文就不介绍该算法。
关于图像强度的进一步详细介绍见:图像增强综述
2 代码与结果分析
2.1 调用接口说明
本文介绍OpenCV contrib的intensity_transform模块中四种图像强度增强算法。所有图像增加代码都在intensity_transform模块中。本文提供C++和Python版本的实现,不同图像强度增强算法调用接口如下:
C++
// Apply intensity transformations
// 应用强度转换
Mat imgAutoscaled, imgLog;
// autoscaling
autoscaling(g_image, imgAutoscaled);
// gamma变换
gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0f);
// 对数变换
logTransform(g_image, imgLog);
// 对比度拉伸
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
Python
# Apply intensity transformations
# 应用强度转换
# autoscaling
imgAutoscaled = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.autoscaling(g_image, imgAutoscaled)
# gamma变换
g_imgGamma = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0)
# 对数变换
imgLog = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.logTransform(g_image, imgLog)
# 对比度拉伸
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
不同的方法所需要设定的参数不同,具体如下:
- autoscaling:对输入图像进行自适应缩放以增强对比度,仅需要输入待增强的图像。
- gamma变换:对输入图像进行伽马校正以增强对比度,需要输入待增强图像和参数gamma。
- 对数变换:对输入图像进行对数转换以增强对比度,仅需要输入待增强的图像。
- 对比度拉伸:对输入图像应用线性对比度拉伸以增强对比度,需要输入待增强图像和参数r1,s1,r2,s2,(r1,s1)和(r2,s2)为转换函数第一个点和第二个点的坐标。
此外为了比较不同图像强度增强方法的效果,加入了图像对比度计算方法
图像对比度计算方法为RMS Contrast,来自于How to calculate the contrast of an image?
方法原理很简单,就是将图像变为灰度图,然后计算图像方差。
2.2 完整代码
代码功能很简单,就是获得输入图像,然后对输入图像应用不同的图像增强算法。对于可调参数的,创建滑动条以调整方法的输入参数。但是要注意的是,输入图像必须为三通道RGB图像。C++和Python代码如下:
C++
#include <opencv2/opencv.hpp>
#include <opencv2/intensity_transform.hpp>
#include <iostream>
using namespace std;
using namespace cv;
using namespace cv::intensity_transform;
// 计算对比度
double rmsContrast(Mat srcImg)
{
Mat dstImg, dstImg_mean, dstImg_std;
// 灰度化
cvtColor(srcImg, dstImg, COLOR_BGR2GRAY);
// 计算图像均值和方差
meanStdDev(dstImg, dstImg_mean, dstImg_std);
// 获得图像对比度
double contrast = dstImg_std.at<double>(0, 0);
return contrast;
}
// 设置命名空间避免污染用户变量
namespace
{
// global variables
Mat g_image;
// gamma变换变量
int g_gamma = 40;
const int g_gammaMax = 500;
Mat g_imgGamma;
const std::string g_gammaWinName = "Gamma Correction";
// 对比度拉伸
Mat g_contrastStretch;
int g_r1 = 70;
int g_s1 = 15;
int g_r2 = 120;
int g_s2 = 240;
const std::string g_contrastWinName = "Contrast Stretching";
// 创建gamma变换滑动条
static void onTrackbarGamma(int, void*)
{
float gamma = g_gamma / 100.0f;
gammaCorrection(g_image, g_imgGamma, gamma);
imshow(g_gammaWinName, g_imgGamma);
cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;
}
// 创建对数变换滑动条
static void onTrackbarContrastR1(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("Contrast Stretching", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastS1(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("Contrast Stretching", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastR2(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("Contrast Stretching", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastS2(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("Contrast Stretching", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
}
int main()
{
// 图像路径
const std::string inputFilename = "./image/tree.jpg";
// Read input image
// 读图
g_image = imread(inputFilename);
if (g_image.empty())
{
printf("image is empty");
return 0;
}
// Create trackbars
// 创建滑动条
namedWindow(g_gammaWinName);
// 创建gamma变换筛选方法
createTrackbar("Gamma value", g_gammaWinName, &g_gamma, g_gammaMax, onTrackbarGamma);
// 对比度拉伸 Contrast Stretching
namedWindow(g_contrastWinName);
createTrackbar("Contrast R1", g_contrastWinName, &g_r1, 256, onTrackbarContrastR1);
createTrackbar("Contrast S1", g_contrastWinName, &g_s1, 256, onTrackbarContrastS1);
createTrackbar("Contrast R2", g_contrastWinName, &g_r2, 256, onTrackbarContrastR2);
createTrackbar("Contrast S2", g_contrastWinName, &g_s2, 256, onTrackbarContrastS2);
// Apply intensity transformations
// 应用强度转换
Mat imgAutoscaled, imgLog;
// autoscaling
autoscaling(g_image, imgAutoscaled);
// gamma变换
gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0f);
// 对数变换
logTransform(g_image, imgLog);
// 对比度拉伸
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
// Display intensity transformation results
// 展示结果
imshow("Original Image", g_image);
cout << "Original Image: " << rmsContrast(g_image) << endl;
imshow("Autoscale", imgAutoscaled);
cout << "Autoscale: " << rmsContrast(imgAutoscaled) << endl;
imshow(g_gammaWinName, g_imgGamma);
cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;
imshow("Log Transformation", imgLog);
cout << "Log Transformation: " << rmsContrast(imgLog) << endl;
imshow(g_contrastWinName, g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
waitKey(0);
return 0;
}
Python
# -*- coding: utf-8 -*-
"""
Created on Thu Sep 10 18:48:56 2020
@author: luohenyueji
"""
import cv2
import numpy as np
# ----- 全局变量
# 输入图片
g_image = np.zeros((3, 3, 3), np.uint8)
# gamma变换变量
g_gamma = 40
g_gammaMax = 500
g_gammaWinName = "Gamma Correction"
# 对比度拉伸
g_r1 = 70
g_s1 = 15
g_r2 = 120
g_s2 = 240
g_contrastWinName = "Contrast Stretching"
# 创建gamma变换滑动条
def onTrackbarGamma(x):
g_gamma = x
gamma = g_gamma / 100.0
g_imgGamma = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.gammaCorrection(g_image, g_imgGamma, gamma)
cv2.imshow(g_gammaWinName, g_imgGamma);
print(g_gammaWinName + ": " + str(rmsContrast(g_imgGamma)))
# 创建对数变换滑动条
def onTrackbarContrastR1(x):
g_r1 = x
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
cv2.imshow("Contrast Stretching", g_contrastStretch)
print(g_contrastWinName + ": " + str(rmsContrast(g_contrastStretch)))
def onTrackbarContrastS1(x):
g_s1 = x
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
cv2.imshow("Contrast Stretching", g_contrastStretch)
print(g_contrastWinName + ": " + str(rmsContrast(g_contrastStretch)))
def onTrackbarContrastR2(x):
g_r2 = x
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
cv2.imshow("Contrast Stretching", g_contrastStretch)
print(g_contrastWinName + ": " + str(rmsContrast(g_contrastStretch)))
def onTrackbarContrastS2(x):
g_s2 = x
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
cv2.imshow("Contrast Stretching", g_contrastStretch)
print(g_contrastWinName + ": " + str(rmsContrast(g_contrastStretch)))
# 计算对比度
def rmsContrast(scrImg):
dstImg = cv2.cvtColor(scrImg, cv2.COLOR_BGR2GRAY)
contrast = dstImg.std()
return contrast
def main():
# 图像路径
inputFilename = "./image/car.png"
# 读图
global g_image
g_image = cv2.imread(inputFilename)
if g_image is None:
print("image is empty")
return
# 创建滑动条
cv2.namedWindow(g_gammaWinName)
# 创建gamma变换筛选方法
cv2.createTrackbar("Gamma value", g_gammaWinName, g_gamma, g_gammaMax, onTrackbarGamma)
# 对比度拉伸 Contrast Stretching
cv2.namedWindow(g_contrastWinName)
cv2.createTrackbar("Contrast R1", g_contrastWinName, g_r1, 256, onTrackbarContrastR1)
cv2.createTrackbar("Contrast S1", g_contrastWinName, g_s1, 256, onTrackbarContrastS1)
cv2.createTrackbar("Contrast R2", g_contrastWinName, g_r2, 256, onTrackbarContrastR2)
cv2.createTrackbar("Contrast S2", g_contrastWinName, g_s2, 256, onTrackbarContrastS2)
# Apply intensity transformations
# 应用强度转换
# autoscaling
imgAutoscaled = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.autoscaling(g_image, imgAutoscaled)
# gamma变换
g_imgGamma = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0)
# 对数变换
imgLog = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.logTransform(g_image, imgLog)
# 对比度拉伸
g_contrastStretch = np.zeros(g_image.shape, np.uint8)
cv2.intensity_transform.contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2)
# 展示结果
cv2.imshow("Original Image", g_image);
print("Original Image: " + str(rmsContrast(g_image)))
cv2.imshow("Autoscale", imgAutoscaled)
print("Autoscale: " + str(rmsContrast(imgAutoscaled)))
cv2.imshow(g_gammaWinName, g_imgGamma)
print(g_gammaWinName + ": " + str(rmsContrast(g_imgGamma)))
cv2.imshow("Log Transformation", imgLog)
print("Log Transformation: " + str(rmsContrast(imgLog)))
cv2.imshow(g_contrastWinName, g_contrastStretch)
print(g_contrastWinName + ": " + str(rmsContrast(g_contrastStretch)))
cv2.waitKey(0)
if __name__ == '__main__':
main()
2.3 测试与结果评价
2.3.1 测试结果
测试图片部分来自于intensity_transformations。本文分别对四种不同场景进行了测试,其中Gamma Correction和Contrast Stretching是手动调整参数后个人觉得最好结果。具体结果如下:
场景1 car
类型 | 结果 |
---|---|
原图 | |
Autoscaling | |
Gamma Correction | |
Contrast Stretching | |
Log Transformations |
场景2 tree
类型 | 结果 |
---|---|
原图 | |
Autoscaling | |
Gamma Correction | |
Contrast Stretching | |
Log Transformations |
场景3 xray
类型 | 结果 |
---|---|
原图 | |
Autoscaling | |
Gamma Correction | |
Contrast Stretching | |
Log Transformations |
场景4 indicator
类型 | 结果 |
---|---|
原图 | |
Autoscaling | |
Gamma Correction | |
Contrast Stretching | |
Log Transformations |
2.3.2 结果评价
总结不同算法在四个场景表现如下:
- Autoscaling:Autoscaling适用于原始图像本身比较模糊的场景,如果原始图像不模糊则没什么太大改进,但各个环境下总体效果不错。
- Gamma Correction:Gamma Correction所需要调整的参数仅有一个,在各种场景下稍微调整参数就能获得不错的结果。
- Contrast Stretching:Contrast Stretching在各个场景都能获得特别好的效果,但是需要调整的参数太多。
- Log Transformations:Log Transformations仅仅适用于亮度极低的场景,其他场景增强后对比度反而更差。
总结来说,如果对比度影响不那么大或者需要自动化,autoscaling足以对付绝大部分场景,事实上autoscaling用的也算最多的方式。如果对图像对比度要求特别高,通常都是自动参数寻优+Contrast Stretching+图像对比度结果评价来应用,通过设定不同的参数,然后使用Contrast Stretching对图像进行处理,最后筛选图像对比度最高的一次作为最后结果,但是这种方式可能需要一定处理时间,不过确实是一个很不错的解决方案。在实际场景,结合autoscaling和Contrast Stretching自动寻找参,找对比度最好结果即可。
3 参考
3.1 参考代码
3.2 参考文章
- Histogram Equalization
- What is Contrast in Image Processing?
- 图像增强综述
- How to calculate the contrast of an image?
- intensity_transformations
[OpenCV实战]46 在OpenCV下应用图像强度变换实现图像对比度均衡的更多相关文章
- [OpenCV实战]45 基于OpenCV实现图像哈希算法
目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash).图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅 ...
- [OpenCV实战]19 使用OpenCV实现基于特征的图像对齐
目录 1 背景 1.1 什么是图像对齐或图像对准? 1.2 图像对齐的应用 1.3 图像对齐基础理论 1.4 如何找到对应点 2 OpenCV的图像对齐 2.1 基于特征的图像对齐的步骤 2.2 代码 ...
- [OpenCV实战]51 基于OpenCV实现图像极坐标变换与逆变换
在图像处理领域中,经常通过极坐标与笛卡尔直角坐标的互转来实现图像中圆形转为方形,或者通过极坐标反变换实现方形转圆形.例如钟表的表盘,人眼虹膜,医学血管断层都需要用到极坐标变换来实现圆转方. 文章目录 ...
- [OpenCV实战]44 使用OpenCV进行图像超分放大
图像超分辨率(Image Super Resolution)是指从低分辨率图像或图像序列得到高分辨率图像.图像超分辨率是计算机视觉领域中一个非常重要的研究问题,广泛应用于医学图像分析.生物识别.视频监 ...
- [OpenCV实战]34 使用OpenCV进行图像修复
目录 1 什么是图像修复 1.1 INPAINT_NS : Navier-Stokes based Inpainting 1.2 INPAINT_TELEA : Fast Marching Metho ...
- [OpenCV实战]48 基于OpenCV实现图像质量评价
本文主要介绍基于OpenCV contrib中的quality模块实现图像质量评价.图像质量评估Image Quality Analysis简称IQA,主要通过数学度量方法来评价图像质量的好坏. 本文 ...
- [OpenCV实战]52 在OpenCV中使用颜色直方图
颜色直方图是一种常见的图像特征,顾名思义颜色直方图就是用来反映图像颜色组成分布的直方图.颜色直方图的横轴表示像素值或像素值范围,纵轴表示该像素值范围内像素点的个数或出现频率.颜色直方图属于计算机视觉中 ...
- [OpenCV实战]50 用OpenCV制作低成本立体相机
本文主要讲述利用OpenCV制作低成本立体相机以及如何使用OpenCV创建3D视频,准确来说是模仿双目立体相机,我们通常说立体相机一般是指双目立体相机,就是带两个摄像头的那种(目就是指眼睛,双目就是两 ...
- [OpenCV实战]31 使用OpenCV将一个三角形仿射变换到另一个三角形
目录 1 什么是仿射变换? 2 使用OpenCV进行三角形仿射变换 2.1 定义输入和输出 2.2 计算边界框 2.3 裁剪图像和更改坐标 2.4 计算仿射变换矩形 2.5 应用仿射变换到三角形 2. ...
随机推荐
- mysql工具的使用、增删改查
mysql工具使用 目录 mysql工具使用 mysql的程序组成 mysql工具使用 服务器监听的两种socket地址 mysql数据库操作 DDL操作 数据库操作 表操作 用户操作 查看命令SHO ...
- 齐博x1到底是怎么的存在?
齐博X1是齐博软件基于thinkphp5开发的内容管理系统,拓展性非常强,后台一键升级,后台提供丰富的频道模块云市插件市场.风格市场.钩子市场,所有都是一键在线安装. 系统已经对接好QQ.微信登录,同 ...
- python环境安装(pyhon和pycharm)
一.python安装 在地址栏输入https://www.python.org/进入python官网, 点击windows后会出现各种可供下载的历史版本, 安装包下载后,双击运行 点击下一步 勾选下面 ...
- NLP之基于logistic回归的文本分类
数据集下载: 链接:https://pan.baidu.com/s/17EL37CQ-FtOXhtdZHQDPgw 提取码:0829 逻辑斯蒂回归 @ 目录 逻辑斯蒂回归 1.理论 1.1 多分类 1 ...
- 1.httprunner3入门
一.httprunner3 httprunner是一款面向HTTP(S)协议的通用开源测试框架 支持三种格式的用例:YAML/JSON/Pytest,httprunner3以前不支持pytest用例 ...
- AI带你省钱旅游!精准预测民宿房源价格!
作者:韩信子@ShowMeAI 数据分析实战系列:https://www.showmeai.tech/tutorials/40 机器学习实战系列:https://www.showmeai.tech/t ...
- CSS line-break属性与中文标点换行
关于标点符号把文字带着换行的问题解决方案 出现的问题 最近在弄一个介绍页面的时候遇到一个很巧的问题,在文本换行的时候刚好能够放下文字,但是标点符号把这个文字带着换行了 如下图所示: 如果的在上一行显示 ...
- Angular SSR 探究
一般来说,普通的 Angular 应用是在 浏览器 中运行,在 DOM 中对页面进行渲染,并与用户进行交互.而 Angular Universal 是在 服务端 进行渲染(Server-Side Re ...
- Python学习之实例1
一.求n个数字的平均值 n=3 #定义常量n=3 sum=0 #定义求和变量sum count=0 #定义变量count,记录输入数字的次数 print("请输入3个数字:") # ...
- mysql是如何实现mvcc的
mvcc的概念 mvcc即多版本并发控制,是一种并发控制的策略,能让数据库在高并发下做到安全高效的读写,提升数据库的并发性能; 是一种用来解决并发下读写冲突的无锁解决方案,为事务分配单向增长时间戳,为 ...