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

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;dst指数
cout << "请输⼊k和b值:";
cin >> k >> b;
int RowsNum = ws;
int ColsNum = ls;
Mat dstImg(srcImg.size(), pe());
//进⾏遍历图像像素,对每个像素进⾏相应的线性变换
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(), pe());
Mat dstImage2 = dstImage1.clone();
Mat dstImage3 = dstImage1.clone();
//使⽤第⼀种⽅法进⾏对数变换,对图像整体进⾏操作
//⾸先计算 1+r,注意,是对每⼀个像素点都进⾏加1操作
add(srcImage, Scalar(1.0), srcImage1);
//转换为32位的浮点数
//计算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 < ws; i++)
{
for (int j = 0; j < ls; 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);
//////////////////////////////////////////////////////////////
//使⽤第三种⽅法进⾏图像的对数变换
//图像矩阵元素进⾏加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];
{
//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 = d<uchar>();
for (; iterator != iteratorEnd; iterator++)
*iterator = LUT[(*iterator)];
}
else
{
//输⼊通道为3通道时,需要对每个通道分别进⾏变换
MatIterator_<Vec3b>iterator = dstImage.begin<Vec3b>();
MatIterator_<Vec3b>iteratorEnd = d<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 = ws;
int colsNum = ls;
//图像连续性判断
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 = ws;
int colsNum = ls;
//图像连续性判断
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;
}
执⾏后效果图如下:

本文发布于:2024-09-21 20:32:41,感谢您对本站的认可!

本文链接:https://www.17tex.com/tex/3/380288.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:图像   灰度   范围   区域   灰度级   变换
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议