OpenCv学习笔记(六)----图像空间缩减,OpenCv中的计时函数和OpenCv中操作图像单个像素点的方法

(一)我们将探索以下问题的答案:
	1--如何遍历图像中的每一个像素点?
	2--OpenCv的矩阵值是如何存储的?
	3--如何测试我们所实现算法的性能?
	4--查找表是什么?为什么要用它?
(二)图像矩阵是如何存储在内存之中的?
	1--图像矩阵的大小取决于我们使用的颜色模型,确切的说,取决于所用通道数。如果是灰度图像,矩阵就会像这样:

2--而对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等,例如,RGB颜色模型的矩阵:

3--在这里,我们需要注意的是:子列的通道顺序是反过来的,是BGR,而不是我们数字图像书中常
	   说的RGB.很多情况下,因为内存够大,可以实现连续存储。连续存储可以提高图像的扫描速度。
(三)颜色空间的缩减
    如果矩阵元素存储的是单通道像素,使用C或C++的无符号字符类型,那么像素可能有256个不同的值.
但若是三通道图像,这种存储格式的颜色数就太多了(确切的说,有一千六百多万种).用如此之多的颜色
可能会对我们算法的性能造成严重的影响。其实,有时候,我们仅适用这些颜色的一小部分,就足以达到
同样的效果。
    这种情况下,常用的一种方法就是【颜色空间缩减】.其做法是:将现有颜色值除以某个输入值,以获
得较少的颜色数。例如:0到9可取新值0;10到19取值1,以此类推。
    uchar(无符号字符,即0到255之间取值的数)类型除以int值,结果仍为char。因为结果是char类型的,
所以,求出来小数也要向下取整。利用这一点,刚才提到在uchar定义域中进行颜色缩减运算就可以表达
为下列形式:
       Inew=Iold/10*10;
	这样的话,简单的颜色缩减算法就可以由以下两步组成:
		1--遍历图像矩阵中的每一个像素
		2--对像素应用上述公式
	值得注意的是,我们这里用到了除法和乘法运算,而这种运算又特别费时,所以,我们应尽可能用代
价较低的加减,赋值等运算替换他们。
(四)计时函数
	1--OpenCv为我们提供了两个简单的计时函数---getTickCount()和getTickFrequency()
	2--getTickCount()函数---返回CPU自某个事件以来走过的----时钟周期数
	3--getTickFrequency()函数返回CUP一秒钟所走的时钟周期数。这样,我们就可以轻松的以秒为单位
	   对某运算计时
	这两个函数组合起来的使用如下所示:
	//【5】记录其实时间
	double timeStartPtr=static_cast<double>(getTickCount());
	//【6】调用颜色空间缩减函数
	colorReducePtr(srcImg,dstImgPtr,32);
	//【7】计算运行时间,并输出
	double timeExpensePtr=((double)getTickCount()-timeStartPtr)/getTickFrequency();
	cout<<"【1】ptr指针操作像素,此方法运行时间为---------->"<<timeExpensePtr<<endl;
(五)访问图像中像素的三类方法
    任何图像处理算法。都是从操作每个像素开始的。即使我们不会使用OpenCv提供的各种图像
处理函数,只要我们了解图像处理算法的基本原理,也可以写出具有相同功能的程序.在OpenCv中,
提供了三种访问每个像素的方法:
	1--指针访问----------ptr<>()函数
	2--动态地址计算------at<>()函数
	3--迭代器iterator
	这三种方法,在访问速度上略有差异,指针比较快。
/*********************************************************************************************
程序功能:
        图像像素点操作的三种常用方法的介绍:
			1--ptr<>()------Mat类中的模板函数--可以得到图像中任意一行的--------首地址
			2--at<>()-------Mat类中的模板函数--可以得到图像中任意指定元素的----地址
			3--Iterator-----迭代器操作像素
编写环境:
        OpenCv2.4.8+VS2010
地点时间:
        陕西师范大学 2016.4.25
作者信息:
        九月
**********************************************************************************************/
/********************************【头文件.命名空间包含部分】**********************************/
#include<opencv2/core/core.hpp>
#include<highgui/highgui.hpp>
#include<iostream>

using namespace std;
using namespace cv;

/***************************************【全局函数声明部分】**********************************/

void colorReducePtr(Mat& inputImg,Mat& outputImg,int div);
void colorReduceAt(Mat& inputImg,Mat& outputImg,int div);
void colorReduceIterator(Mat& inputImg,Mat& outputImg,int div);
void matHelp(Mat& inputImg);

/********************************************【main函数】*************************************/
int main(int argc,char** argv)
{
	//【1】创建Mat类对象的信息头
	Mat srcImg;
	//【2】创建矩阵体,载入图片并显示
	srcImg=imread("D:\\scenery.png",CV_LOAD_IMAGE_COLOR);
	imshow("原始图像",srcImg);

	//【3】显示图像的基本信息
	matHelp(srcImg);

	//【4】按照原图的参数规格,来创建效果图
	Mat dstImgPtr;
	dstImgPtr.create(srcImg.rows,srcImg.cols,srcImg.type());

	Mat dstImgAt;
	dstImgAt.create(srcImg.rows,srcImg.cols,srcImg.type());

	Mat dstImgIterator;
	dstImgIterator.create(srcImg.rows,srcImg.cols,srcImg.type());


	//【5】记录其实时间
	double timeStartPtr=static_cast<double>(getTickCount());
	//【6】调用颜色空间缩减函数
	colorReducePtr(srcImg,dstImgPtr,32);
	//【7】计算运行时间,并输出
	double timeExpensePtr=((double)getTickCount()-timeStartPtr)/getTickFrequency();
	cout<<"【1】ptr指针操作像素,此方法运行时间为---------->"<<timeExpensePtr<<endl;


	double timeStartAt=static_cast<double>(getTickCount());
	colorReduceAt(srcImg,dstImgAt,32);
	double timeExpenseAt=((double)getTickCount()-timeStartAt)/getTickFrequency();
	cout<<"【2】at()动态地址计算,此方法运行时间为--------->"<<timeExpenseAt<<endl;

	double timeStartIterator=static_cast<double>(getTickCount());
	colorReduceIterator(srcImg,dstImgIterator,32);
	double timeExpenseIterator=((double)getTickCount()-timeStartIterator)/getTickFrequency();
	cout<<"【3】Iterator迭代器操作像素,此方法运行时间为--->"<<timeExpenseIterator<<endl;


	//【8】显示效果图
	imshow("效果图",dstImgIterator);

	waitKey(0);
}

/***************************************************************************************************
函数功能:
       利用Mat类中的成员函数和数据成员提供一些编程的参考信息
函数参数:
       Mat& inputImg---输入图像的引用类型
函数返回值:
       无
***************************************************************************************************/
void matHelp(Mat& inputImg)
{
	//【1】图像的行数
	int nRows=inputImg.rows;
	//【2】图像的列数
	int nCols=inputImg.cols;
	//【3】图像的高度
	int nHeight=inputImg.size().height;
	//【4】图像的高度
	int nWidth =inputImg.size().width;
	//【5】图像的通道个数,彩色图像3通道,灰度图像单通道
	int nChannels=inputImg.channels();
	//【6】图像中的一个像素点位置所含的字节数
	int nElemByte=inputImg.elemSize();
	//【7】图像中的一行,所含有的字节数
	int nColByte=inputImg.step;
	//【8】图像中像素的总个数
	int elemTotal=inputImg.total();
	//【9】返回图像的列数*行数---返回图像的尺寸大小
	cout<<"【1】图像的行数-------------------------------------------------->"<<nRows<<endl;
    cout<<"【2】图像的列数-------------------------------------------------->"<<nCols<<endl;
	cout<<"【3】图像的高度-------------------------------------------------->"<<nHeight<<endl;
	cout<<"【4】图像的高度-------------------------------------------------->"<<nWidth<<endl;
	cout<<"【5】图像的通道个数,彩色图像3通道,灰度图像单通道----------------->"<<nChannels<<endl;
	cout<<"【6】图像中的一个像素点位置所含的字节数-------------------------->"<<nElemByte<<endl;
	cout<<"【7】图像中的一行,所含有的字节数--------------------------------->"<<nColByte<<endl;
	cout<<"【8】图像中像素的总个数------------------------------------------>"<<elemTotal<<endl;
	cout<<"【9】返回图像的列数*行数---返回图像的尺寸大小-------------------->"<<inputImg.size()<<endl;
	cout<<"【10】此外,Mat类中还有比如:at<>(),ptr<>(),data等重要的成员"<<endl;
}
/***************************************************************************************************
函数功能:
       1--缩减颜色空间
	   2--显示使用【指针】访问像素
	   3--显示Mat类的模板函数ptr<>()---ptr函数可以得到图像任意一行的---首地址
函数参数:
       Mat& inputImg-----输入图像
	   Mat& outputImg----输出图像
	   int  div----------颜色空间缩减倍数
函数返回值:
       无
***************************************************************************************************/
void colorReducePtr(Mat& inputImg,Mat& outputImg,int div)
{
	outputImg=inputImg.clone();                       //复制图像的信息头和矩阵体到临时变量----->深复制
	int nRows=outputImg.rows;                         //行数
	int nCols=outputImg.cols*outputImg.channels();    //列数*通过数=每一行的元素个数

	//双循环,遍历所有的像素值
	for(int i=0;i<nRows;i++)
	{
		uchar* data=outputImg.ptr<uchar>(i);          //获取第i行的首地址
		for(int j=0;j<nCols;j++)
		{
			/***************【开始处理每个像素】****************/
			data[j]=data[j]/div*div+div/2;
		}//for j
	}//for i
}
/***************************************************************************************************
函数功能:
       1--缩减颜色空间
	   2--显示使用【指针】访问像素
	   3--显示Mat类的模板函数at<>()---at函数可以返回指定数组元素的地址
函数参数:
       Mat& inputImg-----输入图像
	   Mat& outputImg----输出图像
	   int  div----------颜色空间缩减倍数
函数返回值:
       无
***************************************************************************************************/
void colorReduceAt(Mat& inputImg,Mat& outputImg,int div)
{
	outputImg=inputImg.clone();                       //复制图像的信息头和矩阵体到临时变量----->深复制
	int nRows=outputImg.rows;                         //行数
	int nCols=outputImg.cols;                         //列数
	
	//双循环,遍历所有的像素值
	for(int i=0;i<nRows;i++)
	{
		for(int j=0;j<nCols;j++)
		{
			/***************【开始处理每个像素】****************/
			outputImg.at<Vec3b>(i,j)[0]=outputImg.at<Vec3b>(i,j)[0]/div*div+div/2;//蓝色通道
			outputImg.at<Vec3b>(i,j)[1]=outputImg.at<Vec3b>(i,j)[1]/div*div+div/2;//绿色通道
			outputImg.at<Vec3b>(i,j)[2]=outputImg.at<Vec3b>(i,j)[2]/div*div+div/2;//红色通道
		}//for j
	}//for i
}
/***************************************************************************************************
函数功能:
       1--缩减颜色空间
	   2--显示使用【指针】访问像素
	   3--用迭代器操作像素
函数参数:
       Mat& inputImg-----输入图像
	   Mat& outputImg----输出图像
	   int  div----------颜色空间缩减倍数
函数返回值:
       无
***************************************************************************************************/
void colorReduceIterator(Mat& inputImg,Mat& outputImg,int div)
{
	outputImg=inputImg.clone();                                 //复制图像的信息头和矩阵到outputImg这个临时变量中
	//获取迭代器
	Mat_<Vec3b>::iterator itBegin=outputImg.begin<Vec3b>();          //初始位置
	Mat_<Vec3b>::iterator inEnd  =outputImg.end<Vec3b>();            //终止位置
	
	//存取彩色图像像素
	for(;itBegin!=inEnd;++itBegin)
	{
		(*itBegin)[0]=(*itBegin)[0]/div*div+div/2;
		(*itBegin)[1]=(*itBegin)[1]/div*div+div/2;
		(*itBegin)[2]=(*itBegin)[2]/div*div+div/2;
	}
}

 


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值