1.用Mat.data获取到的指针类型默认为uchar*型的,而与矩阵中元素的数据类型无关。使用时要注意指针类型的转化。
2.灰度图Mat矩阵中的元素多数是uchar(CV_8UC1)型的,有时需要访问其中的单个元素(像素值)并用"cout<<"输出。需要注意的是,用"cout<<"输出char/uchar型数据时,输出的并不是数字数据,而是数字对应的ASCII码字符,若对应的字符不可打印,则显示输出为空。若要求输出数字数据,可使用类型强制转换后输出(如:cout<<(int)num<<endl;)。
3.Mat的各行数据在内存中都是连续存储的,但行与行之间的地址不一定连续。因此需要用哪行的数据,就最好先获得对应行的首地址(uchar* p = image.ptr<uchar>(i),获取第i行首地址)。
4.用imshow()显示Mat矩阵存储的图像信息时,若元素的数据类型是uchar(CV_8UC1)的,就默认有256(2的8次方)个灰度级;若元素的数据类型是用int(CV_32SC1)的,就默认有2147483647 (2的32次方)个灰度级。普通灰度图的灰度值都在0-255之间,在CV_8UC1下能够正常显示。要是将其数据类型转化为CV_32SC1的,0-255的灰度值在2147483647的尺度下就显得范围过窄且无限靠近于0,用imshow()显示时显示窗口就会一片黑暗。
颜色空间缩减:将现有颜色空间值除以某个输入值,以获得较少的颜色数。
访问图像中像素的三类方法:
1.指针访问:C操作符[];
2.迭代器 iterator;
3.动态地址计算
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace std;
using namespace cv;
//---------------【全局函数声明部分】---------------
// 描述:全局函数声明
//-----------------------------------------------
void colorReduce(Mat & inputImage, Mat & outputImage, int div);
//----------------【main()函数】----------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//---------------------------------------------------
int main()
{
// 创建原始图并显示
Mat srcImage = imread("D:\\photo\\花.png");
imshow("原始图像", srcImage);
// 按原始图的参数规格来创建效果图
Mat dstImage;
dstImage.create(srcImage.rows, srcImage.cols, srcImage.type());
// 记录起始时间
double time0 = static_cast<double>(getTickCount());
// 调用颜色空间缩减函数
colorReduce(srcImage, dstImage, 32);
//计算运行时间并输出
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "此方法运行时间为: " << time0 << "秒" << endl;
//显示效果图
imshow("效果图", dstImage);
waitKey(0);
return 0;
}
方法一:用指针访问像素
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone(); //复制实参到临时变量
int rowNumber = outputImage.rows;
int colNumber = outputImage.cols*outputImage.channels(); // 列数 × 通道 = 每一行元素的个数 (每行的像素值)
for (int i = 0; i < rowNumber; i++)
{
uchar * data = outputImage.ptr<uchar>(i); //获取第i行的首地址
for (int j = 0; j < colNumber; j++)
{
data[j] = data[j] / div * div + div / 2;
//*data++=*data/div*div+div/2; 与上式等价,用指针运算
}
}
}
方法二:用迭代器操作像素
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone(); //复制实参到临时变量
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); // 初始位置的迭代器
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>(); //z终止位置的迭代器
//存取彩色图像像素
for (; it != itend; ++it)
{
(*it)[0] = (*it)[0] / div*div + div / 2;
(*it)[1] = (*it)[1] / div*div + div / 2;
(*it)[2] = (*it)[2] / div*div + div / 2;
}
}
方法三:动态地址计算
void colorReduce(Mat & inputImage, Mat & outputImage, int div)
{
outputImage = inputImage.clone(); //复制实参到临时变量
int rowNumber = outputImage.rows; //列数
int colNumber = outputImage.cols; //行数
for (int i = 0; i < rowNumber; i++)
{
for (int j = 0; j < colNumber; j++)
{
outputImage.at<Vec3b>(i, j)[0] = outputImage.at<Vec3b>(i, j)[0] / div*div + div / 2;
outputImage.at<Vec3b>(i, j)[1] = outputImage.at<Vec3b>(i, j)[1] / div*div + div / 2;
outputImage.at<Vec3b>(i, j)[2] = outputImage.at<Vec3b>(i, j)[2] / div*div + div / 2;
}
}
}
对于一个包含彩色图像的Mat,会返回一个由三个8位数字组成的向量,因此OpenCV将此类型的向量定义为Vec3b,即由三个unsigned char组成的向量。