OpenCV计算机视觉开发入门与实践<二十>:非线性变换灰度变换

1. 对数变换

  对数变换和反对数变换都属于非线性变换。对数变换的公式为:s=c×log(1+r),其中c为常数,r≥0。对数变换有两个作用:

  • 因为对数曲线在像素值较低的区域斜率较大,在像素值较高的区域斜率较小,所以图像经过对数变换之后,较暗区域的对比度将得到提升,因而能增强图像暗部的细节。
  • 图像的傅里叶频谱的动态范围可能宽达0~106。如果直接显示频谱,显示设备的动态范围往往不能满足要求,这个时候就需要使用对数变换,使得傅里叶频谱的动态范围被合 理地非线性压缩。

  在OpenCV中,图像对数变换的实现可以直接通过对图像中的每个元素运用上述公式来完成,也可以通过矩阵整体操作来完成。下面的实例中给出了3种方法(请看代码注释),其中方法一和方法三都是通过矩阵整体操作来完成的,方法二是通过对图像中每个元素进行操作来完成的。方法一和方法三的区别是前者是对原图像进行对数运算,后者是对目标图像进行对数运算。

【例5.6】实现灰度对数变换

方法一:

bash 复制代码
The time consumed by the first method:12.7645

方法二:

bash 复制代码
The time consumed by the first method:15.2897

方法三:

bash 复制代码
The time consumed by the method:2.52928
cpp 复制代码
#include <iostream>
#include<opencv2/opencv.hpp>
using namespace cv;  //所有opencv类都在命名空间cv下
using namespace std;
#pragma comment(lib, "opencv_world4140d.lib") //引用引入库

// 对数变换方法一
cv::Mat logTransform1(cv::Mat srcImage, int c)
{
	// 输入图像判断
	if (srcImage.empty())
		std::cout << "No data!" << std::endl;
	cv::Mat resultImage =
		cv::Mat::zeros(srcImage.size(), srcImage.type());
	// 计算 1 + r
	cv::add(srcImage, cv::Scalar(1.0), srcImage);
	// 转换为32位浮点数
	srcImage.convertTo(srcImage, CV_32F);
	// 计算 log(1 + r)
	log(srcImage, resultImage);
	resultImage = c * resultImage;
	// 归一化处理
	cv::normalize(resultImage, resultImage,
		0, 255, cv::NORM_MINMAX);
	cv::convertScaleAbs(resultImage, resultImage);
	return resultImage;
}

// 对数变换方法二
cv::Mat logTransform2(Mat srcImage, float c)
{
	// 输入图像判断
	if (srcImage.empty())
		std::cout << "No data!" << std::endl;
	cv::Mat resultImage =
		cv::Mat::zeros(srcImage.size(), srcImage.type());
	double gray = 0;
	// 图像遍历分别计算每个像素点的对数变换
	for (int i = 0; i < srcImage.rows; i++) {
		for (int j = 0; j < srcImage.cols; j++) {
			gray = (double)srcImage.at<uchar>(i, j);
			gray = c * log((double)(1 + gray));
			resultImage.at<uchar>(i, j) = saturate_cast<uchar>(gray);
		}
	}
	// 归一化处理
	cv::normalize(resultImage, resultImage,
		0, 255, cv::NORM_MINMAX);
	cv::convertScaleAbs(resultImage, resultImage);
	return resultImage;
}

// 对数变换方法三
cv::Mat logTransform3(Mat srcImage, float c)
{
	// 输入图像判断
	if (srcImage.empty())
		std::cout << "No data!" << std::endl;
	cv::Mat resultImage =
		cv::Mat::zeros(srcImage.size(), srcImage.type());
	srcImage.convertTo(resultImage, CV_32F);
	resultImage = resultImage + 1;
	cv::log(resultImage, resultImage);
	resultImage = c * resultImage;
	cv::normalize(resultImage, resultImage, 0, 255, cv::NORM_MINMAX);
	cv::convertScaleAbs(resultImage, resultImage);
	return resultImage;
}

int main()
{
	// 读取灰度图像及验证
	cv::Mat srcImage = cv::imread("lakeWater.jpg", 0);
	if (!srcImage.data)
		return -1;

	// 验证三种不同方式的对数变换速度
	cv::imshow("src", srcImage);
	float c = 1.2;
	cv::Mat resultImage;
	double tTime;
	tTime = (double)getTickCount();
	const int nTimes = 10;
	for (int i = 0; i < nTimes; i++)
	{
		resultImage = logTransform1(srcImage, c);
	}

	tTime = 1000 * ((double)getTickCount() - tTime) /
		getTickFrequency();
	tTime /= nTimes;
	//输出第一种方法的耗时
	std::cout << " The time consumed by the first method:" << tTime << endl;	cv::imshow("result", resultImage);
	cv::waitKey(0);
	return 0;
}

2. 反对数变换

  反对数变换的公式是s = ((v + 1)r - 1) / v,我们直接以实例来说明。

【例5.7】实现灰度反对数变换

cpp 复制代码
//归一化
//data				进行处理的像素集合
//grayscale			目标灰度级
//rows cols type	目标图像的行、列以及类型
Mat Normalize(vector<double> data, int grayscale, int rows, int cols, int type)
{
	double max = 0.0;
	double min = 0.0;
	for (int i = 0; i < data.size(); i++)
	{
		if (data[i] > max)
			max = data[i];
		if (data[i] < min)
			min = data[i];
	}
	Mat dst;
	dst.create(rows, cols, type);
	int index = 0;
	for (int r = 0; r < dst.rows; r++)
	{
		uchar* dstRowData = dst.ptr<uchar>(r);
		for (int c = 0; c < dst.cols; c++)
		{
			dstRowData[c] = (uchar)(grayscale * ((data[index++] - min) * 1.0 / (max - min)));
		}
	}
	return dst;
}

//反对数变换
Mat NegativeLogTransform(Mat src, double parameter)
{
	vector<double> value;
	for (int r = 0; r < src.rows; r++)
	{
		uchar* srcRowData = src.ptr<uchar>(r);
		for (int c = 0; c < src.cols; c++)
		{
			//反对数变换公式为s = ((v + 1) ^ r - 1) / v
			value.push_back((pow(parameter + 1, srcRowData[c]) - 1) / parameter);
		}
	}
	//计算得出的s经过对比拉升(将像素值归一化到0~255)得到最终的图像
	return Normalize(value, 255, src.rows, src.cols, src.type());
}

int main()
{
	Mat srcImg = imread("gza.jpg", 0);  //读取源图片转为灰度图
	if (srcImg.data == NULL)
	{
		cout << "Fail to open file." << endl;
		return -1;
	}
	imshow("src", srcImg);
	//Mat dstImg = LogTransform(srcImg,0.2);
	Mat dstImg;
	dstImg = NegativeLogTransform(srcImg, 3);
	imshow("result", dstImg);
	waitKey(0);
	return 0;
}

3. 幂律变换

  幂律变换也称伽马变换或指数变换,是另一种常用的灰度非线性变换,主要用于图像的校正,对漂白的图片或者过黑的图片进行修正,也就是对灰度过高或者灰度过低的图片进行修正,增强对比度。幂律变换的公式如下:

  其中系数c和指数γ为正常数。伽马变换的效果与对数变换有点类似,当γ>1时,将较窄范围的低灰度值映射为较宽范围的灰度值,同时将较宽范围的高灰度值映射为较窄范围的灰度值,即低灰度区间压缩,高灰度区间拉伸;当γ小于1时,低灰度区间拉伸,高灰度区间压缩;当γ等于1时,简化成恒等变换。不同γ值对灰度区间进行拉伸或者压缩的效果如图所示。

【例5.8】实现幂律变换

cpp 复制代码
#include <iostream>
#include<opencv2/opencv.hpp>
using namespace cv;  //所有opencv类都在命名空间cv下
using namespace std;

int main()
{
	Mat src;
	src = imread("ball.jpg");
	if (src.empty()) //检验是否成功导入数据
	{
		cout << "not open successed!" << endl;
		return -1;
	}
	namedWindow("input", 0);
	imshow("input", src); // 显示输入的图像src
	cvtColor(src, src, COLOR_RGB2GRAY);
	Mat grayimg;
	grayimg.create(src.size(), src.type()); //创建一个大小类型相同的图像矩阵序列,也可以使用clone()函数
	int height = src.rows;
	int width = src.cols;
	for (int i = 0; i < height; i++)
		for (int j = 0; j < width; j++)
		{
			int gray = src.at< uchar>(i, j);
			grayimg.at< uchar>(i, j) = pow(gray, 0.5);//将灰度值开方
		}
	normalize(grayimg, grayimg, 0, 255, NORM_MINMAX);//归一化,将数据归一到0~255
	imshow("output", grayimg);//显示图像grayimg
	waitKey(0);
	return 0;
}
相关推荐
美狐美颜SDK开放平台1 小时前
直播APP开发技术详解:视频美颜SDK在人脸识别、美型算法与渲染优化中的应用
人工智能·音视频·美颜sdk·直播美颜sdk·第三方美颜sdk
进击的横打1 小时前
【人工智能】Agent 变慢变贵?用「精准路由」把前置判断时间砍掉 85%
人工智能
是Dream呀1 小时前
5人设计组远程连工作站靠抢?用ToDesk团队设计版搭_数位板映射+多屏+权限_协同工作流
人工智能·ai·远程工作·todesk
跨境技工小黎1 小时前
LLM蜜罐系统是什么?AI爬虫如何精准绕过?
开发语言·人工智能·python
hongyucai1 小时前
通用分类算法的建模过程
人工智能·分类·数据挖掘
葡萄城技术团队1 小时前
表格智能体系列 · 2:AI 怎么“看到“你的表格
人工智能
一航jason1 小时前
AIOS 岗位技能技术栈与开源工程全景
人工智能·ai·ai编程·ai-native
SZ_WSKY1 小时前
拆解微型滑台的“生命周期”:非标自动化产线背后的精度损耗与维护真相
人工智能·科技·机器人·自动化·制造
王志来137944730081 小时前
多元场景催生工控服务器机箱差异化需求匀天以柔性适配回应行业挑战
运维·服务器·人工智能·python