OpenCV图像处理之常见的图像灰度变换

1.灰度线性变换

图像的灰度线性变换是图像灰度变换的一种,图像的灰度变换通过建立灰度映射来调整源图像的灰度,从而达到图像增强的目的。灰度映射通常是用灰度变换曲线来进行表示。通常来说,它是将图像的像素值通过指定的线性函数进行变换,以此来增强或者来减弱图像的灰度,灰度线性变换的函数就是常见的线性函数。

g(x, y) = k · f(x, y) + d

设源图像的灰度值为x,则进行灰度线性变换后的灰度值为y = kx + b (0<=y<=255),下面分别来讨论k的取值变化时线性变换的不同效果

(1).|k|>1时

当k>1时,可以用来增加图像的对比度,图像的像素值在进行变换后全部都线性方法,增强了整体的显示效果,且经过这种变换后,图像的整体对比度明显增大,在灰度图中的体现就是变换后的灰度图明显被拉伸了。

(2).|k|=1时

当k=1时,这种情况下常用来调节图像的亮度,亮度的调节就是让图像的各个像素值都增加或是减少一定量。在这种情况下可以通过改变d值来达到增加或者是减少图像亮度的目的。因为当k=1,只改变d值时,只有图像的亮度被改变了,d>0时,变换曲线整体发生上移,图像的亮度增加,对应的直方图整体向右侧移动,d<0时,变换曲线整体下移,图像的亮度降低,对应的直方图发生水平左移。

(3).0<|k|<1时

此时变换的效果正好与k>1时相反,即图像的整体对比度和效果都被削减了,对应的直方图会被集中在一段区域上。k值越小,图像的灰度分布也就越窄,图像看起来也就显得越是灰暗。

(4).k<0时

在这种情况下,源图像的灰度会发生反转,也就是原图像中较亮的区域会变暗,而较暗的区域将会变量。特别的,此时我们令k = -1,d = 255,可以令图像实现完全反转的效果。对应的直方图也会发生相应的变化。

相应的程序试下如下:

//实现图像的灰度线性变化
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat srcImg = imread("1234.jpg");
	if (!srcImg.data)
	{
		cout << "读入图片失败" << endl;
		return -1;
	}
	imshow("原图像", srcImg);
	double k, b;
	cout << "请输入k和b值:";
	cin >> k >> b;
	int RowsNum = srcImg.rows;
	int ColsNum = srcImg.cols;
	Mat dstImg(srcImg.size(), srcImg.type());
	//进行遍历图像像素,对每个像素进行相应的线性变换
	for (int i = 0; i < RowsNum; i++)
	{
		for (int j = 0; j < ColsNum; j++)
		{
			//c为遍历图像的三个通道
			for (int c = 0; c < 3; c++)
			{
				//使用at操作符,防止越界
				dstImg.at<Vec3b>(i, j)[c] = saturate_cast<uchar>
					(k* (srcImg.at<Vec3b>(i, j)[c]) + b);

			}
		}
	}
	imshow("线性变换后的图像", dstImg);
	waitKey();
	return 0;
}

当k=1.2,b=50时 执行程序的效果如下:

2.灰度对数变换

对数变换的基本形式为

其中,b是一个常数,用来控制曲线的弯曲程度,其中,b越小越靠近y轴,b越大越靠近x轴。表达式中的x是原始图像中的像素值,y是变换后的像素值,可以分析出,当函数自变量较低时,曲线的斜率很大,而自变量较高时,曲线的斜率变得很小。正是因为对数变换具有这种压缩数据的性质,使得它能够实现图像灰度拓展和压缩的功能。即对数变换可以拓展低灰度值而压缩高灰度级值,让图像的灰度分布更加符合人眼的视觉特征。例如进行傅里叶变换后的图像,图像中心绝对高灰度值的存在压缩了低灰度部分的动态范围,所以无法在现实的时候便显出原油的细节。这时就需要使用一个对数变换来对结果图像进行修正,经过适当的处理后,原始图像中低灰度区域的对比度将会增加,暗部细节将被增强。

使用程序进行实现如下:

//实现图像的对数变换,作用是压缩图像较亮区域的动态范围
//使用不同的方法实现图像的对数变换
//基本公式为 y = clog(1+r)
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;

int main()
{
	Mat srcImage = imread("1234.jpg",0);
	if (!srcImage.data)
	{
		cout << "读入图片错误~" << endl;
		return -1;
	}
	double c;
	cout << "请输入常数c:";
	cin >> c;
	Mat srcImage1(srcImage);
	imshow("原图像", srcImage);
	Mat dstImage1(srcImage.size(), srcImage.type());
	Mat dstImage2 = dstImage1.clone();
	Mat dstImage3 = dstImage1.clone();
	//使用第一种方法进行对数变换,对图像整体进行操作
	//首先计算 1+r,注意,是对每一个像素点都进行加1操作
	add(srcImage, Scalar(1.0), srcImage1);
	//转换为32位的浮点数
	srcImage1.convertTo(srcImage1, CV_32F);
	//计算log(1+r)
	log(srcImage1, dstImage1);
	dstImage1 = c * dstImage1;
	//进行归一化处理
	normalize(dstImage1, dstImage1, 0, 255, NORM_MINMAX);
	//convertScaleAbs:先缩放元素再取绝对值,最后转换格式为8bit型
	//在这里不具有缩放功能,作用仅为将格式转换为8bit型
	convertScaleAbs(dstImage1, dstImage1);
	imshow("对数变换图像1", dstImage1);
	/////////////////////////////////////////////////////////////
	//使用第二种方法进行图像的对数变换,对图像的像素进行遍历
	double temp = 0.0;
	for (int i = 0; i < srcImage.rows; i++)
	{
		for (int j = 0; j < srcImage.cols; j++)
		{
			temp = (double)srcImage.at<uchar>(i, j);
			temp = c*log((double)(1 + temp));
			dstImage2.at<uchar>(i, j) = saturate_cast<uchar>(temp);
		}
	}
	//进行归一化处理
	normalize(dstImage2, dstImage2, 0, 255, NORM_MINMAX);
	convertScaleAbs(dstImage2, dstImage2);
	imshow("对数变换图像2", dstImage2);
	//////////////////////////////////////////////////////////////
	//使用第三种方法进行图像的对数变换
	//首先进行图像类型转换
	srcImage.convertTo(dstImage3, CV_32F);
	//图像矩阵元素进行加1操作
	dstImage3 = dstImage3 + 1;
	//图像对数操作
	cv::log(dstImage3, dstImage3);
	dstImage3 = c*dstImage3;
	//图像进行归一化操作
	normalize(dstImage3, dstImage3, 0, 255, NORM_MINMAX);
	convertScaleAbs(dstImage3, dstImage3);
	imshow("对数变换图像3", dstImage3);

	waitKey();
	return 0;
}

当c取1时,效果如下:

3.灰度幂次变换与Gamma校正

基于幂次变换的Gamma校正是图像处理中一种非常重要的非线性变换,它与对数变换相反,它是对输入图像的灰度值进行指数变换,进而校正亮度上的偏差。通常Gamma校正长应用于拓展暗调的细节。通常来讲,当Gamma校正的值大于1时,图像的高光部分被压缩而暗调部分被扩展;当Gamma校正的值小于1时,相反的,图像的高光部分被扩展而暗调备份被压缩。

通常情况下,最简单的Gamma校正可以用下面的幂函数来表示:

其中A是常数,函数的输入和输出都是非负数,当r=1时,为直线变换;当r<1时,低灰度区域动态范围扩大,进而图像对比度增强,高灰度值区域动态范围减小,图像对比度降低,图像整体灰度值增大,此时与图像的对数变换类似。当r>11时,低灰度区域的动态范围减小进而对比度降低,高灰度区域动态范围扩大,图像的对比度提升,图像的整体灰度值变小,Gamma校正主要应用在图像增强。目标检测和图像分析等不同的领域。

总之,r<1的幂函数的作用是提高图像暗区域中的对比度,而降低亮区域的对比度;r>1的幂函数的作用是提高图像中亮区域的对比度,降低图像中按区域的对比度。

所以,对于灰度级整体偏暗的图像,可以使用r<1的幂函数增大动态范围。对于灰度级整体偏亮的图像,可以使用r>1的幂函数增大灰度动态范围。

下面使用程序进行简单的Gamma变换:

//幂次变换与Gamma灰度校正
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace std;
using namespace cv;

Mat GammaTrans(Mat &srcImag, float parameter);

int main()
{
	Mat srcImage = imread("1234.jpg", 0);
	if (!srcImage.data)
	{
		cout << "读入图片失败!" << endl;
		return -1;
	}
	imshow("原始图像", srcImage);
	//初始化几组不同的参数
	float parameter1 = 0.3;
	float parameter2 = 3.0;
	Mat dstImage1 = GammaTrans(srcImage, parameter1);
	imshow("参数1下的Gamma变换", dstImage1);
	Mat dstImage2 = GammaTrans(srcImage, parameter2);
	imshow("参数2下的Gamma变换", dstImage2);
	waitKey();
	return 0;
}

Mat GammaTrans(Mat &srcImag, float parameter)
{
	//建立查表文件LUT
	unsigned char LUT[256];
	for (int i = 0; i < 256; i++)
	{
		//Gamma变换定义
		LUT[i] = saturate_cast<uchar>(pow((float)(i / 255.0), parameter)*255.0f);
	}
	Mat dstImage = srcImag.clone();
	//输入图像为单通道时,直接进行Gamma变换
	if (srcImag.channels() == 1)
	{
		MatIterator_<uchar>iterator = dstImage.begin<uchar>();
		MatIterator_<uchar>iteratorEnd = dstImage.end<uchar>();
		for (; iterator != iteratorEnd; iterator++)
			*iterator = LUT[(*iterator)];
	}
	else
	{
		//输入通道为3通道时,需要对每个通道分别进行变换
		MatIterator_<Vec3b>iterator = dstImage.begin<Vec3b>();
		MatIterator_<Vec3b>iteratorEnd = dstImage.end<Vec3b>();
		//通过查表进行转换
		for (; iterator!=iteratorEnd; iterator++)
		{
			(*iterator)[0] = LUT[((*iterator)[0])];
			(*iterator)[1] = LUT[((*iterator)[1])];
			(*iterator)[2] = LUT[((*iterator)[2])];
		}
	}
	return dstImage;
}

4.分段线性变换

分段线性变换也是一种重要的灰度级变换。对于曝光不足,曝光过度和传感器动态范围都会造成图像表现出低对比度的特征。分段线性变换的作用是提高图像灰度级的动态范围。通常来说,通过阶段一定比例的最亮像素和最暗像素,并使得中间亮度像素占有整个灰度级,因而能够提高图像的全局对比度。在这种情况下,通常称之为对比度拉伸,直方图裁剪,目前广泛的应用于图像后期处理中。通常使用分段函数来实现。下面先简单介绍一下对比度拉伸技术。

图像的对比度拉伸是通过扩展图像灰度级动态范围来实现的,它可以扩展对应的全部灰度范围。图像的低对比度一般是由于图像图像成像亮度不够、成像元器件参数限制或设置不当造成的。提高图像的对比度可以增强图像各个区域的对比效果,对图像中感兴趣的区域进行增强,而对图像中不感兴趣的区域进行相应的抑制作用。对比度拉伸是图像增强中的重要的技术之一。这里设点(x1,y1)与(x2,y2)是分段线性函数中折点位置坐标。常见的三段式分段线性变换函数的公式如下:

其中k1=y1/x1,k2=(y2-y1)/(x2-x1),k3=(255-y2)/(255-y1)

需要注意的是,分段线性一般要求函数是单调递增的,目的是防止图像中的灰度级不满足一一映射。

分段的灰度拉伸技术可以结合直方图处理技术,从而更加灵活地控制输出图像的直方图分布,对特定感兴趣的区域进行对比度调整,增强图像画质。对于图像灰度集中在较暗的区域,可以采用斜率k<0来进行灰度拉伸扩展;对于图像中较亮的区域,可以采用修了k<0来进行灰度拉伸压缩。

实现代码如下:

//实现对比度拉伸
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat srcImage = imread("1234.jpg",0);
	if (!srcImage.data)
	{
		cout << "读入图片错误!" << endl;
		return -1;
	}
	imshow("原始图片", srcImage);
	Mat dstImage(srcImage);
	int rowsNum = dstImage.rows;
	int colsNum = dstImage.cols;
	//图像连续性判断
	if (dstImage.isContinuous())
	{
		colsNum = colsNum*rowsNum;
		rowsNum = 1;
	}
	//图像指针操作
	uchar *pDataMat;
	int pixMax = 0, pixMin = 255;
	//计算图像像素的最大值和最小值
	for (int j = 0; j < rowsNum; j++)
	{
		pDataMat = dstImage.ptr<uchar>(j);
		for (int i = 0; i < colsNum; i++)
		{
			if (pDataMat[i]>pixMax)
				pixMax = pDataMat[i];
			if (pDataMat[i] < pixMin)
				pixMin = pDataMat[i];
		}
	}

	//进行对比度拉伸
	for (int j = 0; j < rowsNum; j++)
	{
		pDataMat = dstImage.ptr<uchar>(j);
		for (int i = 0; i < colsNum; i++)
		{
			pDataMat[i] = (pDataMat[i] - pixMin) * 255 / (pixMax - pixMin);
		}
	}
	imshow("对比度拉伸后的图像", dstImage);
	waitKey();
	return 0;
}

执行后显示效果如下

5.灰度级分层

灰度级分层,也叫做灰度级切片,作用是在整个灰度级范围内将设定窗口内的灰度和其他部分分开。从而突出图像中具有一定灰度范围的区域。大体上来说,灰度级分层有两种类型,即:清除背景和保持背景。清除背景是将灰度窗口内的像素赋值为较亮的值,而其他部分赋值为较暗的值。经过这样的处理后产生的是二值图像,原图像的细节将全部丢失。而保持背景指的是将灰度窗口内的像素赋值为较亮的值,而其他部分的灰度保持不变。

相关代码如下:

//实现灰度级分层
#include <iostream>
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;
using namespace std;

int main()
{
	Mat srcImage = imread("2345.jpg", 0);
	if (!srcImage.data)
	{
		cout << "读入图片错误!" << endl;
		return 0;
	}
	imshow("原图像", srcImage);
	Mat dstImage = srcImage.clone();
	int rowsNum = dstImage.rows;
	int colsNum = dstImage.cols;
	//图像连续性判断
	if (dstImage.isContinuous())
	{
		colsNum *= rowsNum;
		rowsNum = 1;
	}
	//图像指针操作
	uchar *pDataMat;
	int controlMin = 50;
	int controlMax = 150;
	//计算图像的灰度级分层
	for (int j = 0; j < rowsNum; j++)
	{
		pDataMat = dstImage.ptr<uchar>(j);
		for (int i = 0; i < colsNum; i++)
		{
			//第一种方法,二值映射
			if (pDataMat[i]>controlMin)
				pDataMat[i] = 255;
			else
				pDataMat[i] = 0;
			//第二种方法:区域映射
			//if (pDataMat[i] > controlMax && pDataMat[j] < controlMin)
			//	pDataMat[i] = controlMax;
		}
	}
	imshow("灰度分层后的图像", dstImage);
	waitKey();
	return 0;
}

执行后效果图如下:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Opencv实现图像灰度线性变换

    本文实例为大家分享了Opencv实现图像灰度线性变换的具体代码,供大家参考,具体内容如下 通过图像灰度线性变换提高图像对比度和亮度,原图像为src,目标图像为dst,则dst(x,y) = * src(x,y) + . 不仅对单通道图像可以做灰度线性变换,对三通道图像同样可以. #include<opencv2/opencv.hpp>; #include<iostream> using namespace cv; using namespace std; int main(int

  • OpenCV图像处理之常见的图像灰度变换

    1.灰度线性变换 图像的灰度线性变换是图像灰度变换的一种,图像的灰度变换通过建立灰度映射来调整源图像的灰度,从而达到图像增强的目的.灰度映射通常是用灰度变换曲线来进行表示.通常来说,它是将图像的像素值通过指定的线性函数进行变换,以此来增强或者来减弱图像的灰度,灰度线性变换的函数就是常见的线性函数. g(x, y) = k · f(x, y) + d 设源图像的灰度值为x,则进行灰度线性变换后的灰度值为y = kx + b (0<=y<=255),下面分别来讨论k的取值变化时线性变换的不同效果

  • OpenCV图像处理之七种常用图像几何变换

    0 程序环境与所学函数 本章程序运行需要导入下面三个库,并定义了一个显示图像的函数 所学函数 ##放大.缩小 cv.resize(img,dsize,[interpolation]) ##平移变换 M = np.array([[...]], dtype=np.float32) cv.warpAffine(img, M, dsize) ##镜像变换 cv.flip(img, 1) # 垂直镜像 cv.flip(img, 0) # 水平镜像 cv.flit(img, -1) # 水平垂直同时进行 #

  • Python OpenCV图像处理之图像滤波特效详解

    目录 1分类 2邻域滤波 2.1线性滤波 2.2非线性滤波 3频域滤波 3.1低通滤波 3.2高通滤波 1 分类 图像滤波按图像域可分为两种类型: 邻域滤波(Spatial Domain Filter),其本质是数字窗口上的数学运算.一般用于图像平滑.图像锐化.特征提取(如纹理测量.边缘检测)等,邻域滤波使用邻域算子——利用给定像素周围像素值以决定此像素最终输出的一种算子 频域滤波(Frequency Domain Filter),其本质是对像素频率的修改.一般用于降噪.重采样.图像压缩等. 按

  • python opencv 图像处理之图像算数运算及修改颜色空间

    目录 1.图像加法 1.1Numpy加法 1.2OpenCV加法 2.图像融合 3.改变颜色空间 1.图像加法 图像加法有两种方式,一种是通过 Numpy 直接对两个图像进行相加,另一种是通过 OpenCV 的 add() 函数进行相加. 不管使用哪种方法,相加的两个图像必须具有相同的深度和类型,简单理解就是图像的大小和类型必须一致. 1.1Numpy加法 Numpy 的运算方法是: img = img1 + img2 ,然后再对最终的运算结果取模. 当最终的像素值 <= 255 时,则运算结果

  • Python+OpenCV图像处理——图像二值化的实现

    简介:图像二值化就是将图像上的像素点的灰度值设置为0或255,也就是将整个图像呈现出明显的黑白效果的过程. 普通图像二值化 代码如下: import cv2 as cv import numpy as np #全局阈值 def threshold_demo(image): gray = cv.cvtColor(image, cv.COLOR_RGB2GRAY) #把输入图像灰度化 #直接阈值化是对输入的单通道矩阵逐像素进行阈值分割. ret, binary = cv.threshold(gray

  • Java OpenCV图像处理之自定义图像滤波算子

    目录 示例代码 效果图 示例代码 package com.xu.image; import java.io.File; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.Point; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Img

  • C++ opencv图像处理实现图像腐蚀和膨胀示例

    目录 1 概念 2 膨胀 3 腐蚀 1 概念 1.1 腐蚀跟膨胀是最基本的形态学运算 1.2 腐蚀跟膨胀是对图像的白色的部分(亮光部分)进行操作 1.3 腐蚀是对亮光部分进行腐蚀 拥有比原图更小的亮光部分 1.4 膨胀是对亮光部分进行膨胀 拥有比原图更大的亮光部分 这里有一个误区 假如说在图片上的一个字,很多人都会认为膨胀是将图片上的字进行膨胀放大实则不然 可以看到膨胀是将图片上的亮色区域进行放大,字就会变得更小,结构体够大的情况下,字就会看不见了** 可以看到腐蚀是将图片上的亮色区域进行放小,

  • OpenCV数字图像处理基于C++之图像形态学处理详解

    目录 1.图像腐蚀 1.1 CV腐蚀函数 1.2 自定义腐蚀函数 1.3 对比 2.图像膨胀 2.1 CV膨胀函数 2.2 自定义膨胀函数 2.3 对比 3.开运算 3.1 方法一 3.2 方法二 4.闭运算 4.1 方法一 4.2 方法二 4.3 morphologyEx函数介绍 5.顶帽运算 5.1 方法一 5.2 方法二 6.黑帽运算 6.1 方法一 6.2 方法二 7.形态学梯度 7.1 方法一 7.2 方法二 总结 1.图像腐蚀 ​ 原理:腐蚀用来收缩或细化二值图像中的前景,借此实现去

  • Python+OpenCV图像处理—— 色彩空间转换

    一.色彩空间的转换 代码如下: #色彩空间转换 import cv2 as cv def color_space_demo(img): gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) #RGB转换为GRAY 这里的GRAY是单通道的 cv.imshow("gray", gray) hsv = cv.cvtColor(img, cv.COLOR_BGR2HSV) #RGB转换为HSV cv.imshow("hsv", hsv) y

  • Opencv图像处理:如何判断图片里某个颜色值占的比例

    一.功能 这里的需求是,判断摄像头有没有被物体遮挡.这里只考虑用手遮挡---->判断黑色颜色的范围. 二.使用OpenCV的Mat格式图片遍历图片 下面代码里,传入的图片的尺寸是640*480,判断黑色范围. /* 在图片里查找指定颜色的比例 */ int Widget::Mat_color_Find(QImage qimage) { Mat image = QImage2cvMat(qimage);//将图片加载进来 int num = 0;//记录颜色的像素点 float rate;//要计

随机推荐