OpenCV坐标系与操作像素的四种方法
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
OpenCV坐标系与操作像素的四种⽅法
像素是图像的基本组成单位,熟悉了如何操作像素,就能更好的理解对图像的各种处理变换的实现⽅式了。
1.at⽅法
第⼀种操作像素的⽅法是使⽤“at”,如⼀幅3通道的彩⾊图像image的第i⾏j列的B、G、R分量分别表⽰为:
image.at<Vec3b>(i,j)[0];
image.at<Vec3b>(i,j)[1];
image.at<Vec3b>(i,j)[2];
⽽对于单通道的灰度图像就简单很多了:
image.at<uchar>(i,j);
这⾥要注意at中(i,j)的顺序表⽰的是第i⾏第j列,跟Point(i,j)和Rect(i,j)中表⽰第j⾏第i列是相反的,如果把这个搞混了,很容易导致内存异常,还不容易发现错误。
补充说明⼀下:opencv中坐标体系中的零点坐标定义为图⽚的左上⾓,X轴为图像矩形的上⾯那条⽔平线,从左往右;Y轴为图像矩形左边的那条垂直线,从上往下。
在Point(x,y)和Rect(x,y)中,第⼀个参数x代表的是元素所在图像的列数,第⼆个参数y代表的是元素所在图像的⾏数,⽽在at(x,y)中是相反的。
演⽰程序如下:
#include<iostream>
#include<core/core.hpp>
#include<highgui/highgui.hpp>
using namespace cv;
using namespace std;
int main()
{
Mat image(Size(500,500),CV_8UC3);
image.at<Vec3b>(100,250)[0]=0;
image.at<Vec3b>(100,250)[1]=0;
image.at<Vec3b>(100,250)[2]=255;
putText(image,"at(100,250) is Here!",Point(250,100),0,0.7,Scalar(255,0,0));
image.at<Vec3b>(Point(100,250))[0]=0;
image.at<Vec3b>(Point(100,250))[1]=0;
image.at<Vec3b>(Point(100,250))[2]=255;
putText(image,"at(Point(100,250)) is Here!",Point(100,250),0,0.7,Scalar(255,0,0));
imshow("Test Function at",image);
waitKey();
return 0;
}
2.⾏指针⽅法
⾏指针⽅法的思路是获取图像每⼀⾏的⾸地址的指针,把每⼀⾏当成⼀个循环对象,逐⾏遍历所有的像素。
例如对于⼀个X⾏、Y列的图像image,每⾏需要循环的数量是Y*image.channels(),channels表⽰的是图像的通道,对于灰度图像channels为1,对于彩⾊RGB图
像,channels为3。
3.指针⽅法
对于硬件处理芯⽚来说,如果图像每⾏的长度是4或8的倍数的话,运算起来会更加快速,如果不是4或8的倍数的话,在运算前图像⾏长度会被补充⾄固定值。
“指针⽅法”是⼀种可以⾼效遍历图像的⽅式,但是只能针对没有经过填充的连续图像,所以在使⽤指针⽅法之前需要先判断图像有没有经过填充,是否连续性。
判断的⽅法很简单,使⽤Mat的成员函数isContinuous来判断,若返回值为真的话,说明图像是连续的,可以应⽤⾏指针的⽅法遍历像素。
4.迭代⽅法
使⽤迭代器遍历图像集合中的各个元素,相⽐前3中⽅法,这个⽅法简直可以永简单快捷的“⽆脑操作”来形容,你只需获取集合中元素的⾸地址以及元素的终⽌位置,剩下的⼯作交给迭代器来完成就可以了,并且我们不⽤关⼼集合的数据类型,指针总是逐次访问下⼀地址,直到指针到达终⽌元素位置。
不过其缺点也是显⽽易见的:不容易指定访问集合中某⼀个位置的元素。
话不多少,下边给出⼀个完整的四种访问图像像素⽅法的⽰例,并对每种⽅法的耗时作对⽐,包括opencv本⾝的Copy⽅法;当然五种⽅法实现的是同⼀个简单的功能——通过逐个遍历⼀幅图像的每⼀个像素,复制该图像到另⼀Mat对象。
#include<iostream>
#include<core/core.hpp>
#include<highgui/highgui.hpp>
using namespace cv;
using namespace std;
//At⽅法
double CopyImageByAt(Mat originalImage, Mat &targetImage);
//⾏指针⽅法
double CopyImageByRowPtr(Mat originalImage, Mat &targetImage);
//指针⽅法
double CopyImageByPtr(Mat originalImage, Mat &targetImage);
//迭代⽅法
double CopyImageByIterator(Mat originalImage, Mat &targetImage);
//Opencv⽅法
double CopyFun(Mat originalImage, Mat &targetImage);
int main()
{
//读⼊图⽚,注意图⽚路径
Mat image=imread("D:\\Picture\\lena.jpg",1);
//图⽚读⼊成功与否判定
if(!image.data)
{
cout<<"you idiot!where did you hide lena!"<<endl;
//等待按键
system("pause");
return -1;
}
imshow("原始图像",image);
//输出图像
Mat targetImage(image.size(),image.type());
cout<<endl<<"At⽅法耗时:"<<CopyImageByAt(image,targetImage)<<endl;
imshow("At⽅法",targetImage);
cout<<endl<<"⾏指针⽅法耗时:"<<CopyImageByRowPtr(image,targetImage)<<endl;
imshow("⾏指针⽅法",targetImage);
cout<<endl<<"指针⽅法耗时:"<<CopyImageByPtr(image,targetImage)<<endl;
imshow("指针⽅法",targetImage);
cout<<endl<<"迭代⽅法耗时:"<<CopyImageByIterator(image,targetImage)<<endl;
imshow("迭代⽅法",targetImage);
cout<<endl<<"OpenCV Copy⽅法耗时:"<<CopyFun(image,targetImage)<<endl;
imshow("Copy⽅法",targetImage);
waitKey();
return 0;
}
//使⽤at⽅法实现逐个像素复制
double CopyImageByAt(Mat originalImage, Mat &targetImage)
{
double now=getTickCount();
//⾏
int rows=originalImage.rows;
//列
int cols=originalImage.cols;
for(int i=0;i<rows;i++)
{
for(int j=0;j<cols;j++)
{
//若是灰度图像应使⽤如下表⽰:
//targetImage.at<uchar>(i,j)=originalImage.at<Vec3b>(i,j);
targetImage.at<Vec3b>(i,j)[0]=originalImage.at<Vec3b>(i,j)[0];
targetImage.at<Vec3b>(i,j)[1]=originalImage.at<Vec3b>(i,j)[1];
targetImage.at<Vec3b>(i,j)[2]=originalImage.at<Vec3b>(i,j)[2];
}
}
double end=getTickCount();
//返回⽅法耗时
return (end-now)/getTickFrequency();
}
//使⽤访问每⾏⾸指针⽅法实现像素复制
double CopyImageByRowPtr(Mat originalImage, Mat &targetImage) {
double now=getTickCount();
//⾏
int rows=targetImage.rows;
//每⾏总元素数量,此处图像为3通道
int totalNum=targetImage.cols*targetImage.channels();
for(int i=0;i<rows;i++)
{
//data1指向⽬标图像第i⾏的⾸元素
uchar *data1=targetImage.ptr<uchar>(i);
////data2指向原始图像第i⾏的⾸元素
uchar *data2=originalImage.ptr<uchar>(i);
for(int j=0;j<totalNum;j++)
{
//遍历每⾏所有元素
data1[j]=data2[j];
}
}
double end=getTickCount();
//返回⽅法耗时
return (end-now)/getTickFrequency();
}
//⽆扩充的图像,采⽤指针⽅法逐个像素复制
double CopyImageByPtr(Mat originalImage, Mat &targetImage)
{
double now=getTickCount();
//⾏
int rows=targetImage.rows;
//每⾏总元素数量,此处图像为3通道
int totalNum=targetImage.cols*targetImage.channels();
//判断图像数据是否连续
if(originalImage.isContinuous())
{
totalNum*=rows;
rows=1;
}
//外层循环只执⾏⼀次
for(int i=0;i<rows;i++)
{
uchar *data1=targetImage.ptr<uchar>(i);
uchar *data2=originalImage.ptr<uchar>(i);
for(int j=0;j<totalNum;j++)
{
data1[j]=data2[j];
}
}
double end=getTickCount();
//返回⽅法耗时
return (end-now)/getTickFrequency();
}
//使⽤迭代器遍历逐个像素复制
double CopyImageByIterator(Mat originalImage, Mat &targetImage) {
double now=getTickCount();
//获取起始位置迭代器
Mat_<Vec3b>::iterator itBegin1=targetImage.begin<Vec3b>();
Mat_<Vec3b>::iterator itBegin2=originalImage.begin<Vec3b>();
//获取终⽌位置迭代器
Mat_<Vec3b>::iterator itEnd1=targetImage.end<Vec3b>();
Mat_<Vec3b>::iterator itEnd2=originalImage.end<Vec3b>();
for(;itBegin1!=itEnd1;++itBegin1)
{
(*itBegin1)[0]=(*itBegin2)[0];
(*itBegin1)[1]=(*itBegin2)[1];
(*itBegin1)[2]=(*itBegin2)[2];
++itBegin2;
}
double end=getTickCount();
//返回⽅法耗时
return (end-now)/getTickFrequency();
}
//OpenCV Copy⽅法实现图像复制
double CopyFun(Mat originalImage, Mat &targetImage)
{
double now=getTickCount();
originalImage.copyTo(targetImage);
double end=getTickCount();
//返回⽅法耗时
return (end-now)/getTickFrequency();
}
可见,指针⽅法是⾼效快捷访问像素的⾸选⽅法,然⽽跟opencv的Copy⽅法相⽐,还是弱爆了……。