指针访问
ptr操作时通过指针偏移的方式进行像素的查找、遍历和修改的,因此效率相对较高。
uchar pixel_value = Mat.ptr<uchar>(row)[col]; //获取某个像素值
Mat.ptr<uchar>(row)[col] = pixel_value; //修改某个像素值,也可以直接赋值为0,1,...,255
对于彩色图:row = Mat.rows; col = Mat.cols * Mat.channels();
对于灰度图:row = Mat.rows; col = Mat.cols;
uchar * datarow = Mat.ptr<uchar>(row); //获取某行的首地址
对于彩色图像也可以表示为:
Vec3b color_value = Mat.ptr<Vec3b>(row)[col];
Mat.ptr<Vec3b>(row)[col] = color_value;
实现示例:
Mat srcImage = Mat(7, 15, CV_8UC1,Scalar::all(0));
cout << "srcImage = " << endl << srcImage << endl;
namedWindow("原图",WINDOW_NORMAL);
imshow("原图", srcImage);
for (int i = 0; i < srcImage.rows; i++)
{
for (int j = 0; j < srcImage.cols; j++)
{
if (i==1 || i==5)
{
if ((j >= 1 && j <= 4) || (j >= 10 && j <= 13))
{
srcImage.ptr<uchar>(i)[j] = 255;
}
}
else if (i == 2 || i== 3 || i==4)
{
if (j>=1 && j<=13)
{
srcImage.ptr<uchar>(i)[j] = 255;
}
}
}
}
cout << "srcImage = " << endl << srcImage << endl;
namedWindow("修改图", WINDOW_NORMAL);
imshow("修改图", srcImage);
参考代码
//-----------------------------------【全局函数声明部分】-----------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div);
void ShowHelpText();
//--------------------------------------【main( )函数】---------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main( )
{
//【1】创建原始图并显示
Mat srcImage = imread("1.jpg");
imshow("原始图像",srcImage);
//【2】按原始图的参数规格来创建创建效果图
Mat dstImage;
dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());//效果图的大小、类型与原图片相同
ShowHelpText();
//【3】记录起始时间
double time0 = static_cast<double>(getTickCount());
//【4】调用颜色空间缩减函数
colorReduce(srcImage,dstImage,32);
//【5】计算运行时间并输出
time0 = ((double)getTickCount() - time0)/getTickFrequency();
cout<<"\t此方法运行时间为: "<<time0<<"秒"<<endl; //输出运行时间
//【6】显示效果图
imshow("效果图",dstImage);
waitKey(0);
}
//---------------------------------【colorReduce( )函数】---------------------------------
// 描述:使用【指针访问:C操作符[ ]】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
//参数准备
outputImage = inputImage.clone(); //拷贝实参到临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputImage.cols*outputImage.channels(); //列数 x 通道数=每一行元素的个数
//双重循环,遍历所有的像素值
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;
// ----------【处理结束】---------------------
} //行处理结束
}
}
//-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV2版的第21个配套示例程序\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
printf("\n\n ----------------------------------------------------------------------------\n");
}
整个程序的主要部分在这里:
//双重循环,遍历所有的像素值
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;
// ----------【处理结束】---------------------
} //行处理结束
}
主要思想是:双重循环遍历所有的像素值。
行循环是rows,列循环是cols。
首先,进入行循环的时候,找到每一行的行首地址:uchar* data = outputImage.ptr<uchar>(i)。存进data指针里面。注意指针类型是uchar类型。
然后,进入列循环的时候,data[]数组的元素就是每一列的图像像素。直接使用数组的方式来访问元素即可。
就这两步。
下面看一下这个头指针ptr<uchar>():
函数原型是:
template<typename _Tp> inline _Tp* Mat::ptr(int y)
{
CV_DbgAssert( y == 0 || (data && dims >= 1 && (unsigned)y < (unsigned)size.p[0]) );
return (_Tp*)(data + step.p[0]*y);
}
返回的是_Tp类型的指针。指针指向的是data数据的行首地址。step.p[0]是第一行的首地址,step是步长,最后乘以y是指向y行的首地址。
指针访问图像像素值耗时测评
// Read_Modify_Piexl_Value.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include "pch.h"
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
Mat readPixelNormal(Mat img, Mat thres)
{
for (int i = 0; i < thres.rows; i++)
{
for (int j = 0; j < thres.cols; j++)
{
if (thres.at<uchar>(i, j) == 255)
img.at<Vec3b>(i, j) = Vec3b(197, 247, 254); //BGR彩色图像素值改变
}
}
return img;
}
Mat readPixelFast(Mat img, Mat thres)
{
for (int i = 0; i < thres.rows; i++)
{
uchar *ptr = thres.ptr<uchar>(i);
uchar *ptrColor = img.ptr<uchar>(i);
for (int j = 0; j < thres.cols; j++)
{
if (ptr[j] == 255)
{
ptrColor[j * 3] = 197;
ptrColor[j * 3 + 1] = 247;
ptrColor[j * 3 + 2] = 254;
}
}
}
return img;
}
int main()
{
Mat img = imread("S.png");
Mat gray, thres;
cvtColor(img, gray, COLOR_BGR2GRAY);
threshold(gray, thres, 0, 255, THRESH_BINARY | THRESH_OTSU);
double start = static_cast<double>(getTickCount());
//Mat result = readPixelNormal(img, thres);
Mat result = readPixelFast(img, thres);
double end = static_cast<double>(getTickCount());
double useTime = ((double)end - start) / getTickFrequency() * 1000;
cout << "use-time = " << useTime << "ms" << endl;
imwrite("res.png", result);
return 0;
}
传统方法 480ms[3M左右图片]
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;
int main()
{
Mat m_img = imread("D://vvoo/lena.jpg");
Mat src(m_img.rows, m_img.cols, CV_8UC1, Scalar(0));
cvtColor(m_img, src, CV_RGB2GRAY);
Mat dstImage(src.rows, src.cols, CV_8UC1, Scalar(0));
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
dstImage.data[i*dstImage.step + j] = sqrt((src.data[(i - 1)*src.step + j + 1]
+ 2 * src.data[i*src.step + j + 1]
+ src.data[(i + 1)*src.step + j + 1]
- src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- src.data[(i + 1)*src.step + j - 1])*(src.data[(i - 1)*src.step + j + 1]
+ 2 * src.data[i*src.step + j + 1] + src.data[(i + 1)*src.step + j + 1]
- src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- src.data[(i + 1)*src.step + j - 1]) + (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1]));
}
}
Mat grad_y(src.rows, src.cols, CV_8UC1, Scalar(0));
{
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
grad_y.data[i*grad_y.step + j] = abs((src.data[(i - 1)*src.step + j + 1]
+ 2 * src.data[i*src.step + j + 1]
+ src.data[(i + 1)*src.step + j + 1]
- src.data[(i - 1)*src.step + j - 1] - 2 * src.data[i*src.step + j - 1]
- src.data[(i + 1)*src.step + j - 1]));
}
}
}
Mat grad_x(src.rows, src.cols, CV_8UC1, Scalar(0));
{
for (int i = 1; i < src.rows - 1; i++)
{
for (int j = 1; j < src.cols - 1; j++)
{
grad_x.data[i*grad_x.step + j] = sqrt((src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1])* (src.data[(i - 1)*src.step + j - 1] + 2 * src.data[(i - 1)*src.step + j]
+ src.data[(i - 1)*src.step + j + 1] - src.data[(i + 1)*src.step + j - 1]
- 2 * src.data[(i + 1)*src.step + j]
- src.data[(i + 1)*src.step + j + 1]));
}
}
}
imshow("原图", src);
imshow("gradient", dstImage);
imshow("Vertical gradient", grad_y);
imshow("Horizontal gradient", grad_x);
waitKey(0);
return 0;
}
指针访问 耗时20ms[3M左右图片]
#include<iostream>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
Mat src = imread("facetest\\face0.jpg",0);
//GaussianBlur(src, src, Size(5,5), 0);
medianBlur(src,src,3);
Mat gx(src.size(),src.type(),Scalar(0));
Mat gy(src.size(), src.type(), Scalar(0));
Mat scharr_y(src.size(),src.type(),Scalar(0));
int Gx[9] = {-1,0,1,-2,0,2,-1,0,1};//gx方向的滤波模板
int Gy[9] = {1,2,1,0,0,0,-1,-2,-1};//gy方向的滤波模板
int scharrx [9]= {-3,0,3,-10,0,10,-3,0,3};
int scharry[9] = {-3,-10,-3,0,0,0,3,10,3};
if (!src.isContinuous())
{
cout << "不连续内存!!" << endl;
return 0;
}
//是连续内存用指针访问
uchar *px = gx.data;
uchar *py=gy.data;
uchar *s = src.data;
uchar *pscharr_y = scharr_y.data;
int sstep = src.step[0];
int xstep = gx.step[0];
int ystep = gy.step[0];
int scstep = scharr_y.step[0];
for (int i = 1; i <src. rows-1; i++)
{
for(int j=1;j<src.cols-1;j++)
{
py[i*ystep + j] = abs(Gy[0] * s[(i - 1)*sstep + j - 1]
+ Gy[1] * s[(i - 1)*sstep + j]
+Gy[2]*s[(i-1)*sstep+j+1]
+Gy[3]*s[i*sstep+j-1]
+Gy[4]*s[i*sstep+j]
+Gy[5]*s[i*sstep+j+1]
+Gy[6]*s[(i+1)*sstep+j-1]
+Gy[7] * s[(i + 1)*sstep + j ]
+Gy[8] * s[(i + 1)*sstep + j + 1]);
pscharr_y[i*scstep + j] = abs(scharry[0]*s[(i-1)*sstep+j-1]
+ scharry[1] * s[(i - 1)*sstep + j]
+ scharry[2] * s[(i - 1)*sstep + j + 1]
+ scharry[3] * s[i*sstep + j - 1]
+ scharry[4] * s[i*sstep + j]
+ scharry[5] * s[i*sstep + j + 1]
+ scharry[6] * s[(i + 1)*sstep + j - 1]
+ scharry[7] * s[(i + 1)*sstep + j]
+ scharry[8] * s[(i + 1)*sstep + j + 1]);
}
}
medianBlur(gy,gy,3);
medianBlur(scharr_y,scharr_y,3);
imshow("gy",gy);
imshow("scharr_y", scharr_y);
imshow("src",src);
waitKey(0);
}
参考文献
OpenCV、EmguCV和OpenCvSharp指针访问图像像素值耗时测评(附源码)
https://blog.csdn.net/Liuzhu_shusheng_DH/article/details/118558406?spm=1001.2014.3001.5502