基于对数变换和非线性变换的图像增强(图像亮度调节)

使用C++、opencv实现对图像的对数变换及非线性变换,实现图像增强

相关API:

void normalize(InputArray src, OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray() )

normalize函数的作用是进行矩阵的归一化。归一化就是把要处理的数据经过某种算法的处理限制在所需要的范围内。首先归一化是为了后面数据处理的方便,其次归一化能够保证程序运行时收敛加快。归一化的具体作用是归纳同意样本的统计分布性,归一化在0-1之间是统计的概率分布,归一化在某个区间上是统计的坐标分布,在机器学习算法的数据预处理阶段,归一化也是非常重要的步骤。

第一个参数,InputArray类型的src。输入图像,即源图像,填Mat类的对象即可。
第二个参数,OutputArray类型的dst。函数调用后的运算结果存在这里,和源图片有一样的尺寸和类型。
第三个参数,double类型的 alpha。归一化后的最大值,有默认值1。
第四个参数,double类型的beta。归一化后的最大值,有默认值0。
第五个参数,int类型的 norm type。归一化类型,有NORM_INF、 NORM_L1、NORM_L2和 NORM_MINMAX等参数可选,有默认值 NORM_L2。

第六个参数,int类型的 dtype,有默认值-1。当此参数取负值时,输出矩阵和src有同样的类型,否则,它和src有同样的通道数,且此时图像深度为CV_MAT_DEPTH(dtype)。
第七个参数,InputArray类型的mask,可选的操作掩膜,有默认值noArray()。

void meanStdDev(InputArray src, OutputArray mean, OutputArray stddev, InputArray mask=noArray())

第一个参数src,输入数组,应该有1到4个通道,以便结果可以存储在Scalar中。

第二个参数mean,输出参数,计算的平均值。

第三个参数srddev,输出参数,计算标准偏差。

第四个参数mask,可选的操作掩膜,有默认值没有掩膜

meanStdDev计算结果是double类型的。
meanStdDev计算的均值和标准差都以Mat形式返回,这里返回的是1X1的矩阵。所以访问结果,要访问Mat的元素。

以上参考:毛星云《OpenCV3编程入门》、https://blog.csdn.net/billbliss/article/details/44178737 


代码通过对图像进行对数变换和非线性变换增强图像暗处细节,适用于彩色图像及灰度图像,但灰度图像不要使用cvtColor函数转换,直接以三通道形式输入。

其中,对数变换使用函数为dst(i,j)=c*log(1+src(i,j)),src(i,j)为源图像(i,j)处的像素值,c为可调节的系数,dst(i,j)为经过对数变换后(i,j)处的像素值。

非线性变换的公式如下图:

基于对数变换和非线性变换的图像增强(图像亮度调节)_第1张图片

#include "stdafx.h"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include 
#include 
#include 
#include 
using namespace cv;
using namespace std;

//定义声明一些全局变量和函数
Mat src = imread("C:\\Users\\lenovo\\Desktop\\4.jpg");

Mat hsv, lab, ycrcb;
Mat logdst, nonlinerdst;
Mat dst(src.size(), src.type(), Scalar::all(0));

void LogTransform(cv::Mat& src, cv::Mat& dst, double c);//对数变换函数
void non_liner_tran(Mat& src, Mat& dst);//非线性变换函数
void balance(Mat& src, Mat& logdst, Mat&  nonlinerdst);//平衡图像函数

int main(int argc, char** argv)
{
	system("color 02");
	/*//获取图像每个像素点的RGBHSVLabYCrCb的值,并写入文件中
	cvtColor(src, hsv, COLOR_BGR2HSV);
	cvtColor(src, lab, COLOR_BGR2Lab);
	cvtColor(src, ycrcb, COLOR_BGR2YCrCb);
	FILE *fp;
	fopen_s(&fp, "C:\\Users\\lenovo\\Desktop\\RGB数据.xls", "a");
	for(int i=0;i(i, j)[2], src.at(i, j)[1], src.at(i, j)[0],
				hsv.at(i, j)[0], hsv.at(i, j)[1], hsv.at(i, j)[2],
				lab.at(i, j)[0], lab.at(i, j)[1], lab.at(i, j)[2], 
				ycrcb.at(i, j)[0], ycrcb.at(i, j)[1], ycrcb.at(i, j)[2]);
		}
	fclose(fp);*/

	//对数变换
	LogTransform(src, logdst, 8);
	//非线性变换
	non_liner_tran(src, nonlinerdst);
	//平衡图像
	balance(src, logdst, nonlinerdst);
	//显示原图
	namedWindow("src", WINDOW_NORMAL);
	imshow("src", src);
	waitKey(0);
	return 0;
}

/*对数变换方法1*(灰度图像和彩色图像都适用)*/
void LogTransform(cv::Mat& src, cv::Mat& dst, double c) {
	int nr = src.rows;
	int nc = src.cols*src.channels();
	src.copyTo(dst);
	dst.convertTo(dst, CV_64F);
	if (src.isContinuous() && dst.isContinuous()) {  //判断图像连续性
		nr = 1;
		nc = src.rows*src.cols*src.channels(); //行数*列数 * 通道数= 一维数组的个数
	}

	for (int i = 0; i < nr; i++) {
		const uchar* srcdata = src.ptr (i);  //采用指针访问像素,获取第i行的首地址
		double* dstdata = dst.ptr (i);
		for (int j = 0; j < nc; j++) {
			dstdata[j] = c*log(double(1.0 + srcdata[j])); //开始处理每个像素
		}
	}
	normalize(dst, dst, 0, 255, NORM_MINMAX); //经过对比拉升(将像素值归一化到0-255)得到最终的图像
	dst.convertTo(dst, CV_8U);  //转回无符号8位图像
	namedWindow("logdst", WINDOW_NORMAL);
	imshow("logdst", dst);
}

void non_liner_tran(Mat& src, Mat& dst)
{
	src.copyTo(dst);
	//拆分图像,获取每个通道的平均像素值
	Mat channels[3];
	Mat mat_mean[3], mat_stddev[3];
	double vmean[3] = { 0 };
	split(src, channels);

	for (int n = 0; n < 3; n++)
	{
		meanStdDev(channels[n], mat_mean[n], mat_stddev[n]);
		vmean[n] = mat_mean[n].at(0, 0);
	}
	//非线性变换
	double m = 0, kv = 0, a = 1;
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			for (int n = 0; n < 3; n++)
			{
				if ((src.at(i, j)[n] >= 0) && (src.at(i, j)[n] <= 60))
				{
					kv = 7;
				}
				else if ((src.at(i, j)[n] > 60) && (src.at(i, j)[n] <= 200))
				{
					kv = (src.at(i, j)[n] - 60) / 70 + 7;
				}
				else if ((src.at(i, j)[n] > 200) && (src.at(i, j)[n] <= 255))
				{
					kv = (src.at(i, j)[n] - 200) / 55 + 9;
				}

				m = kv*(src.at(i, j)[n] / (src.at(i, j)[n] + a*vmean[n]));
				
				dst.at(i, j)[n] = (int)(2 / (1 + exp(-m)) - 1) * 255 - src.at(i, j)[n];
			}

		}
	}

	namedWindow("nonlinerdst", WINDOW_NORMAL);
	imshow("nonlinerdst", dst);
}

void balance(Mat& src, Mat& logdst, Mat&  nonlinerdst)
{
	for (int i = 0; i < src.rows; i++)
	{
		for (int j = 0; j < src.cols; j++)
		{
			for (int n = 0; n < 3; n++)
			{
				//将源图像、对数变换图像、非线性变换图像以一定比例合并
				dst.at(i, j)[n] = (int)(0.05*logdst.at(i, j)[n] +
					0.05*nonlinerdst.at(i, j)[n] + 0.8*src.at(i, j)[n]);
			}
		}
	}
	namedWindow("dst", WINDOW_NORMAL);
	imshow("dst", dst);//显示经直方图均衡化的图像
}

源图像:

基于对数变换和非线性变换的图像增强(图像亮度调节)_第2张图片    基于对数变换和非线性变换的图像增强(图像亮度调节)_第3张图片

对数变换后图像:

基于对数变换和非线性变换的图像增强(图像亮度调节)_第4张图片    基于对数变换和非线性变换的图像增强(图像亮度调节)_第5张图片

非线性变换后图像:

基于对数变换和非线性变换的图像增强(图像亮度调节)_第6张图片    基于对数变换和非线性变换的图像增强(图像亮度调节)_第7张图片

结果图:

基于对数变换和非线性变换的图像增强(图像亮度调节)_第8张图片    基于对数变换和非线性变换的图像增强(图像亮度调节)_第9张图片

参考文献:http://xueshu.baidu.com/usercenter/paper/show?paperid=864f24732d62a37915e71dbea2050010&site=xueshu_se&hitarticle=1

代码部分参考:http://www.pianshen.com/article/2960258309/

你可能感兴趣的:(C++,opencv,图像处理)