OpenCV中图像的变量是cv::Mat类型,是一个包含像素数据的矩阵,对于灰度图像素是一个8位无符号数,彩色图像是一个三元数,表示三个颜色通道(OpenCV默认使用BGR的通道顺序)。
存取像素
image.at<uchar>(y,x) = value;
image.at<cv::Vec3b>(y,x)[channel] = value;
//image是cv::Mat类的对象
//channel表明颜色通道号
成员函数at(int y,int x)返回相应数据结构的引用,故可以提取像素也可以修改像素,但前提是使用时必须知道图像的数据类型。
在实现知道图像数据类型的情况下,可以使用cv::Mat_来精简代码,该类重载了操作符(),允许通过它直接存取矩阵元素。
假设有一个灰度图,用cv::Mat_可以这样写
cv::Mat_<uchar> im2 = image;
im2(100,50) = 0;
Exp1 给图像添加椒盐噪点
void salt(cv::Mat &src,int n)
{
for(int k=0;k < n;k++)
{
int i = qrand()%src.rows;
int j = qrand()%src.cols;
if(src.channels() == 1)
src.at<uchar>(i,j) = 255;
else if(src.channels() == 3)
{
src.at<cv::Vec3b>(i,j)[0] = 255;
src.at<cv::Vec3b>(i,j)[1] = 255;
src.at<cv::Vec3b>(i,j)[2] = 255;
}
}
}
遍历图像
1.使用指针遍历图像
cv::Mat 提供ptr函数可以获得图像任意行的首地址。ptr函数是一个模板函数,使用前需要知道图像的数据类型。
//获取图像第j行的首地址
uchar *data = image.ptr<uchar>(j);
由于图像数据缓存区对应一个W*H的矩阵,每个元素又是由n个uchar构成(n为图像颜色通道数),所以一个宽W,高H的图像数据是一个大小由W*H*n个uchar构成的内存块。
/*遍历图像*/
int r = image.rows; //行数
int c = image.cols*image.channels(); //每行元素个数
uchar *data = NULL;
for(int i=0;i<r;i++)
{
data = image.ptr<uchar>(i);
for(int j=0;j<c;j++)
{
data[j].... // 操作像素
}
}
2.使用迭代器遍历图像
OpenCV为cv::Mat提供了与STL迭代器兼容的迭代器。
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>(); //初始位置迭代器
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>(); //终止位置迭代器
for(;it!=itend;++it)
{
//操作像素
(*it)[0]...
(*it)[1]...
(*it)[2]...
}
Exp2 对图像缩减颜色
/*将RGB空间划分为同等大小的格子,将每个颜色替代为它所在格子的的中心对应的颜色*/
//指针操作
void colorReduce(const cv::Mat &src,cv::Mat &dst,int div=64)
{
int c = src.cols*src.channels();
int r = src.rows;
if(dst.empty())
{
dst.create(r,c,src.type());
dst = src.clone();
}
const uchar *dataIn = NULL;
uchar *dataOut = NULL;
for(int i=0;i<r;i++)
{
dataIn = src.ptr<uchar>(i);
dataOut = dst.ptr<uchar>(i);
for(int j=0;j<c;j++)
{
dataOut[j] = dataIn[j]/div*div + div/2;
}
}
}
//迭代器
void colorReduce(const cv::Mat &src,,int div=64)
{
cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
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;
}
}