图像处理之灰度变换(C++)
文章目录
前言
灰度变换是将图像的灰度级从一个范围映射到另一个范围的操作,从而实现调整图像的亮度和对比度,增强图像的细节和视觉效果。灰度级是指图像中像素的亮度或者灰度值的程度。
灰度变换增强(线性灰度变换、分段线性灰度变换、非线性灰度变换)C++实现。
一、线性灰度变换
1.原理
线性灰度变换的公式: g(x,y) = a*f(x,y) + b
其中,g(x,y)为变换后的灰度值,f(x,y)为原始灰度值,a和b为常数。通过调整a和b的值,可以实现不同的灰度变换效果。
常见的线性灰度变换包括亮度增强、对比度增强、灰度拉伸等操作。例如,
如果要进行亮度增强,可以选择a>1,b=0;
如果要进行对比度增强,可以选择a>1,b=(1-a)*128。
2.代码实现
#include <iostream>
#include <opencv.hpp>
using namespace std;
/*
* @param cv::Mat src 输入图像
* @param cv::Mat& dst 输出图像
* @breif 灰度线性变换
*/
void LinearGrayTrans(const cv::Mat& src,cv::Mat& dst,float a,float b)
{
//建立灰度映射表
float grayTable[256];
for (int i = 0; i < 256; i++)
grayTable[i] = a * i + b;
//遍历修改灰度值
//为了防止发生截断(像素值超过255或者小于0),对dst图像进行数据类型转换
//dst.convertTo(dst, CV_16FC1);
int temp = 0;
for(int i=0;i<src.rows;i++)
for (int j = 0; j < src.cols; j++)
{
temp = src.at<uchar>(i, j);
dst.at<float>(i, j) = grayTable[temp];
}
//将数据缩放到0-255
cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX);
//dst的数据类型还原为CV_8UC1
dst.convertTo(dst,CV_8UC1);
}
int main()
{
// 读取图片
string filepath = "F://work_study//algorithm_demo//zidane.jpg";
cv::Mat src = cv::imread(filepath,cv::IMREAD_GRAYSCALE);
if (src.empty())
{
return -1;
}
//注意:大坑!因为CV_16FC1是为了深度学习引入的,所以在后续的其他函数不支持。更改数据类型为CV_32FC1
cv::Mat dst(src.size(), CV_32FC1);
LinearGrayTrans(src,dst,1.45,15.0);
// 显示图片
cv::imshow("dst", dst);
system("pause");
return 0;
}
二、分段线性灰度变换
1.原理
分段线性变换是指在不同的区间内,使用不同的线性函数进行变换。其公式可以表示为:
其中,a1、a2分别为不同区间内的斜率,b1、b2分别为不同区间内的截距,x1为灰度变换灰度值分界点。根据实际需要,可以有多个分段和不同的线性函数。
2.代码实现
/*
* @param cv::Mat src 输入图像
* @param cv::Mat& dst 输出图像
* @param float a1,b1 第一段分段变换直线的斜率和截距
* @param float a2,b2 第二段分段变换直线的斜率和截距
* @param int x 分段变换灰度值临界点,范围是[0,255]
* @breif 分段灰度线性变换
*/
void PieceLinearGrayTrans(const cv::Mat& src, cv::Mat& dst, float a1, float b1,float a2,float b2,int x)
{
//建立灰度映射表
float grayTable[256];
for (int i = 0; i < 256; i++)
{
if(i<x)
grayTable[i] = a1 * i + b1;
else
grayTable[i] = a2 * i + b2;
}
//遍历修改灰度值
//为了防止发生截断(像素值超过255或者小于0),对dst图像进行数据类型转换
//dst.convertTo(dst, CV_16FC1);
int temp = 0;
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
{
temp = src.at<uchar>(i, j);
dst.at<float>(i, j) = grayTable[temp];
}
//将数据缩放到0-255
cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX);
//dst的数据类型还原为CV_8UC1
dst.convertTo(dst, CV_8UC1);
}
int main()
{
// 读取图片
string filepath = "F://work_study//algorithm_demo//zidane.jpg";
cv::Mat src = cv::imread(filepath,cv::IMREAD_GRAYSCALE);
if (src.empty())
{
return -1;
}
//注意:大坑!因为CV_16FC1是为了深度学习引入的,所以在后续的其他函数不支持。更改数据类型为CV_32FC1
cv::Mat dst(src.size(), CV_32FC1);
PieceLinearGrayTrans(src,dst,1.45,15.0,1.5,25,100);
// 保存图片
cv::imwrite("dst.jpg", dst);
system("pause");
return 0;
}
三、非线性灰度变换(对数变换、伽马变换)
1.原理
1.1 对数变换
对数变换(Logarithmic Transformation)是一种将图像的亮度值进行对数运算的方法,可以增强图像的暗部细节。其原理是通过对亮度值进行对数变换,将较大亮度的像素值压缩到较小范围,同时增强较小亮度的像素值。对数变换的公式为:
s = c * log(1 + v*r)
其中,s为变换后的亮度值,r为原始亮度值,c为常数,用于调控变换的幅度,v为系数。
1.2 伽马变换
伽马变换(Gamma transformations)的原理是通过对原始数据进行幂次运算,改变数据的幅度范围,从而使得数据更加符合人眼对亮度的感知。幂次变换的公式为:
s = c*(r ^ γ)
其中,c为常数,r为原始数据,s为变换后的数据,γ为幂次指数。
当γ>1时,幂次变换将增强较暗的像素值,减弱较亮的像素值;
当γ<1时,幂次变换将增强较亮的像素值,减弱较暗的像素值;
当γ=1时,幂次变换不对像素值进行变换。
2.代码实现
1.1 对数变换
/*
* @param cv::Mat src 输入图像
* @param cv::Mat& dst 输出图像
* @param float c 常数,用于控制变换幅度
* @param float v 系数
* @breif 对数变换
*/
void LogGrayTrans(const cv::Mat& src, cv::Mat& dst,float c,float v)
{
//建立灰度映射表
float grayTable[256];
for (int i = 0; i < 256; i++)
{
grayTable[i] = c*log(1 + v * i);
}
//遍历修改灰度值
//为了防止发生截断(像素值超过255或者小于0),对dst图像进行数据类型转换
//dst.convertTo(dst, CV_16FC1);
int temp = 0;
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
{
temp = src.at<uchar>(i, j);
dst.at<float>(i, j) = grayTable[temp];
}
//将数据缩放到0-255
cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX);
//dst的数据类型还原为CV_8UC1
dst.convertTo(dst, CV_8UC1);
}
int main()
{
// 读取图片
string filepath = "F://work_study//algorithm_demo//zidane.jpg";
cv::Mat src = cv::imread(filepath,cv::IMREAD_GRAYSCALE);
if (src.empty())
{
return -1;
}
//注意:大坑!因为CV_16FC1是为了深度学习引入的,所以在后续的其他函数不支持。更改数据类型为CV_32FC1
cv::Mat dst(src.size(), CV_32FC1);
LogGrayTrans(src, dst, 2, 4);
// 保存图片
cv::imwrite("dst.jpg", dst);
system("pause");
return 0;
}
1.2 伽马变换
/*
* @param cv::Mat src 输入图像
* @param cv::Mat& dst 输出图像
* @param float c 常数,用于控制变化幅度
* @param float gamma 指数
* @breif 伽马变换
*/
void GammaLinearGrayTrans(const cv::Mat & src, cv::Mat & dst, float c, float gamma)
{
//建立灰度映射表
float grayTable[256];
for (int i = 0; i < 256; i++)
{
grayTable[i] = c*pow(i,gamma);
}
//遍历修改灰度值
//为了防止发生截断(像素值超过255或者小于0),对dst图像进行数据类型转换
//dst.convertTo(dst, CV_16FC1);
int temp = 0;
for (int i = 0; i < src.rows; i++)
for (int j = 0; j < src.cols; j++)
{
temp = src.at<uchar>(i, j);
dst.at<float>(i, j) = grayTable[temp];
}
//将数据缩放到0-255
cv::normalize(dst, dst, 0, 255, cv::NORM_MINMAX);
//dst的数据类型还原为CV_8UC1
dst.convertTo(dst, CV_8UC1);
}
int main()
{
// 读取图片
string filepath = "F://work_study//algorithm_demo//zidane.jpg";
cv::Mat src = cv::imread(filepath,cv::IMREAD_GRAYSCALE);
if (src.empty())
{
return -1;
}
//注意:大坑!因为CV_16FC1是为了深度学习引入的,所以在后续的其他函数不支持。更改数据类型为CV_32FC1
cv::Mat dst(src.size(), CV_32FC1);
GammaGrayTrans(src, dst, 2, 4);
// 保存图片
cv::imwrite("dst.jpg", dst);
system("pause");
return 0;
}
总结
本文主要讲解了几种常见的图像变换方法,主要包括线性变换、非线性变换、以及分段变换,关于参数的选择需要通过实验确定,也可以将各种方法组合,比如分段变换不仅仅是线性的。`
因为笔者水平有限,有错误欢迎指出,代码本人均在本地运行实验正确,大家放心使用。