zoukankan      html  css  js  c++  java
  • 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方法相比,还是弱爆了……


  • 相关阅读:
    Mac下安装LNMP(Nginx+PHP5.6)环境
    MySQL中文全文检索
    关于Mysql模糊查询的优化-全文检索和Like的使用
    MySql全文索引
    为mysql数据库建立索引
    【高并发简单解决方案】redis队列缓存 + mysql 批量入库 + php离线整合
    PHP中利用redis实现消息队列处理高并发请求
    Windows下为PHP安装redis扩展
    Linux中postfix邮件服务器的搭建
    ELK日志分析系统(转)
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9412012.html
Copyright © 2011-2022 走看看