OpenCV数字图像处理基于C++:灰度变换

1.1 灰度变换概念

在图像预处理中,图像的灰度变换是图像增强的重要手段,灰度变换可以使图像对比度扩展,图像清晰,特征明显,灰度变换主要利用点运算来修正像素灰度,由输入像素点的灰度值确定相应输出点的灰度值,是一种基于图像变换的操作。

1.2 灰度变换的作用

  1. 改善图像是质量,显示更多的细节,提高图像的对比度;
  2. 有选择的突出图像感兴趣的特征或者抑制图像中不需要的特征;
  3. 可以有效的改变图像的直方图的分布,使像素的分布更加均匀。

1.3 灰度变换的方法

  1. 线性灰度变换
  2. 分段线性灰度变换
  3. 非线性灰度变换(对数变换,幂律变换(伽马变换))

1.4 灰度化

(1) 灰度化原理
灰度化处理就是将一幅色彩图像转化为灰度图像的过程。彩色图像分为R,G,B三个分量,分别显示出红绿蓝等各种颜色,灰度化就是使彩色的R,G,B分量相等的过程。灰度值大的像素点比较亮(像素值最大为255,为白色),反之比较暗(像素最下为0,为黑色)。图像灰度化核心思想是 R = G = B ,这个值也叫灰度值。
图像灰度化的算法:1)最大值法:使转化后的R,G,B得值等于转化前3个值中最大的一个,即:R=G=B=max(R,G,B)。这种方法转换的灰度图亮度很高。2)平均值法:是转化后R,G,B的值为转化前R,G,B的平均值。即:R=G=B=(R+G+B)/3。这种方法产生的灰度图像比较柔和。
# Y = 0.299R + 0.587G + 0.114B3)加权平均值法:按照一定权值,对R,G,B的值加权平均,即:Y = 0.299R + 0.587G + 0.114B,分别为R,G,B的权值,取不同的值形成不同的灰度图像。由于人眼对绿色最为敏感,红色次之,对蓝色的敏感性最低,因此使将得到较易识别的灰度图像。一般时,得到的灰度图像效果最好。(2) 二值化原理二值化核心思想,设阈值,大于阈值的为0(黑色)或 255(白色),使图像称为黑白图。阈值可固定,也可以自适应阈值。自适应阈值一般为一点像素与这点为中序的区域像素平均值或者高斯分布加权和的比较,其中可以设置一个差值也可以不设置。

1.5 实现 RGB 图像转灰度图像

void grayImageShow(Mat& input, Mat& output)
{for (int i = 0; i < input.rows; ++i)for (int j = 0; j < input.cols; ++j)output.at<uchar>(i, j) = saturate_cast<uchar>(0.114 * input.at<Vec3b>(i, j)[0] + 0.587 * input.at<Vec3b>(i, j)[1] + 0.2989 * input.at<Vec3b>(i, j)[2]);imshow("由经验公式得到的灰度图像", output);
}int main()
{Mat src, gray, dst;        //分别用来存储原图,灰度图gray = imread("E:\\Lena.jpg", IMREAD_GRAYSCALE);//由imread()得到的灰度图像src = imread("E:\\Lena.jpg");dst.create(src.rows, src.cols, CV_8UC1);      //创建原图尺寸大小的空白图imshow("scr", src);imshow("由imread得到的灰度图像", gray);grayImageShow(src, dst);//由经验公式得到的灰度图像waitKey(-1); //按键后再继续return 0;
}

saturate_cast函数的作用即是:当运算完之后,结果为负,则转为0,结果超出255,则为255。(防止溢出)

访问(i,j)处像素

以8位(0~255)灰度图像和BGR彩色图像为例,用at可以访问图像像素:

//灰度图像:
image.at<uchar>(i,j) //j为行数,i为列数
//BGR彩色图像
image.at<Vec3b>(i,j)[0] //B分量
image.at<Vec3b>(i,j)[1] //G分量
image.at<Vec3b>(i,j)[2] //R分量

优化

 Gray = (2989*R+5870*G+1140*B)/10000
=>>  Gray = (4898*R+9618*G+1868*B)>>14
=>>  Gray = (76*R+150*G+30*B)>>8

1.6 对比度

图像对比度指的是一幅图像中明暗区域最亮的白和最暗的黑之间不同亮度层级的测量,即指一幅图像灰度反差的大小。差异范围越大代表对比越大,图像越清晰;差异范围越小代表对比越小,图像越模糊。

1.7 线性变换

y = k x + b y = kx+b y=kx+b

k > 1 时,输出图像的对比度增大;
k < 1 时,输出图像的对比度减小;
k = 1 且 b ≠ 0 时,所有图像的灰度值上移或者下移,其效果是是整个图像变亮或者变暗;
k = 1 且 b = 0 时,输入图像与输出图像相同;
k = -1 且 b = 255 时,输入图像的灰度正好反转;
k > 0 且 b > 0时,暗区域变量,亮区域变暗,点运算完成了图像求补运算。

1.8 实现图像线性变换

#include<opencv2/opencv.hpp>
#include<iostream>using namespace cv;
using namespace std;// 图像线性变换操作
Mat linearTransform(Mat srcImage, float k, int b)
{if (srcImage.empty()) {std::cout << "No data!" << std::endl;}const int nRows = srcImage.rows;const int nCols = srcImage.cols;Mat resultImage = Mat::zeros(srcImage.size(), srcImage.type());// 图像元素遍历for (int i = 0; i < nRows; i++){for (int j = 0; j < nCols; j++){for (int c = 0; c < 3; c++)//如果源图像是灰度图,那么把这里改为c<1即可{// 矩阵at操作,检查下标防止越界resultImage.at<Vec3b>(i, j)[c] = saturate_cast<uchar>(k * (srcImage.at<Vec3b>(i, j)[c]) + b);}}}return resultImage;
}
int main()
{// 图像获取及验证Mat src;src = imread("E:\\Lena.jpg");Size nSizeWindows = Size(2, 1);// 大图像大小Mat showWindowsImages(400, 820, CV_8UC3, Scalar(0, 0, 0));//利用Rect区域将小图像置于大图像的相应区域Mat tempImage = showWindowsImages(Rect(0, 0, 400, 400));//利用resize函数实现图像缩放resize(src, tempImage, Size(400, 400));tempImage = showWindowsImages(Rect(420, 0, 400, 400));Mat resImage(src.size(), src.type());// 线性变换float k = 1.5;int b = 1;Mat new_image = linearTransform(src, k, b);resize(new_image, tempImage, Size(400, 400));imshow("线性变换对比图", showWindowsImages);waitKey(0);return 0;
}
int main()
{// 图像获取及验证Mat src;src = imread("E:\\Lena.jpg");Size nSizeWindows = Size(2, 1);// 大图像大小Mat showWindowsImages(400, 820, CV_8UC3, Scalar(0, 0, 0));//利用Rect区域将小图像置于大图像的相应区域Mat tempImage = showWindowsImages(Rect(0, 0, 400, 400));//利用resize函数实现图像缩放resize(src, tempImage, Size(400, 400));tempImage = showWindowsImages(Rect(420, 0, 400, 400));Mat resImage(src.size(), src.type());// 线性变换float k = -1;int b = 255;int nRows = src.rows;int nCols = src.cols;Mat resultImage(src.size(), src.type());for (int i = 0; i < nRows; i++){for (int j = 0; j < nCols; j++){for (int c = 0; c < 3; c++)//如果源图像是灰度图,那么把这里改为c<1即可{// 矩阵at操作,检查下标防止越界resultImage.at<Vec3b>(i, j)[c] = saturate_cast<uchar>(k * (src.at<Vec3b>(i, j)[c]) + b);}}}resize(resultImage, tempImage, Size(400, 400));imshow("线性变换对比图", showWindowsImages);waitKey(0);return 0;
}

k > 1 时,输出图像的对比度增大

k < 1 时,输出图像的对比度减小

k = 1 且 b ≠ 0 时,所有图像的灰度值上移或者下移,其效果是是整个图像变亮或者变暗

k = 1 且 b = 0 时,输入图像与输出图像相同

k = -1 且 b = 255 时,输入图像的灰度正好反转

k > 0 且 b > 0时,暗区域变量,亮区域变暗,点运算完成了图像求补运算

1、bit_depth:比特数,有代表8bite\16bite\32bite\64bite8表示你所创建的储存图片的Mat对象中,每个像素点在内存空间所占的空间大小8bite。2、S|U|FS: signed int,即有符号整型。U: unsigned int,即无符号整型。F: float,单精度浮点型。3、<number_of_channels>:代表所存储的图片的通道数。若为1:grayImg灰度图像,即单通道图像。若为2:RGB彩色图像,即3通道图像。若为3:带Alpha通道的RGB彩色图像,即4通道图像。
Scalar(a)  灰度值1、Scalar(0)  黑色2、Scalar(255)  白色3、Scalar(100)  灰色Scalar(B,G.R)   BGR3通道颜色Scalar(255,0,0)  蓝色Scalar(0,255,0)  绿色
Scalar(B,G.R,C)   Blue,Green,Red,ChannelsScalar(H,S.V) hue色调,saturation饱和度,value亮度

1.9 分段线性变换

其中x表示原图灰度,y表示变换后的图像灰度

1.10 实现分段变换核心算法

// 分段线性拉伸
//fStart :分段区间起点
//fEnd   :分段区间终点
//fSout  :映射区间起点
//fEout  :映射区间终点void dividedLinearStrength(cv::Mat& matInput, cv::Mat& matOutput, float fStart, float fEnd,float fSout, float fEout)
{float fK1 = fSout / fStart;float fK2 = (fEout - fSout) / (fEnd - fStart);float fC2 = fSout - fK2 * fStart;float fK3 = (255.0f - fEout) / (255.0f - fEnd);float fC3 = 255.0f - fK3 * 255.0f;       //把点(255.255)和斜率fk3代入y=kx+b求截距std::vector<unsigned char> loolUpTable(256);for (size_t m = 0; m < 256; m++){if (m < fStart){loolUpTable[m] = static_cast<unsigned char>(m * fK1);}else if (m > fEnd){loolUpTable[m] = static_cast<unsigned char>(m * fK3 + fC3);}else{loolUpTable[m] = static_cast<unsigned char>(m * fK2 + fC2);}}matOutput = cv::Mat::zeros(matInput.rows, matInput.cols, matInput.type());for (size_t r = 0; r < matInput.rows; r++){unsigned char* pInput = matInput.data + r * matInput.step[0];unsigned char* pOutput = matOutput.data + r * matOutput.step[0];for (size_t c = 0; c < matInput.cols; c++){pOutput[c] = loolUpTable[pInput[c]];}}
}
int main()
{cv::Mat matSrc = cv::imread("E:\\Lena.jpg", 1);cv::imshow("原始图", matSrc);cv::Mat matDLS;dividedLinearStrength(matSrc, matDLS, 72, 200, 5, 240);cv::imshow("分段线性拉伸", matDLS);cv::waitKey(0);return 0;
}

int main()
{Mat img1, img2;img1 = imread("E:\\Lena.jpg", 1);imshow("原图", img1);img2 = Mat::zeros(img1.size(), 1);for (int i = 0; i < img1.rows; i++){for (int j = 0; j < img1.cols; j++){uchar temp = img1.at<uchar>(i, j);if (temp <= 70){img2.at<uchar>(i, j) = saturate_cast<uchar>(0.5 * temp + 20);}else if (temp > 70 && temp <= 150){img2.at<uchar>(i, j) = saturate_cast<uchar>(1.2 * temp + 100);}else if (temp > 150 && temp <= 255){img2.at<uchar>(i, j) = saturate_cast<uchar>(0.9 * temp + 55);}}}imshow("分段线性", img2);waitKey(0);
}

1.11 对数变换

对数变换原理:扩展图像中的暗像素值,压缩高灰度值。

1.12 实现灰度对数变换

// 对数变换方法1Mat logTransform1( Mat srcImage, int c)
{// 输入图像判断if (srcImage.empty())cout << "No data!" <<  endl;Mat resultImage = Mat::zeros(srcImage.size(), srcImage.type());// 计算 1 + radd(srcImage,  Scalar(1.0), srcImage);// 转换为32位浮点数srcImage.convertTo(srcImage, CV_32F);// 计算 log(1 + r)log(srcImage, resultImage);resultImage = c * resultImage;// 归一化处理normalize(resultImage, resultImage,0, 255,  NORM_MINMAX);convertScaleAbs(resultImage, resultImage);return resultImage;
}
// 对数变换方法2Mat logTransform2(Mat srcImage, float c)
{// 输入图像判断if (srcImage.empty())cout << "No data!" <<  endl;Mat resultImage =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);}}// 归一化处理normalize(resultImage, resultImage,0, 255,  NORM_MINMAX);convertScaleAbs(resultImage, resultImage);return resultImage;
}
// 对数变换方法3Mat logTransform3(Mat srcImage, float c)
{// 输入图像判断if (srcImage.empty())cout << "No data!" <<  endl;Mat resultImage =Mat::zeros(srcImage.size(), srcImage.type());srcImage.convertTo(resultImage, CV_32F);resultImage = resultImage + 1;log(resultImage, resultImage);resultImage = c * resultImage;normalize(resultImage, resultImage, 0, 255,  NORM_MINMAX);convertScaleAbs(resultImage, resultImage);return resultImage;
}
int main()
{// 读取灰度图像及验证Mat srcImage =  imread("E:\\Lena.jpg", 0);if (!srcImage.data)return -1;// 验证三种不同方式的对数变换速度imshow("原图", srcImage);float c = 1.2;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;cout << "第一种方法耗时:" << tTime <<  endl;imshow("效果图", resultImage);waitKey(0);return 0;
}

上面的实例给了三种方法,其中方法一和方法三是通过对矩阵整体操作来完成的,方法二是对图像中每个元素操作来完成的。方法一和方法三的区别是前者是对源图像进行对数操作,后者是对目标图像进行对数操作。

1.13 反对数变换

//归一化
//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) / vvalue.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("E:\\Lena.jpg", 0);if (srcImg.data == NULL){cout << "图像打开失败" << endl;return -1;}imshow("原图", srcImg);//Mat dstImg = LogTransform(srcImg,0.2);Mat dstImg;dstImg = NegativeLogTransform(srcImg, 255);imshow("变换后", dstImg);waitKey(0);return 0;}

1.14 幂律变换

幂律变换也称伽马变换或指数变换,主要用于图像的校正,对漂白的图片或过黑的图片进行修正,增强对比度。

其中,c和γ为常数。伽马变换的效果与对数变换效果类似,当γ>1时,将较窄范围的低灰度值映射为较宽范围的灰度,同时将较宽范围的高灰度值映射为较窄范围的灰度值;当γ<1时,情况相反,与反对数变换类似。

γ>1时,低灰度区间压缩,高灰度区间拉伸;当γ<1时,低灰度区间拉伸,高灰度区间压缩;γ=1时,简化为恒等变换。

1.15 实现幂律变换

int main()
{Mat src;src = imread("E:\\Lena.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;
}

OpenCV数字图像处理基于C++:灰度变换相关推荐

  1. OpenCV数字图像处理学习平台

    原始图像1-1 处理中的图像1-2 处理结果1-3(检测到所有圆) OpenCV数字图像处理学习平台,是一款可视化.交互式方式学习OpenCV的工具软件,由资深计算机视觉专家精心打造,花费近两年整理成 ...

  2. 《opencv 数字图像处理 图像基础》

    <opencv 数字图像处理 图像基础> 矩阵 通道分离和合并 彩色图像转灰度图像 灰度图转二值化图像 图像运算 矩阵 定义一个显示图像的函数,对于灰度图,里面添加了vmin=0,vmax ...

  3. 数字图像处理(18): 图像灰度变换——线性灰度变换 和 非线性灰度变换(对数变换 与 伽马变换)

    目录 1 灰度变换简介 2 线性灰度变换­-图像反转 3 非线性灰度变换 3.1 对数变换 3.2 伽马变换 参考资料 1 灰度变换简介 灰度变换是图像增强的一种重要手段,用于改善图像显示效果,属于空 ...

  4. c++ opencv数字图像处理:访问图像像素,遍历图像像素

    文章目录 前言 一.访问图像像素 1.访问(j,i)处像素 2.例子:在图像中加入白色椒盐噪声 二.遍历图像像素 1.指针扫描 2.opencv自带的卷积运算:filter2D 前言 数字图像处理c+ ...

  5. c++ opencv数字图像处理:频率域滤波--高通滤波--高斯高通滤波

    文章目录 前言 一.高斯高通滤波器(GHPF) 二.代码 前言 数字图像处理c++ opencv(VS2019 opencv4.53)持续更新 一.高斯高通滤波器(GHPF) D 2 ( u , v ...

  6. c++ opencv数字图像处理:频率域滤波--低通滤波--理想低通滤波

    文章目录 前言 一.理想低通滤波器(ILPF) 二.代码 三.说明 前言 数字图像处理c++ opencv(VS2019 opencv4.53)持续更新 一.理想低通滤波器(ILPF) 通过设置频率半 ...

  7. c++ opencv数字图像处理:频率域滤波--低通滤波--巴特沃斯低通滤波

    文章目录 前言 一.巴特沃斯低通滤波器(BLPF) 二.代码 三.说明 前言 数字图像处理c++ opencv(VS2019 opencv4.53)持续更新 一.巴特沃斯低通滤波器(BLPF) D2( ...

  8. c++ opencv数字图像处理:频率域滤波--高通滤波--巴特沃斯高通滤波

    文章目录 前言 一.巴特沃斯高通滤波器(BHPF) 二.代码 前言 数字图像处理c++ opencv(VS2019 opencv4.53)持续更新 一.巴特沃斯高通滤波器(BHPF) D2(u,v)为 ...

  9. c++ opencv数字图像处理:频率域滤波--高通滤波--理想高通滤波

    文章目录 前言 一.理想高通滤波器(IHPF) 二.代码 前言 数字图像处理c++ opencv(VS2019 opencv4.53)持续更新 一.理想高通滤波器(IHPF) 二.代码 主代码: #i ...

最新文章

  1. 欧洲AI规范先行,值得肯定与借鉴 --- 我看欧盟发布AI道德规范
  2. 接口中私有方法【应用】
  3. Oil Deposits
  4. web前端编程语言有哪些?
  5. Mac Wireshark 设置中文
  6. 81 文件操作——关闭文件
  7. redis 修改默认端口号6379(Windows)
  8. 联想拯救者Y7000P和Y9000P的区别
  9. BZOJ1778 [Usaco2010 Hol]Dotp 驱逐猪猡
  10. 压敏电阻特点,与普通电阻有什么不同?
  11. 使用MMDetection训练自己的数据集
  12. Android VR入门文章
  13. win10硬盘锁怎么解除_大师教你解决win10系统取消移动硬盘写保护的图文办法
  14. 程序员的10大境界,计算科学的10层楼,比尔盖茨看了会自卑,牛顿看了会落泪!
  15. opencv3/C++ 积分图像
  16. 京东运维开发工程师 2019校招卷总结
  17. 解析复杂xml资源获取目标参数值
  18. 超额准备金 超额备付金_销售不足和超额投放
  19. 在linux系统下实现SHELL自动化批量备份交换机配置文件
  20. 计算机实用教程pdf,计算机基础实用教程电子版.pdf

热门文章

  1. Echarts绘制Tree树图的涟漪效果effectScatter
  2. 华为瘦胖ap互转_华为胖ap转换瘦apv200r006c10spc100指导书
  3. 传销?花生日记罚款7456万元这个微信社群营销分钱模式要知道
  4. 删除 linux 回收站内容,Linux删除文件实现回收站功能
  5. vmalloc 虚拟实现
  6. 【系统分析师之路】第七章 复盘系统设计(业务流程建模)
  7. hadoop 报错 there appears to be a gap in the edit log. we expected txitd 1, but got txid 14444
  8. 做报表到10点才下班,做的还是丑,怎样才能做出一张好看的报表?
  9. 使用C++模拟动态密码验证
  10. 如何制作公众号首图?教你一键套用图片模板