本文主要介绍基于图像强度变换算法来实现图像对比度均衡。通过图像对比度均衡能够抑制图像中的无效信息,使图像转换为更符合计算机或人处理分析的形式,以提高图像的视觉价值和使用价值。本文主要通过OpenCV contrib中的intensity_transform模块实现图像对比度均衡。如果想了解具体相关方法原理见冈萨雷斯主编的图像处理经典书籍 数字图像处理Digital Image Processing 第四版第三章。

本文需要OpenCV contrib库,OpenCV contrib库的编译安装见:

OpenCV_contrib库在windows下编译使用指南

本文所有代码见:

OpenCV-Practical-Exercise

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 参考文章

[OpenCV实战]46 在OpenCV下应用图像强度变换实现图像对比度均衡的更多相关文章

  1. [OpenCV实战]45 基于OpenCV实现图像哈希算法

    目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash).图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅 ...

  2. [OpenCV实战]19 使用OpenCV实现基于特征的图像对齐

    目录 1 背景 1.1 什么是图像对齐或图像对准? 1.2 图像对齐的应用 1.3 图像对齐基础理论 1.4 如何找到对应点 2 OpenCV的图像对齐 2.1 基于特征的图像对齐的步骤 2.2 代码 ...

  3. [OpenCV实战]51 基于OpenCV实现图像极坐标变换与逆变换

    在图像处理领域中,经常通过极坐标与笛卡尔直角坐标的互转来实现图像中圆形转为方形,或者通过极坐标反变换实现方形转圆形.例如钟表的表盘,人眼虹膜,医学血管断层都需要用到极坐标变换来实现圆转方. 文章目录 ...

  4. [OpenCV实战]44 使用OpenCV进行图像超分放大

    图像超分辨率(Image Super Resolution)是指从低分辨率图像或图像序列得到高分辨率图像.图像超分辨率是计算机视觉领域中一个非常重要的研究问题,广泛应用于医学图像分析.生物识别.视频监 ...

  5. [OpenCV实战]34 使用OpenCV进行图像修复

    目录 1 什么是图像修复 1.1 INPAINT_NS : Navier-Stokes based Inpainting 1.2 INPAINT_TELEA : Fast Marching Metho ...

  6. [OpenCV实战]48 基于OpenCV实现图像质量评价

    本文主要介绍基于OpenCV contrib中的quality模块实现图像质量评价.图像质量评估Image Quality Analysis简称IQA,主要通过数学度量方法来评价图像质量的好坏. 本文 ...

  7. [OpenCV实战]52 在OpenCV中使用颜色直方图

    颜色直方图是一种常见的图像特征,顾名思义颜色直方图就是用来反映图像颜色组成分布的直方图.颜色直方图的横轴表示像素值或像素值范围,纵轴表示该像素值范围内像素点的个数或出现频率.颜色直方图属于计算机视觉中 ...

  8. [OpenCV实战]50 用OpenCV制作低成本立体相机

    本文主要讲述利用OpenCV制作低成本立体相机以及如何使用OpenCV创建3D视频,准确来说是模仿双目立体相机,我们通常说立体相机一般是指双目立体相机,就是带两个摄像头的那种(目就是指眼睛,双目就是两 ...

  9. [OpenCV实战]31 使用OpenCV将一个三角形仿射变换到另一个三角形

    目录 1 什么是仿射变换? 2 使用OpenCV进行三角形仿射变换 2.1 定义输入和输出 2.2 计算边界框 2.3 裁剪图像和更改坐标 2.4 计算仿射变换矩形 2.5 应用仿射变换到三角形 2. ...

随机推荐

  1. 强国杯东杯分区赛miscwp

    ​ 目录 不要被迷惑 PCAP文件分析 平正开 不要被迷惑 ​编辑 导出http ​编辑 得到flag.zip后直接爆破密码 ​编辑 得到​编辑 然后一键解码 ​编辑 flag{WImuJeqSNPh ...

  2. 齐博x1 直播神器聊天小插件

    下载地址如下:https://down.php168.com/livemsg.rar 本插件由论坛网友笨熊提供 非常感谢他给大家提供那么一个非常好用的直播必备神器. 如下图所示,大家在直播的时候,这个 ...

  3. 使用 nvm 对 node 进行版本管理

    前端项目工程化,基本都依赖于 nodejs, 不同的项目对于 nodejs 的版本会有要求,nvm 就是可以让我们在各个版本之间进行快速切换的工具. Linux 系统 下载解压 查看所有版本 , 选择 ...

  4. Linux--多线程(一)

    线程 线程的概念 线程: 线程是OS能够进行运算调度的基本单位.线程是一个进程中的一个单一执行流,通俗地说,一个程序里的一个执行路线就叫做线程. 可以知道的是,一个进程至少有一个执行线程,这个线程就是 ...

  5. 「MySQL高级篇」MySQL之MVCC实现原理&&事务隔离级别的实现

    大家好,我是melo,一名大三后台练习生,死去的MVCC突然开始拷打我! 引言 MVCC,非常顺口的一个词,翻译起来却不是特别顺口:多版本并发控制. 其中多版本是指什么呢?一条记录的多个版本. 并发控 ...

  6. 学习ASP.NET Core Blazor编程系列十——路由(中)

    学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...

  7. 题解合集 (update on 11.5)

    收录已发布的题解 按发布时间排序. 部分可能与我的其他文章有重复捏 qwq . AtCoder for Chinese: Link ZHOJ: Link 洛谷 \(1\sim 5\) : [题解]CF ...

  8. C#之GCHandle

    转载 略谈GCHandle C# - Marshal.StructureToPtr方法简介 Marshal类 两个方法StructureToPtr和PtrToStructure实现序列化 字节 数组 ...

  9. java基础篇—基础语法

    一.关键字和保留字    1.什么是关键字? 通俗来说就是带有特殊含义的字符,有自己专门用途的单词 2.特点? 关键字全部由小写构成,以下是java官方列举出的关键字 注意: 保留关键字:指的是现有版 ...

  10. Kubernetes安装GitLab

    个人名片: 对人间的热爱与歌颂,可抵岁月冗长 Github‍:念舒_C.ying CSDN主页️:念舒_C.ying 个人博客 :念舒_C.ying Kubernetes安装GitLab Step 1 ...