BGR而不是RGB,内存足够大,可以以连续的方式存储行,所以这些行创建单个长行,所有内容都在一个接一个的位置。我们可以使用cv :: Mat :: isContinuous()函数判断。
像素访问速度的快慢,排序如下:
【1】使用LUT查找表访问图像像素,LUT方法通过映射关系表大大减少相关操作的时间复杂度,是最快的处理方式,常用于(替换、反转、赋值、阈值、二值化、灰度变换等)图像操作。
【2】使用isContinuous()访问图像像素,图像必须是连续的才能使用
【3】使用ptr<>(row)访问图像像素,速度最快
【4】使用data结合step访问图像像素
【5】使用ptr<>(row, col)访问图像像素
【6】使用迭代器iterator访问图像像素,相比用指针直接访问像素可能出现越界问题,迭代器绝对是非常安全
【7】使用at<>()访问图像像素,速度是最慢的,直观
int width = img.cols;
int height = img.rows;
int dims = img.channels();
//循环遍历像素读写
for(int row = 0; row < width;++row)
{
for(int col=0;col<height;++col )
{
if(dims==1)//黑白
{
img.at<uchar>(row, col) = saturate_cast<uchar>(img.at<uchar>(row, col) * 2);
}
if(dims==3)//彩图
{
img.at<Vec3b>(row, col)[0] = 255 - img.at<Vec3b>(row, col)[1];
img.at<Vec3b>(row, col)[1] = 255 - img.at<Vec3b>(row, col)[2];
img.at<Vec3b>(row, col)[2] = 255 - img.at<Vec3b>(row, col)[0];
}
}
}
for(int row=0;row<height;++row)
{
uchar*current_row = img.ptr<uchar>(row);
for(int col=0;col<width;++col)
{
if (dims == 1)
{
*current_row++ = 255 - *current_row;//current_row[]数组的首地址,每行第一个像素
}
if (dims == 3)
{
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
*current_row++ = 255 - *current_row;
}
}
}
参考博客:C/C++中 p++、++p、(*p)++、++*p的区别
或者
Mat outputImg= srcImg.clone();
int height = outputImg.rows;
int weight= outputImg.cols;
int colNum= outputImg.cols * outputImg.channels();//每一行元素个数
for (int row = 0; row < height ; ++row)//行循环
{
uchar *srcData = srcImg.ptr<uchar>(row);
uchar *outputData = outputImg.ptr<uchar>(row);
for (int col = 0; col < colNum;j++)//列循环
{
outputData[col] = saturate_cast<uchar>(255 - srcData[col]);
}
}
// 设置参数
dstImg = srcImg.clone(); // 设置目标图像与源图像一样
// 获取迭代器
Mat_<Vec3b>::iterator it = dstImg.begin<Vec3b>(); // 初始位置的迭代器
Mat_<Vec3b>::iterator itEnd = dstImg.end<Vec3b>(); // 终止位置的迭代器
// 存取彩色图像像素
for (; it != itEnd; it++) // 行循环
{
// 开始处理每个像素,在OpenCV中,存储顺序是BGR
(*it)[0] = (*it)[0] / div * div + div / 2;//blue通道
(*it)[1] = (*it)[1] / div * div + div / 2;//绿色通道
(*it)[2] = (*it)[2] / div * div + div / 2;//蓝色通道
}
dstImg = srcImg.clone(); // 设置目标图像与源图像一样
for (int row = 0; row < srcImg.rows; row++)
{
for (int col = 0; col < srcImg.cols; col++)
{
Vec3b* pData = srcImg.ptr<Vec3b>(row, col);//彩图
(*pData)[0] = 255 - (*pData)[0];
(*pData)[1] = 255 - (*pData)[1];
(*pData)[2] = 255 - (*pData)[2];
//这种方式也可以
//uchar* pData = m2.ptr<uchar>(row, col);
//*(pData + 0) = 255 - *(pData + 0);
//*(pData + 1) = 255 - *(pData + 1);
//*(pData + 2) = 255 - *(pData + 2);
}
}
成员函数step是返回该Mat对象一行所占的数据字节数。
成员函数data:uchar类型的指针,指向Mat数据矩阵的首地址
参考博客:https://blog.csdn.net/CxC2333/article/details/107735638
dstImg = srcImg.clone(); // 设置目标图像与源图像一样
uchar* pData = srcImg.data;
MatStep mst = srcImg.step;
for (int row = 0; row < srcImg.rows; row++)
{
for (int col = 0; col < srcImg.cols; col++)
{
*(pData + row * mst + col * srcImg.channels() + 0) = 255 - *(pData + row * mst + col * srcImg.channels() + 0);
*(pData + row * mst + col * srcImg.channels() + 1) = 255 - *(pData + row * mst + col * srcImg.channels() + 1);
*(pData + row * mst + col * srcImg.channels() + 2) = 255 - *(pData + row * mst + col * srcImg.channels() + 2);
}
}
图像行与行之间的存储可能是不连续的,进行像素值遍历,很大程度上造成数据指针移动的浪费。一般经过裁剪的Mat图像,都不再连续了,如cv::Mat crop_img = src(rect);crop_img 是不连续的Mat图像,如果想转为连续的,最简单的方法,就是将不连续的crop_img 重新clone()一份给新的Mat就是连续的了
int row = src.rows;
int col = src.cols;
cv::Mat dstImg = src.clone();
// 判断是否是连续图像,即是否有像素填充
if (src.isContinuous() && dstImg.isContinuous())
{
row = 1;
// 按照行展开
col = col * src.rows * src.channels();
}
// 遍历图像的每个像素
for (int i = 0; i < row; i++)
{
// 设定图像数据源指针及输出图像数据指针
const uchar* pSrcData = src.ptr<uchar>(i);
uchar* pResultData = temp.ptr<uchar>(i);
for (int j = 0; j < col; j++)
{
*pResultData++ = 255 - *pSrcData++;
}
}
int row = src.rows;
int col = src.cols;
Mat temp = src.clone();
// 建立LUT 反色table
uchar LutTable[256 * 3];
for (int i = 0; i < 256; ++i)
{
LutTable[i * 3] = 255 - i;
LutTable[i * 3+1] = 255 - i;
LutTable[i * 3+2] = 255 - i;
}
Mat lookUpTable(1, 256, CV_8UC3, LutTable);
// 应用索引表进行查找
LUT(src, lookUpTable, temp);
参考博客:【OpenCV】之LUT函数
在原图上src1操作,如果src2和src1逻辑操作为1,src1像素值不变,否则src1像素值为0
// 逻辑操作
Mat dst1, dst2, dst3;
bitwise_and(src1, src2, dst1);
bitwise_xor(src1, src2, dst2);
bitwise_or(src1, src2, dst3);
// 取反操作
Mat dst;
bitwise_not(src, dst);
Mat dst=~src;