图像灰度化的目的是为了简化矩阵,提高运算速度。
彩色图像中的每个像素颜色由R、G、B三个分量来决定,而每个分量的取值范围都在0-255之间,这样对计算机来说,彩色图像的一个像素点就会有256*256*256=16777216种颜色的变化范围!
而灰度图像是R、G、B分量相同的一种特殊彩色图像,对计算机来说,一个像素点的变化范围只有0-255这256种。
假设我们现在有一幅彩色图像,但是我们现在想得到它的灰度图,应该怎么做呢?
Opencv自带了这种转换函数,
我们可以在用imread()函数读取时设置第二个参数为0,就可以直接得到彩色图像的灰度图;
Mat src0 = imread("C:\\Users\\32498\\Pictures\\16.png",0);
也可以先把彩色图像读进来,然后使用cvtColor函数进行转换得到它的灰度图。
Mat src = imread("C:\\Users\\32498\\Pictures\\16.png");
cvtColor(src, cvt_gray_image,COLOR_BGR2GRAY);
也可以使用心理学公式GRAY=0.299*R+0.587*G+0.114*B自己编写一个转换函数,其中的加权系数是根据人眼的亮度感知系统调节出来的参数。
void ConvertRGB2GRAY(const Mat& image, Mat& imageGray)
{
if (!image.data || image.channels() != 3)
{
return;
}
//创建一张单通道的灰度图像
imageGray = Mat::zeros(image.size(), CV_8UC1);
//取出存储图像像素的数组的指针
uchar* pointImage = image.data;
uchar* pointImageGray = imageGray.data;
//取出图像每行所占的字节数
size_t stepImage = image.step;
size_t stepImageGray = imageGray.step;
for (int i = 0; i < imageGray.rows; i++)
{
for (int j = 0; j < imageGray.cols; j++)
{
//opencv的通道顺序是BGR,而不是我们常说的RGB顺序
pointImageGray[i * stepImageGray + j] =
(uchar)(0.114 * pointImage[i * stepImage + 3 * j] +
0.587 * pointImage[i * stepImage + 3 * j + 1] +
0.299 * pointImage[i * stepImage + 3 * j + 2]);
}
}
}
在多通道图像中,比如RGB图像是这样保存的
int widthStep; /* 排列的图像行大小,以字节为单位 */
int nChannels; /* 大多数OPENCV函数支持1,2,3 或 4 个通道 */
图像矩阵是一个二维数组,不论是灰度图像还是彩色图像,在计算机内存中都是以一维数组的形式存储的。用Mat存储一幅图像时,若图像在内存中是连续存储的(Mat对象的isContinuous == true),则可以将图像的数据看成是一个一维数组,而其data(uchar*)成员就是指向图像数据的第一个字节的,因此可以用data指针访问图像的数据。
如果想要遍历其中的元素,可以使用指针的遍历方法
uchar* data=(uchar *)img->imageData;
int step = img->widthStep/sizeof(uchar);
int channels = img->nChannels;
uchar *b,*g,*r;
for(int i=0;iheight;i++)
for(int j=0;jwidth;j++){
*b=data[i*step+j*chanels+0];
*g=data[i*step+j*chanels+1];
*r=data[i*step+j*chanels+2];
}
下面我们来看一看用这几种方法进行灰度转化的结果
#include
using namespace cv;
void ConvertRGB2GRAY(const Mat& image, Mat& imageGray);
int main()
{
Mat src = imread("C:\\Users\\32498\\Pictures\\16.png");
Mat src0 = imread("C:\\Users\\32498\\Pictures\\16.png",0);
Mat grayImage;
Mat cvt_gray_image;
//读入的彩色图
namedWindow("origin_image", WINDOW_NORMAL);
imshow("origin_image", src);
//opencv使用imread函数读入彩色图,设置第二个参数为0得到的灰度图
namedWindow("opencv_image", WINDOW_NORMAL);
imshow("opencv_image", src0);
//使用公式GRAY=0.299*R+0.587*G+0.114*B
ConvertRGB2GRAY(src, grayImage);
namedWindow("my_gray_image", WINDOW_NORMAL);
imshow("my_gray_image", grayImage);
//先读入彩色图,然后使用cvtColor函数进行灰度转化
cvtColor(src, cvt_gray_image,COLOR_BGR2GRAY);
namedWindow("cvt_gray_image", WINDOW_NORMAL);
imshow("cvt_gray_image", cvt_gray_image);
waitKey(0);
return 0;
}
void ConvertRGB2GRAY(const Mat& image, Mat& imageGray)
{
if (!image.data || image.channels() != 3)
{
return;
}
//创建一张单通道的灰度图像
imageGray = Mat::zeros(image.size(), CV_8UC1);
//取出存储图像像素的数组的指针
uchar* pointImage = image.data;
uchar* pointImageGray = imageGray.data;
//取出图像每行所占的字节数
size_t stepImage = image.step;
size_t stepImageGray = imageGray.step;
for (int i = 0; i < imageGray.rows; i++)
{
for (int j = 0; j < imageGray.cols; j++)
{
//opencv的通道顺序是BGR,而不是我们常说的RGB顺序
pointImageGray[i * stepImageGray + j] =
(uchar)(0.114 * pointImage[i * stepImage + 3 * j] +
0.587 * pointImage[i * stepImage + 3 * j + 1] +
0.299 * pointImage[i * stepImage + 3 * j + 2]);
}
}
}