Opencv C++ 六、灰度变换:线性变换、灰度反转、对数变换、伽马变换、(自适应)直方图均衡化
时间:2024-04-25 15:50:31 来源:网络cs 作者:胡椒 栏目:卖家故事 阅读:
一、灰度变换的原理:
通过变换函数T将原图像像素灰度值r映射为灰度值s: s=T(r).
二、灰度变换的方法:
线性变换(亮度和对比度调整):
原理:线性变换是一种简单的亮度和对比度调整方法,通过对每个像素的灰度级别应用线性变换公式来实现。对每个像素应用公式output_pixel = input_pixel * alpha + beta
,其中 alpha
控制对比度,beta
控制亮度。增大 alpha
值可以增加对比度,增大 beta
值可以增加亮度。 对数变换:
原理:对数变换通过应用对数函数对图像的每个像素值进行修改。这种变换适用于增强图像的低灰度级别,因为它拉伸了低灰度级别之间的差异。公式为output_pixel = c * log(1 + input_pixel)
,其中 c
是缩放常数。 伽马校正:
原理:伽马校正通过应用幂函数对图像的每个像素值进行修改。伽马校正可以用于调整图像的对比度和亮度。公式为output_pixel = c * (input_pixel ^ gamma)
,其中 c
是缩放常数,gamma
是伽马值。增大 gamma
值可以增加对比度。 直方图均衡化:
原理:直方图均衡化旨在拉伸图像的灰度级别分布,使其更均匀。这通过重新分配像素值以增加亮度级别的数量来实现。这有助于增强图像的对比度,并突出图像中的细节。该方法的原理是重新映射图像的累积分布函数,使其变为均匀分布。自适应直方图均衡化:
原理:自适应直方图均衡化将图像划分为小块,然后对每个块进行直方图均衡化。这使得图像在不同区域的灰度级别分布更加均匀,尤其是当图像具有明显的亮度变化时。三、线性变换
#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>using namespace cv;using namespace std;int main() { // 加载图像 Mat image = imread("D://lena.png"); if (image.empty()) { cout << "无法加载图像" << endl; return -1; } // 用户定义的亮度和对比度参数 double alpha = 1.5; // 控制对比度 int beta = 30; // 控制亮度 // 线性变换 Mat adjusted_image = Mat::zeros(image.size(), image.type()); for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { for (int c = 0; c < image.channels(); c++) { adjusted_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(alpha * image.at<Vec3b>(y, x)[c] + beta); } } } // 显示原始图像和调整后的图像 imshow("原始图像", image); imshow("亮度和对比度调整后的图像", adjusted_image); waitKey(0); return 0;}
函数介绍:
// 线性变换 Mat adjusted_image = Mat::zeros(image.size(), image.type());
Mat::zeros
是OpenCV中的一个函数,用于创建一个矩阵(图像)并将其所有元素初始化为零。在这个特定的情况下,Mat::zeros(image.size(), image.type())
用于创建一个与 image
具有相同尺寸和通道数的矩阵,并将其所有元素初始化为零。
具体解释:
Mat
是OpenCV中用于表示图像和矩阵的数据结构。image.size()
返回原始图像 image
的尺寸(行数和列数)。image.type()
返回原始图像 image
的数据类型和通道数信息。 Mat::zeros(image.size(), image.type())
将创建一个与原始图像 image
具有相同尺寸和通道数的图像,但所有像素的值都将初始化为零。这是为了在后续的操作中存储调整后的图像,因为在进行线性变换之前,我们希望输出图像的像素值都为零。
实际上,Mat::zeros
在图像处理中经常用于创建用于存储中间或结果图像的矩阵,以确保其初始值为零。这有助于避免潜在的垃圾值或不可预测的结果。
for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { for (int c = 0; c < image.channels(); c++) { adjusted_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(alpha * image.at<Vec3b>(y, x)[c] + beta); } } }
for (int y = 0; y < image.rows; y++)
:这是外循环,用于遍历图像的每一行。
for (int x = 0; x < image.cols; x++)
:这是内循环,用于遍历图像的每一列(像素)。
for (int c = 0; c < image.channels(); c++)
:这是内部的循环,用于遍历图像的通道(例如,对于彩色图像,通道可能是B、G、R)。
adjusted_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(alpha * image.at<Vec3b>(y, x)[c] + beta);
:这行代码执行实际的线性变换操作。具体地,它对每个像素的每个通道执行以下操作:
image.at<Vec3b>(y, x)[c]
:这部分代码从原始图像 image
中获取当前像素位置 (y, x)
处的通道 c
的灰度级别。alpha * image.at<Vec3b>(y, x)[c] + beta
:这是线性变换的公式。alpha
控制对比度的调整,beta
控制亮度的调整。通过将 alpha
乘以当前像素的灰度级别并添加 beta
,可以实现亮度和对比度的调整。saturate_cast<uchar>(...)
:这是一个饱和操作,确保调整后的像素值在0到255之间。如果结果小于0,它将被截断为0;如果结果大于255,它将被截断为255。adjusted_image.at<Vec3b>(y, x)[c]
:最终的调整后的像素值将存储在 adjusted_image
中的相同位置 (y, x)
处的通道 c
中。 四、灰度反转:
#include<iostream>#include<opencv2/opencv.hpp>using namespace cv;using namespace std;int main(){Mat image1, output_image, image1_gray; //定义输入图像,输出图像,灰度图像image1 = imread("D://lena.png"); //读取图像;if (image1.empty()){cout << "读取错误" << endl;return -1;}cvtColor(image1, image1_gray, COLOR_BGR2GRAY); //灰度化imshow(" image1_gray", image1_gray); //显示灰度图像output_image = image1_gray.clone();for (int i = 0; i < image1_gray.rows; i++){for (int j = 0; j < image1_gray.cols; j++){output_image.at<uchar>(i, j) = 255 - image1_gray.at<uchar>(i, j); //灰度反转}}imshow("output_image", output_image); //显示反转图像waitKey(0); //暂停,保持图像显示,等待按键结束return 0;}
其结果为:
左侧为原始图片,右侧为反转后的图片
函数介绍:
cvtColor(image1, image1_gray, COLOR_BGR2GRAY);
cvtColor
函数的一般语法如下:
void cvtColor(InputArray src, OutputArray dst, int code, int dstCn = 0);
以下是一些常见的 code
值以及它们的含义:
CV_BGR2GRAY
:BGR到灰度转换。CV_BGR2HSV
:BGR到HSV(色调、饱和度、明度)转换。CV_BGR2Lab
:BGR到Lab转换。CV_BGR2YUV
:BGR到YUV转换。CV_RGB2BGR
:RGB到BGR转换。CV_GRAY2BGR
:灰度到BGR转换。 该代码中选择的是CV_BGR2GRAY,即将BGR转化为灰度。
output_image = image1_gray.clone();for (int i = 0; i < image1_gray.rows; i++){for (int j = 0; j < image1_gray.cols; j++){output_image.at<uchar>(i, j) = 255 - image1_gray.at<uchar>(i, j); //灰度反转}}
output_image = image1_gray.clone();
:首先,创建一个与输入图像 image1_gray
具有相同尺寸和类型的输出图像 output_image
,并将其初始化为与输入图像相同。
接下来,程序使用嵌套的循环遍历输入图像 image1_gray
中的每个像素。
output_image.at<uchar>(i, j) = 255 - image1_gray.at<uchar>(i, j);
:在内循环中,对于每个像素 (i, j)
,它执行以下操作:
image1_gray.at<uchar>(i, j)
:从输入图像中获取像素 (i, j)
处的灰度值。at<uchar>(i, j)
表示像素值为无符号字符型(8位灰度图像)。255 - image1_gray.at<uchar>(i, j)
:将获取的灰度值减去255,实现了灰度反转。这将导致较亮的像素变暗,较暗的像素变亮,从而反转了图像的外观。 通过这个过程,输入图像中的每个像素的灰度值都被反转,最终结果存储在 output_image
中。这种操作可以用于创建底片效果或将负片转换为正片,以实现图像的特殊效果。
五、伽马校正
#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>using namespace cv;using namespace std;int main() { // 加载图像 Mat image = imread("D://lena.png"); if (image.empty()) { cout << "无法加载图像" << endl; return -1; } // 伽马值 double gamma = 2;//大于等于0,小于0为黑色。 // 伽马校正 Mat gamma_corrected_image = Mat::zeros(image.size(), image.type()); for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { for (int c = 0; c < image.channels(); c++) { double pixel_value = image.at<Vec3b>(y, x)[c] / 255.0; double corrected_value = pow(pixel_value, gamma) * 255.0; gamma_corrected_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(corrected_value); } } } // 显示原始图像和伽马校正后的图像 imshow("原始图像", image); imshow("伽马校正后的图像", gamma_corrected_image); waitKey(0); return 0;}
效果图:
六、对数变换
#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>#include <cmath>using namespace cv;using namespace std;int main() { // 加载图像 Mat image = imread("D://lena.png"); if (image.empty()) { cout << "无法加载图像" << endl; return -1; } // 对数变换参数 double c = 1.0; // 常数 double gamma = 0.5; // 对数变换的参数 // 对数变换 Mat log_transformed_image = Mat::zeros(image.size(), image.type()); for (int y = 0; y < image.rows; y++) { for (int x = 0; x < image.cols; x++) { for (int c = 0; c < image.channels(); c++) { double pixel_value = image.at<Vec3b>(y, x)[c] / 255.0; double corrected_value = c * log(1 + pixel_value) / log(1 + gamma); log_transformed_image.at<Vec3b>(y, x)[c] = saturate_cast<uchar>(corrected_value * 255.0); } } } // 显示原始图像和对数变换后的图像 imshow("原始图像", image); imshow("对数变换后的图像", log_transformed_image); waitKey(0); return 0;}
效果图:
七、直方图均衡化
在这个示例中,我们加载了一幅图像,并使用 IMREAD_GRAYSCALE
模式将其加载为灰度图像。然后,我们使用 equalizeHist
函数执行直方图均衡化,将图像的对比度增强。
直方图均衡化是一种简单而有效的方法,可以使图像中的像素值更均匀分布,从而改善图像的对比度。这个示例演示了如何使用OpenCV来实现直方图均衡化,以改进图像的视觉质量。
#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>using namespace cv;using namespace std;int main() { // 加载图像 Mat image = imread("D://lena.png", IMREAD_GRAYSCALE); // 以灰度模式加载图像 if (image.empty()) { cout << "无法加载图像" << endl; return -1; } // 直方图均衡化 Mat equalized_image; equalizeHist(image, equalized_image); // 显示原始图像和均衡化后的图像 imshow("原始图像", image); imshow("直方图均衡化后的图像", equalized_image); waitKey(0); return 0;}
效果图:
为什么直方图均衡化前要将图像转化为灰度模式?
直方图均衡化通常用于灰度图像的处理,而不是彩色图像,主要有以下原因:
简化处理:直方图均衡化是一种非常基础和常见的图像增强技术。在灰度图像中,每个像素只有一个灰度值,因此处理相对简单。处理彩色图像时,需要对每个通道分别进行直方图均衡化,增加了复杂性。
直方图均衡化的原理:直方图均衡化的核心思想是通过重新分配像素值来拉伸和扩展图像的像素值范围,以使图像的直方图更加均匀。在灰度图像中,这意味着调整图像的灰度级别,使其在0到255之间更均匀地分布。在彩色图像中,需要对每个通道分别进行这种操作。
彩色信息冗余:彩色图像包含了更多的信息,包括颜色和亮度信息。在某些情况下,只需对亮度信息进行均衡化,因为颜色信息可能并不需要改变。通过将彩色图像转换为灰度图像,可以更好地控制均衡化的效果,以保持图像的整体外观。
常见应用:直方图均衡化常用于医学图像处理、计算机视觉和图像增强等应用中,而这些应用通常使用灰度图像。因此,在这些场景下,直方图均衡化通常直接应用于灰度图像以改善对比度。
尽管直方图均衡化通常用于灰度图像,但也存在用于彩色图像的扩展方法,例如对亮度通道进行均衡化而保持色彩信息不变。这类方法通常称为彩色直方图均衡化或色彩均衡化。在这种情况下,处理的是亮度信息,而色彩信息得到保留。
八、自适应直方图均衡化
#include <opencv2/opencv.hpp>#include <opencv2/highgui/highgui.hpp>using namespace cv;using namespace std;int main() { // 加载图像 Mat image = imread("D://lena.png", IMREAD_GRAYSCALE); // 以灰度模式加载图像 if (image.empty()) { cout << "无法加载图像" << endl; return -1; } // 自适应直方图均衡化 Mat adaptive_equalized_image; equalizeHist(image, adaptive_equalized_image); // 显示原始图像和自适应直方图均衡化后的图像 imshow("原始图像", image); imshow("自适应直方图均衡化后的图像", adaptive_equalized_image); waitKey(0); return 0;}
在这个示例中,我们加载了一幅图像,并使用 IMREAD_GRAYSCALE
模式将其加载为灰度图像。然后,我们使用 equalizeHist
函数执行自适应直方图均衡化,将图像的对比度增强。
自适应直方图均衡化是一种改进的直方图均衡化方法,它在不同图像区域上分别应用均衡化,以应对光照差异。这个示例演示了如何使用OpenCV来实现自适应直方图均衡化,以改进图像的视觉质量。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
本文链接:https://www.kjpai.cn/gushi/2024-04-25/162194.html,文章来源:网络cs,作者:胡椒,版权归作者所有,如需转载请注明来源和作者,否则将追究法律责任!
上一篇:C#自定义数据类型-结构体详解
下一篇:返回列表