图像处理 遍历所有像素_图像处理 遍历 像素 处理 一定区域 - CSDN
  • OpenCV中图像遍历像素操作

    万次阅读 2016-04-27 15:09:03
    OpenCV中表示图像遍历一般有三种方式,即数组遍历、指针遍历、迭代器遍历

    OpenCV中表示图像的数据结构是cv::Mat,Mat对象本质上是一个由数值组成的矩阵。矩阵的每一个元素代表一个像素,对于灰度图像,像素是由8位无符号数来表示(0代表黑,255代表白);对于彩色图像,每个像素是一个三元向量,即由三个8位无符号数来表示三个颜色通道(Opencv中顺次为蓝、绿、红)。
    我们先来介绍下cv::Mat类的获取像素的成员函数at(),其函数原型如下:

    template<typename _Tp> _Tp& at(int i0, int i1);
    //由于Mat可以存放任意数据类型的元素,所以该函数是用模板函数来实现的
    //它本身不会进行任何数据类型转换,在调用的过程中需要指明像素的数据类型
    //即要与矩阵中的数据类型相匹配。如:
    img.at<uchar>(i,j)=255
    img.at<cv::Vec3b>(i,j)[0]=255

    在OpenCV中一般有四种图像遍历的方式,第一种自然是最平凡的数组遍历啦。为方便起见,以下所有的示例都是In-place变换操作。

    1、数组遍历

    在这里我们通过操作像素的办法来实现图像的镜像变换,即实现flip(img,img,1)的功能。代码如下:

    #include<iostream>
    #include<opencv2/core/core.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include<opencv2/highgui/highgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    void Flip(Mat &img)
    {
        int rows=img.rows;
        int cols=img.cols;
        for(int i=0; i<rows; i++)
        {
            for(int j=0; j<cols/2; j++)
            {
                uchar t;
                if(img.channels()==1)
                {
                    t=img.at<uchar>(i,j);
                    img.at<uchar>(i,j)=img.at<uchar>(i,cols-1-i);
                    img.at<uchar>(i,cols-1-i)=t;
                }
                else if(img.channels()==3)
                {
                    for(int k=0; k<3; k++)
                    {
                        t=img.at<Vec3b>(i,j)[k];
                        img.at<Vec3b>(i,j)[k]=img.at<Vec3b>(i,cols-1-j)[k];
                        img.at<Vec3b>(i,cols-1-j)[k]=t;
                    }
                }
            }
        }
    }
    
    int main()
    {
        Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
        imshow("First",img1);
        if(!img1.data)
        {
            cout<<"error! The image is not built!"<<endl;
            return -1;
        }
        Flip(img1);
        imshow("Second",img1);
        waitKey();
        return 0;
    }

    效果如下:
    这里写图片描述

    2、指针遍历

    OpenCV中cv::Mat类提供了成员函数ptr得到图像任意行的首地址。ptr函数是一个模板函数,其原型为:

    template<typename _Tp> _Tp* Mat::ptr(int i=0)

    在这里我们通过操作像素的办法来实现图像的水平反转,即实现flip(img,img,0)的功能。代码如下:

    #include<iostream>
    #include<opencv2/core/core.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include<opencv2/highgui/highgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    void Flip(Mat &img)
    {
        int rows=img.rows;
        int cols=img.cols*img.channels();
        for(int i=0; i<rows/2; i++)
        {
            uchar *p=img.ptr<uchar>(i);
            uchar *q=img.ptr<uchar>(rows-1-i);
            uchar t;
            for(int j=0; j<cols;j++)
            {
                t=*p;
                *p++=*q;
                *q++=t;
            }
        }
    }
    
    int main()
    {
        Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
        imshow("First",img1);
        if(!img1.data)
        {
            cout<<"error! The image is not built!"<<endl;
            return -1;
        }
        Flip(img1);
        imshow("Second",img1);
        waitKey();
        return 0;
    }

    效果如下:
    这里写图片描述

    指针遍历图像的过程中,我们可能会受以往遍历矩阵的影响,得到图像首行地址后,想直接通过一个循环去遍历rows*cols*img.channels()的内存,但是考虑到一些多媒体处理芯片在行的长度为是4或8的倍数时,对图像的处理会更加高效,所以OpenCV中对图像的每行会填补一些额外像素(不显示、不保存),将填补后的行的长度称为关键字,成员变量step代表以字节为单位的图像的有效宽度。因此,我们只有在图像的有效宽度等于图像的真实宽度,即没有填补时,进行一重循环遍历。我们可以通过cv::Mat的成员函数isContinuous来判断图像是否对行进行了填充,返回值为真,表示没有对行进行填充,反之填充。此外,我们可以通过cv::Mat的成员变量data得到图像的首地址,等效于上面程序中的一种写法如下:

    uchar *p=img.data;    //首行首地址
    *p += img.step;       //次行首地址
    ……

    3、迭代器遍历

    只要对对C++稍有了解,就知道迭代器是专门用于遍历数据集合的一种非常重要的特殊的类,用其遍历隐藏了在给定集合上元素迭代的具体实现方式。C++的STL为每个容器类型都提供了迭代器,OpenCV同样为cv::Mat提供了与STL迭代器兼容的迭代器。

    cv::Mat实例的迭代器可以通过创建一个cv::MatIterator_的实例来得到,由于这是一个模板类,所以在声明时需指定图像像素的数据类型。

    cv::MatIterator_<cv::Ver3b> it;

    另外可使用定义在Mat_内部的迭代器类型

    cv::Mat_<cv::Verc3b>::iterator it;

    在这里我们通过操作像素的办法来实现图像中心对称反转。代码如下:

    #include<iostream>
    #include<opencv2/core/core.hpp>
    #include<opencv2/imgproc/imgproc.hpp>
    #include<opencv2/highgui/highgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    void Flip(Mat &img)
    {
        uchar t;
        if(img.channels()==1)
        {
            Mat_<uchar>::iterator it=img.begin<uchar>();
            Mat_<uchar>::iterator itend=img.end<uchar>();
            itend--;  //通过end成员函数得到的迭代器已超出集合,所以在这里自减
            for(;it<itend;it++,itend--)
            {
                t=*it;*it=*itend;*itend=t;
            }
        }
        else if(img.channels()==3)
        {
            Mat_<Vec3b>::iterator it=img.begin<Vec3b>();
            Mat_<Vec3b>::iterator itend=img.end<Vec3b>();
            itend--;
            for(;it<itend;it++,itend--)
                for(int k=0; k<3; k++)
                {
                    t=(*it)[k];(*it)[k]=(*itend)[k];(*itend)[k]=t;
                }
        }
    }
    
    int main()
    {
        Mat img1=imread("test.jpg");  //将任意一张名为test.jpg的图片放置于工程文件夹test中
        imshow("First",img1);
        if(!img1.data)
        {
            cout<<"error! The image is not built!"<<endl;
            return -1;
        }
        Flip(img1);
        imshow("Second",img1);
        waitKey();
        return 0;
    }

    效果如下:
    这里写图片描述

    4、核心函数LUT

    LUT是最被推荐的用于实现批量图像元素查找和更该操作图像方法。在图像处理中,对于一个给定的值,将其替换成其他的值是一个很常见的操作,OpenCV 提供里一个函数直接实现该操作,并不需要自己扫描图像,就是:operationsOnArrays:LUT() <lut>,一个包含于core module的函数. 首先我们建立一个mat型用于查表

    Mat lookUpTable(1, 256, CV_8U);
    uchar* p = lookUpTable.data; 
    for( int i = 0; i < 256; ++i)
       p[i] = table[i];

    然后我们调用函数 (I 是输入 J 是输出):
    LUT(I, lookUpTable, J);
    这里,简单提一下LUT的用法,有时间专门写一篇关于该函数的博客。

    5、效率探讨

    一般图像规模比较大的话,图像的遍历是一项相当耗时的工作,因此为提高效率,以下几点值得我们注意:
    1)对于可提前计算的变量应避免写在循环体内;如

    int cols=img.cols*img.channels();
    for(int i=0;i<cols;i++)   //而不是
    // for(int i=p;i<img.cols*img.channels();i++)

    2)在以上四种图像遍历方法中,从效率来看使用 OpenCV 内置函数LUT可以获得最快的速度,这是因为OpenCV库可以通过英特尔线程架构启用多线程。其次,指针遍历最快,迭代器遍历次之,at方法遍历最慢。一般情况下,我们只有在对任意位置的像素进行读写时才考虑at方法。
    3)要对一个像素进行多项操作,在一个循环内完成要高效于几次循环完成;此外,列数*通道数 这样的长循环要比在一个内循环一次性处理三个通道要慢得多。例如以下Flip函数要比我们在上面的指针遍历程序中写的要高效。

    void Flip(Mat &img)
    {
        int rows=img.rows;
        int cols=img.cols;
        for(int i=0; i<rows/2; i++)
        {
            uchar *p=img.ptr<uchar>(i);
            uchar *q=img.ptr<uchar>(rows-1-i);
            uchar t;
            for(int j=0; j<cols;j++)
            {
                t=*p;*p++=*q;*q++=t;
                t=*p;*p++=*q;*q++=t;
                t=*p;*p++=*q;*q++=t;
            }
        }
    }

    当然,如果把上面内循环中值交换的过程用 位运算 代替会更加高效(摊手)。

    展开全文
  • 遍历图像像素的14种方法

    千次阅读 2017-09-29 09:32:20
    //--------------------------------------【程序说明】------------------------------------------- ...// 程序描述:来自一本国外OpenCV2书籍的示例-遍历图像像素的14种方法 // 测试所用IDE版本:Visual Studio
    //--------------------------------------【程序说明】-------------------------------------------
    //		程序说明:《OpenCV3编程入门》OpenCV2版书本配套示例程序24
    //		程序描述:来自一本国外OpenCV2书籍的示例-遍历图像像素的14种方法
    //		测试所用IDE版本:Visual Studio 2010
    //		测试所用OpenCV版本:	2.4.9
    //		2014年11月 Revised by @浅墨_毛星云
    //------------------------------------------------------------------------------------------------
    
    
    /*------------------------------------------------------------------------------------------*\
       This file contains material supporting chapter 2 of the cookbook:  
       Computer Vision Programming using the OpenCV Library. 
       by Robert Laganiere, Packt Publishing, 2011.
    
       This program is free software; permission is hereby granted to use, copy, modify, 
       and distribute this source code, or portions thereof, for any purpose, without fee, 
       subject to the restriction that the copyright notice may not be removed 
       or altered from any source or altered source distribution. 
       The software is released on an as-is basis and without any warranties of any kind. 
       In particular, the software is not guaranteed to be fault-tolerant or free from failure. 
       The author disclaims all warranties with regard to this software, any use, 
       and any consequent failure, is purely the responsibility of the user.
     
       Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name
    \*------------------------------------------------------------------------------------------*/
    
    //---------------------------------【头文件、命名空间包含部分】-----------------------------
    //		描述:包含程序所使用的头文件和命名空间
    //-------------------------------------------------------------------------------------------------
    #include <iostream>
    #include <opencv2/core/core.hpp>
    #include <opencv2/highgui/highgui.hpp>
    using namespace cv;
    using namespace std;
    
    
    
    //---------------------------------【宏定义部分】---------------------------------------------
    //		描述:包含程序所使用宏定义
    //-------------------------------------------------------------------------------------------------
    #define NTESTS 14
    #define NITERATIONS 20
    
    
    
    //----------------------------------------- 【方法一】-------------------------------------------
    //		说明:利用.ptr 和 []
    //-------------------------------------------------------------------------------------------------
    void colorReduce0(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    
    	for (int j=0; j<nl; j++) 
    	{
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			data[i]= data[i]/div*div + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束                  
    	}
    }
    
    //-----------------------------------【方法二】-------------------------------------------------
    //		说明:利用 .ptr 和 * ++ 
    //-------------------------------------------------------------------------------------------------
    void colorReduce1(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    
    	for (int j=0; j<nl; j++) 
    	{
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			*data++= *data/div*div + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束              
    	}
    }
    
    //-----------------------------------------【方法三】-------------------------------------------
    //		说明:利用.ptr 和 * ++ 以及模操作
    //-------------------------------------------------------------------------------------------------
    void colorReduce2(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    
    	for (int j=0; j<nl; j++) 
    	{
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			int v= *data;
    			*data++= v - v%div + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束                   
    	}
    }
    
    //----------------------------------------【方法四】---------------------------------------------
    //		说明:利用.ptr 和 * ++ 以及位操作
    //----------------------------------------------------------------------------------------------------
    void colorReduce3(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
    
    	for (int j=0; j<nl; j++) {
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) {
    
    			//------------开始处理每个像素-------------------
    
    			*data++= *data&mask + div/2;
    
    			//-------------结束像素处理------------------------
    		}  //单行处理结束            
    	}
    }
    
    
    //----------------------------------------【方法五】----------------------------------------------
    //		说明:利用指针算术运算
    //---------------------------------------------------------------------------------------------------
    void colorReduce4(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	int step= image.step; //有效宽度
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 对于 div=16, mask= 0xF0
    
    	//获取指向图像缓冲区的指针
    	uchar *data= image.data;
    
    	for (int j=0; j<nl; j++)
    	{
    
    		for (int i=0; i<nc; i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			*(data+i)= *data&mask + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束              
    
    		data+= step;  // next line
    	}
    }
    
    //---------------------------------------【方法六】----------------------------------------------
    //		说明:利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
    //-------------------------------------------------------------------------------------------------
    void colorReduce5(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 例如div=16, mask= 0xF0
    
    	for (int j=0; j<nl; j++) 
    	{
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<image.cols * image.channels(); i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			*data++= *data&mask + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束            
    	}
    }
    
    // -------------------------------------【方法七】----------------------------------------------
    //		说明:利用.ptr 和 * ++ 以及位运算(continuous)
    //-------------------------------------------------------------------------------------------------
    void colorReduce6(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols * image.channels(); //每行元素的总元素数量
    
    	if (image.isContinuous())  
    	{
    		//无填充像素
    		nc= nc*nl; 
    		nl= 1;  // 为一维数列
    	}
    
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
    
    	for (int j=0; j<nl; j++) {
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) {
    
    			//-------------开始处理每个像素-------------------
    
    			*data++= *data&mask + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束                   
    	}
    }
    
    //------------------------------------【方法八】------------------------------------------------
    //		说明:利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
    //-------------------------------------------------------------------------------------------------
    void colorReduce7(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols ; //列数
    
    	if (image.isContinuous())  
    	{
    		//无填充像素
    		nc= nc*nl; 
    		nl= 1;  // 为一维数组
    	}
    
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
    
    	for (int j=0; j<nl; j++) {
    
    		uchar* data= image.ptr<uchar>(j);
    
    		for (int i=0; i<nc; i++) {
    
    			//-------------开始处理每个像素-------------------
    
    			*data++= *data&mask + div/2;
    			*data++= *data&mask + div/2;
    			*data++= *data&mask + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束                    
    	}
    }
    
    
    // -----------------------------------【方法九】 ------------------------------------------------
    //		说明:利用Mat_ iterator
    //-------------------------------------------------------------------------------------------------
    void colorReduce8(Mat &image, int div=64) {
    
    	//获取迭代器
    	Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
    	Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
    
    	for ( ; it!= itend; ++it) {
    
    		//-------------开始处理每个像素-------------------
    
    		(*it)[0]= (*it)[0]/div*div + div/2;
    		(*it)[1]= (*it)[1]/div*div + div/2;
    		(*it)[2]= (*it)[2]/div*div + div/2;
    
    		//-------------结束像素处理------------------------
    	}//单行处理结束  
    }
    
    //-------------------------------------【方法十】-----------------------------------------------
    //		说明:利用Mat_ iterator以及位运算
    //-------------------------------------------------------------------------------------------------
    void colorReduce9(Mat &image, int div=64) {
    
    	// div必须是2的幂
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 比如 div=16, mask= 0xF0
    
    	// 获取迭代器
    	Mat_<Vec3b>::iterator it= image.begin<Vec3b>();
    	Mat_<Vec3b>::iterator itend= image.end<Vec3b>();
    
    	//扫描所有元素
    	for ( ; it!= itend; ++it) 
    	{
    
    		//-------------开始处理每个像素-------------------
    
    		(*it)[0]= (*it)[0]&mask + div/2;
    		(*it)[1]= (*it)[1]&mask + div/2;
    		(*it)[2]= (*it)[2]&mask + div/2;
    
    		//-------------结束像素处理------------------------
    	}//单行处理结束  
    }
    
    //------------------------------------【方法十一】---------------------------------------------
    //		说明:利用Mat Iterator_
    //-------------------------------------------------------------------------------------------------
    void colorReduce10(Mat &image, int div=64) {
    
    	//获取迭代器
    	Mat_<Vec3b> cimage= image;
    	Mat_<Vec3b>::iterator it=cimage.begin();
    	Mat_<Vec3b>::iterator itend=cimage.end();
    
    	for ( ; it!= itend; it++) { 
    
    		//-------------开始处理每个像素-------------------
    
    		(*it)[0]= (*it)[0]/div*div + div/2;
    		(*it)[1]= (*it)[1]/div*div + div/2;
    		(*it)[2]= (*it)[2]/div*div + div/2;
    
    		//-------------结束像素处理------------------------
    	}
    }
    
    //--------------------------------------【方法十二】--------------------------------------------
    //		说明:利用动态地址计算配合at
    //-------------------------------------------------------------------------------------------------
    void colorReduce11(Mat &image, int div=64) {
    
    	int nl= image.rows; //行数
    	int nc= image.cols; //列数
    
    	for (int j=0; j<nl; j++) 
    	{
    		for (int i=0; i<nc; i++) 
    		{
    
    			//-------------开始处理每个像素-------------------
    
    			image.at<Vec3b>(j,i)[0]=	 image.at<Vec3b>(j,i)[0]/div*div + div/2;
    			image.at<Vec3b>(j,i)[1]=	 image.at<Vec3b>(j,i)[1]/div*div + div/2;
    			image.at<Vec3b>(j,i)[2]=	 image.at<Vec3b>(j,i)[2]/div*div + div/2;
    
    			//-------------结束像素处理------------------------
    
    		} //单行处理结束                 
    	}
    }
    
    //----------------------------------【方法十三】----------------------------------------------- 
    //		说明:利用图像的输入与输出
    //-------------------------------------------------------------------------------------------------
    void colorReduce12(const Mat &image, //输入图像
    	Mat &result,      // 输出图像
    	int div=64) {
    
    		int nl= image.rows; //行数
    		int nc= image.cols ; //列数
    
    		//准备好初始化后的Mat给输出图像
    		result.create(image.rows,image.cols,image.type());
    
    		//创建无像素填充的图像
    		nc= nc*nl; 
    		nl= 1;  //单维数组
    
    		int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    		//掩码值
    		uchar mask= 0xFF<<n; // e.g.比如div=16, mask= 0xF0
    
    		for (int j=0; j<nl; j++) {
    
    			uchar* data= result.ptr<uchar>(j);
    			const uchar* idata= image.ptr<uchar>(j);
    
    			for (int i=0; i<nc; i++) {
    
    				//-------------开始处理每个像素-------------------
    
    				*data++= (*idata++)&mask + div/2;
    				*data++= (*idata++)&mask + div/2;
    				*data++= (*idata++)&mask + div/2;
    
    				//-------------结束像素处理------------------------
    
    			} //单行处理结束                   
    		}
    }
    
    //--------------------------------------【方法十四】------------------------------------------- 
    //		说明:利用操作符重载
    //-------------------------------------------------------------------------------------------------
    void colorReduce13(Mat &image, int div=64) {
    
    	int n= static_cast<int>(log(static_cast<double>(div))/log(2.0));
    	//掩码值
    	uchar mask= 0xFF<<n; // e.g. 比如div=16, mask= 0xF0
    
    	//进行色彩还原
    	image=(image&Scalar(mask,mask,mask))+Scalar(div/2,div/2,div/2);
    }
    
    
    
    
    //-----------------------------------【ShowHelpText( )函数】-----------------------------
    //		描述:输出一些帮助信息
    //----------------------------------------------------------------------------------------------
    void ShowHelpText()
    {
    	//输出欢迎信息和OpenCV版本
    	printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
    	printf("\n\n\t\t\t此为本书OpenCV2版的第24个配套示例程序\n");
    	printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION );
    	printf("\n\n  ----------------------------------------------------------------------------\n");
    
    	printf("\n\n正在进行存取操作,请稍等……\n\n");
    }
    
    
    
    
    //-----------------------------------【main( )函数】--------------------------------------------
    //		描述:控制台应用程序的入口函数,我们的程序从这里开始
    //-------------------------------------------------------------------------------------------------
    int main( )
    {
    	int64 t[NTESTS],tinit;
    	Mat image0;
    	Mat image1;
    	Mat image2;
    
    	system("color 4F");
    
    	ShowHelpText();
    
    	image0= imread("1.png");
    	if (!image0.data)
    		return 0; 
    
    	//时间值设为0
    	for (int i=0; i<NTESTS; i++)
    		t[i]= 0;
    
    
    	// 多次重复测试
    	int n=NITERATIONS;
    	for (int k=0; k<n; k++)
    	{
    		cout << k << " of " << n << endl; 
    
    		image1= imread("1.png");
    		//【方法一】利用.ptr 和 []
    		tinit= getTickCount();
    		colorReduce0(image1);
    		t[0]+= getTickCount()-tinit;
    
    		//【方法二】利用 .ptr 和 * ++ 
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce1(image1);
    		t[1]+= getTickCount()-tinit;
    
    		//【方法三】利用.ptr 和 * ++ 以及模操作
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce2(image1);
    		t[2]+= getTickCount()-tinit;
    
    		//【方法四】 利用.ptr 和 * ++ 以及位操作
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce3(image1);
    		t[3]+= getTickCount()-tinit;
    
    		//【方法五】 利用指针的算术运算
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce4(image1);
    		t[4]+= getTickCount()-tinit;
    
    		//【方法六】利用 .ptr 和 * ++以及位运算、image.cols * image.channels()
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce5(image1);
    		t[5]+= getTickCount()-tinit;
    
    		//【方法七】利用.ptr 和 * ++ 以及位运算(continuous)
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce6(image1);
    		t[6]+= getTickCount()-tinit;
    
    		//【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce7(image1);
    		t[7]+= getTickCount()-tinit;
    
    		//【方法九】 利用Mat_ iterator
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce8(image1);
    		t[8]+= getTickCount()-tinit;
    
    		//【方法十】 利用Mat_ iterator以及位运算
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce9(image1);
    		t[9]+= getTickCount()-tinit;
    
    		//【方法十一】利用Mat Iterator_
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce10(image1);
    		t[10]+= getTickCount()-tinit;
    
    		//【方法十二】 利用动态地址计算配合at
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce11(image1);
    		t[11]+= getTickCount()-tinit;
    
    		//【方法十三】 利用图像的输入与输出
    		image1= imread("1.png");
    		tinit= getTickCount();
    		Mat result;
    		colorReduce12(image1, result);
    		t[12]+= getTickCount()-tinit;
    		image2= result;
    
    		//【方法十四】 利用操作符重载
    		image1= imread("1.png");
    		tinit= getTickCount();
    		colorReduce13(image1);
    		t[13]+= getTickCount()-tinit;
    
    		//------------------------------
    	}
    	//输出图像   
    	imshow("原始图像",image0);
    	imshow("结果",image2);
    	imshow("图像结果",image1);
    
    	// 输出平均执行时间
    	cout << endl << "-------------------------------------------" << endl << endl;
    	cout << "\n【方法一】利用.ptr 和 []的方法所用时间为 " << 1000.*t[0]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法二】利用 .ptr 和 * ++ 的方法所用时间为" << 1000.*t[1]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法三】利用.ptr 和 * ++ 以及模操作的方法所用时间为" << 1000.*t[2]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法四】利用.ptr 和 * ++ 以及位操作的方法所用时间为" << 1000.*t[3]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法五】利用指针算术运算的方法所用时间为" << 1000.*t[4]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法六】利用 .ptr 和 * ++以及位运算、channels()的方法所用时间为" << 1000.*t[5]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法七】利用.ptr 和 * ++ 以及位运算(continuous)的方法所用时间为" << 1000.*t[6]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法八】利用 .ptr 和 * ++ 以及位运算 (continuous+channels)的方法所用时间为" << 1000.*t[7]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法九】利用Mat_ iterator 的方法所用时间为" << 1000.*t[8]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法十】利用Mat_ iterator以及位运算的方法所用时间为" << 1000.*t[9]/getTickFrequency()/n << "ms" << endl;
    	cout << "\n【方法十一】利用Mat Iterator_的方法所用时间为" << 1000.*t[10]/getTickFrequency()/n << "ms" << endl;	
    	cout << "\n【方法十二】利用动态地址计算配合at 的方法所用时间为" << 1000.*t[11]/getTickFrequency()/n << "ms" << endl;	
    	cout << "\n【方法十三】利用图像的输入与输出的方法所用时间为" << 1000.*t[12]/getTickFrequency()/n << "ms" << endl;	
    	cout << "\n【方法十四】利用操作符重载的方法所用时间为" << 1000.*t[13]/getTickFrequency()/n << "ms" << endl;	
    
    	waitKey();
    	return 0;
    }
    

    展开全文
  • 我们在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不是说低效的方法就不好,不同场景使用不同方法。 方法 下面将一一...

    简述

    我们在图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不是说低效的方法就不好,不同场景使用不同方法。

    方法

    下面将一一介绍这些遍历图像像素点的方法:

    方法一:数组遍历法1

    图像Mat中每个像素点,其实就是一个值(int、float、double、uchar等类型),而Mat是一个二维数组。

    1、单通道图像(CV_8UC1);backImg是单通道灰色图。

    int height = backImg.rows;
    int width = backImg.cols;
    for (int i = 0; i < height; i++)
    {
    	for (int j = 0; j < width; j++)
    	{
    		int index = i * width + j;
    		//像素值
    		int data = (int)backImg.data[index];
    	}
    }

    2、三通道图像(CV_8UC3);backImg、colorImg均为三通道彩色图,且大小相同。

    int height = backImg.rows;
    int width = backImg.cols;
    
    //差值图
    //三个通道中只要有一个差值大于阈值,就进行提取
    Mat subImg(height, width, CV_8UC1);
    subImg.setTo(0);
    for (int i = 0; i < height; i++)
    {
    	for (int j = 0; j < width; j++)
    	{
    		int index = i * width + j;
    		//背景
    		int b1 = (int)backImg.data[3 * index + 0];
    		int g1 = (int)backImg.data[3 * index + 1];
    		int r1 = (int)backImg.data[3 * index + 2];
    		//当前图
    		int b2 = (int)colorImg.data[3 * index + 0];
    		int g2 = (int)colorImg.data[3 * index + 1];
    		int r2 = (int)colorImg.data[3 * index + 2];
    		
    		//th为阈值
    		if (g1 - g2 > th || b1 - b2 > th || r1 - r2 > th)
    		{
    			subImg.data[index] = 255;
    		}
    	}
    }

    方法二:数组遍历法2 -- at<typename>(i,j)

    Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。

    void colorReduce(Mat& image)
    {
         for(int i=0;i<image.rows;i++)
         {
    		 for(int j=0;j<image.cols;j++)
    		 {
    			if(image.channels() == 1)
    			{
    				//取出灰度图像中i行j列的点
    				image.at<uchar>(i,j) = image.at<uchar>(i,j) / 2;			
    			}
    			else if(image.channels() == 3)
    			{
    				//取出彩色图像中i行j列第k(0/1/2)通道的颜色点
    				image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0] / 2;
    				image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1] / 2;
    				image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2] / 2;
    			}			 
    		 }
    	 }
    }    

    方法三:指针遍历法

    1、这种图像遍历方法相比“数组遍历法”更高效

     void colorReduce(Mat& srcImg,Mat& dstImg)
    {
    	dstImg = srcImg.clone();
    	dstImg.setTo(0);
    	int height = srcImg.rows;
    	// 将3通道转换为1通道
    	int width = srcImg.cols * srcImg.channels();
    	for(int k = 0;k < height; k++)
    	{
    		// 获取第k行的首地址
    		const uchar* inData = srcImg.ptr<uchar>(k);
    		uchar* outData = dstImg.ptr<uchar>(k);
    		//处理每个像素
    		for(int i = 0; i < width; i++)
    		{
    			outData[i] = inData[i] / 2;
    		}
    	}
    }   

    程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。
    但是这种用法不能用在行与行之间,因为图像在opencv里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。

    2、但是我们可以申明一个连续的空间来存储图像,这样效率就会更高。图像可以是连续的,也可以是不连续的,Mat提供了一个检测图像是否连续的函数isContinuous(),当图像连通时,我们就可以把图像完全展开,看成是一行。

    void colorReduce(Mat& srcImg,Mat& dstImg)
    {
    	int height = srcImg.rows;
    	int width = srcImg.cols;
    	dstImg = srcImg.clone();
    	dstImg.setTo(0);
    	//判断图像是否连续
    	if(srcImg.isContinuous() && dstImg.isContinuous())
    	{
    		height = 1;
    		width = width * srcImg.rows * srcImg.channels();
    	}
    	for(int i = 0; i< height; ++i)
    	{
    		const uchar* inData = srcImg.ptr<uchar>(i);
    		uchar* outData = dstImg.ptr<uchar>(i);
    		for(int j = 0; j< width; ++j)
    		{
    		   outData[j] = inData[j] / 2;
    		}
    	}
    }

    方法四:迭代器遍历法

    1、迭代器Matlterator_

    Matlterator_是Mat数据操作的迭代器,:begin()表示指向Mat数据的起始迭代器,:end()表示指向Mat数据的终止迭代器。迭代器方法是一种更安全的用来遍历图像的方式,首先获取到数据图像的矩阵起始,再通过递增迭代实现移动数据指针。

    //三通道彩色图
    void colorReduce(Mat srcImage)
    {
    	Mat tempImage = srcImage.clone();  
      
        // 初始化原图像迭代器  
        MatConstIterator_<Vec3b> srcIterStart = srcImage.begin<Vec3b>();  
        MatConstIterator_<Vec3b> srcIterEnd = srcImage.end<Vec3b>();  
      
        // 初始化输出图像迭代器  
        MatIterator_<Vec3b> resIterStart = tempImage.begin<Vec3b>();  
        MatIterator_<Vec3b> resIterEnd = tempImage.end<Vec3b>();  
      
        while (srcIterStart != srcIterEnd)   
        {  
            (*resIterStart)[0] = 255 - (*srcIterStart)[0];  
            (*resIterStart)[1] = 255 - (*srcIterStart)[1];  
            (*resIterStart)[2] = 255 - (*srcIterStart)[2];  
      
            srcIterStart++;  
            resIterStart++;  
        } 
    }
    //单通道灰色图
    void colorReduce(Mat srcImage)
    {
    	Mat tempImage = srcImage.clone();  
      
        // 初始化原图像迭代器  
        MatConstIterator_<uchar> srcIterStart = srcImage.begin<uchar>();  
        MatConstIterator_<uchar> srcIterEnd = srcImage.end<uchar>();  
      
        // 初始化输出图像迭代器  
        MatIterator_<uchar> resIterStart = tempImage.begin<uchar>();  
        MatIterator_<uchar> resIterEnd = tempImage.end<uchar>();  
      
        while (srcIterStart != srcIterEnd)   
        {  
            *resIterStart = 255 - *srcIterStart;    
            srcIterStart++;  
            resIterStart++;  
        } 
    }

    2、迭代器Mat_

    OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

    void colorReduce(Mat &img)
    {
    	uchar t;
    	//单通道图像
    	if(img.channels() == 1)
    	{
    		Mat_<uchar>::iterator it = img.begin<uchar>();
    		Mat_<uchar>::iterator itend = img.end<uchar>();
    		while(it != itend)
    		{
    			//相关操作
    			*it = 0;
    			it++;
    		}
    	}
    	//三通道图像
    	else if(img.channels() == 3)
    	{
    		Mat_<Vec3b>::iterator it = img.begin<Vec3b>();
    		Mat_<Vec3b>::iterator itend = img.end<Vec3b>();
    		while(it != itend)
    		{
    			//相关操作
    			(*it)[0] = 0;
    			(*it)[1] = 1;
    			(*it)[2] = 2;
    			it++;
    		}
    	}
    }
    
    //单通道图像(CV_8UC1)src是单通道灰色图
    //作用:统计图像中白色像素个数
    int BaseFun::whiteSums(Mat src)	
    {	
    	int counter = 0;
    	//迭代器访问像素点
    	Mat_<uchar>::iterator it = src.begin<uchar>();
    	Mat_<uchar>::iterator itend = src.end<uchar>();  
    	for (; it!=itend; ++it)
    	{
    		if((*it)>0) counter+=1;//二值化后,像素点是0或者255
    	}			
    	return counter;
    }
    

    方法五:Mat_数组遍历法(非迭代器法,Mat_迭代器法已在方法四中介绍)

    Mat_也可以通过下标的方式来遍历图像,但是相比Mat::at()方法,无疑Mat_通过下标遍历图像是慢的,不推荐这种方式,如果要使用Mat_,最好只是使用迭代器Mat_<>::iterator。

    //单通道图像
    void colorReduce(Mat srcImg)
    {
    	int height = srcImg.rows;
    	int width = srcImg.cols;
    	//转化为Mat_<uchar>
    	Mat_<uchar> tempImg = (Mat_<uchar>&)srcImg;
    
    	for (int i = 0; i < height; i++)
    	{
    		for (int j = 0; j < width; j++)
    		{
    			tempImg(i,j) = tempImg(i,j) * 2;
    			printf("%d ",tempImg(i,j));	
    		}
    		printf("\n");
    	}
    }
    //三通道图像	
    void colorReduce(Mat srcImg)
    {		
    	Mat_<Vec3b> tempImg = (Mat_<Vec3b>&)srcImg;
    	int height = srcImg.rows;
    	int width = srcImg.cols;
    	for (int i = 0; i < height; i++)
    	{
    		for (int j = 0; j < width; j++)
    		{
    			printf("%d ",tempImg(i,j)[0]);	
    			printf("%d ",tempImg(i,j)[1]);	
    			printf("%d ",tempImg(i,j)[2]);	
    		}
    		printf("\n");
    	}
    }

     Mat_<uchar>对应的是CV_8U,Mat_<char>对应的是CV_8S,Mat_<int>对应的是CV_32S,Mat_<float>对应的是CV_32F,Mat_<double>对应的是CV_64F,对应的数据深度如下:

    • CV_8U - 8-bit unsigned integers ( 0..255 )

    • CV_8S - 8-bit signed integers ( -128..127 )

    • CV_16U - 16-bit unsigned integers ( 0..65535 )

    • CV_16S - 16-bit signed integers ( -32768..32767 )

    • CV_32S - 32-bit signed integers ( -2147483648..2147483647 )

    • CV_32F - 32-bit floating-point numbers ( -FLT_MAX..FLT_MAX, INF, NAN )

    • CV_64F - 64-bit floating-point numbers ( -DBL_MAX..DBL_MAX, INF, NAN )

    附一张opencv官方给出的关于Mat_解释的图片:

    总结

    目前找到了这些方式来遍历图像像素,全部亲测可用。如有朋友发现新的遍历方法,可一起交流。

    本文部分内容参考博客:http://www.cnblogs.com/ronny/p/opencv_road_2.html,感谢这位博主。

    展开全文
  • 遍历图像像素的几种方法

    千次阅读 2018-12-19 15:08:26
    以下是我根据几位博主的内容整理的,在此给出转载链接为: [https://blog.csdn.net/daoqinglin/article/details/23628125] [https://blog.csdn.net/xuhang0910/article/details/47058419] ...

    以下是我根据几位博主的内容整理的,在此给出转载链接为:
    [https://blog.csdn.net/daoqinglin/article/details/23628125]
    [https://blog.csdn.net/xuhang0910/article/details/47058419]
    [https://blog.csdn.net/github_35160620/article/details/51708659]

    首先要先介绍一下矩阵元素的表达:

    对于单通道图像,其元素类型一般为 8U (即 8位无符号整数),当然也可以 是 16S 、32F32F 等;这些类型可以直接用 uchar 、short 、float等 C/C++语言中的基本数据类型表达。

    如果多通道图像,如RGB彩色图像,需要用三个通道来表示。在这种情况 下,如果依然将图像视作一个二维矩阵那么矩阵的元素不再是基本数据类型。

    opencv中使用vec可表示向量,用于矩阵元素的表达。


    OpenCV优化:图像的遍历4种方式

    一、遍历图像的4种方式:at<typename>(i,j)

    Mat类提供了一个at的方法用于取得图像上的点,它是一个模板函数,可以取到任何类型的图像上的点。

    在实际应用中,我们很多时候需要对图像降色彩,因为256*256*256实在太多了,在图像颜色聚类或彩色直方图时,我们需要用一些代表性的颜色代替丰富的色彩空间,我们的思路是将每个通道的256种颜色用64种代替,即将原来256种颜色划分64个颜色段,每个颜色段取中间的颜色值作为代表色。

    复制代码
     1 void colorReduce(Mat& image,int div)
     2 {
     3     for(int i=0;i<image.rows;i++)
     4     {
     5         for(int j=0;j<image.cols;j++)
     6         {
     7             image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
     8             image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
     9             image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
    10         }
    11     }
    12 }
    复制代码

    image

    更简单一些的方法:OpenCV定义了一个Mat的模板子类为Mat_,它重载了operator()让我们可以更方便的取图像上的点。

    Mat_<uchar> im=image;

    im(i,j)=im(i,j)/div*div+div/2;

    需要注意的是,如果遍历图像并不推荐使用 需要注意的是,如果遍历图像并不推荐使用 at() 函数。使用at()函数优点是代码可读性高,但效率不高。

    二、高效一点:用指针来遍历图像

    OpenCV Mat数据类型指针ptr的使用

        cv::Mat image = cv::Mat(400, 600, CV_8UC1); //宽400,长600
        uchar * data00 = image.ptr<uchar>(0);
        uchar * data10 = image.ptr<uchar>(1);
        uchar * data01 = image.ptr<uchar>(0)[1];

      解释:

      • 定义了一个Mat变量image。
      • data00是指向image第一行第一个元素的指针。
      • data10是指向image第二行第一个元素的指针。
      • data01是指向image第一行第二个元素的指针。

      上面的例程中可以看到,我们实际喜欢把原图传进函数内,但是在函数内我们对原图像进行了修改,而将原图作为一个结果输出,很多时候我们需要保留原图,这样我们需要一个原图的副本。

      复制代码
       1 void colorReduce(const Mat& image,Mat& outImage,int div)
       2 {
       3     // 创建与原图像等尺寸的图像
       4     outImage.create(image.size(),image.type());
       5     int nr=image.rows;
       6     // 将3通道转换为1通道
       7     int nl=image.cols*image.channels();
       8     for(int k=0;k<nr;k++)
       9     {
      10         // 每一行图像的指针
      11         const uchar* inData=image.ptr<uchar>(k);
      12         uchar* outData=outImage.ptr<uchar>(k);
      13         for(int i=0;i<nl;i++)
      14         {
      15             outData[i]=inData[i]/div*div+div/2;
      16         }
      17     }
      18 }
    复制代码

    从上面的例子中可以看出,取出图像中第i行数据的指针:image.ptr<uchar>(i)。

    值得说明的是:程序中将三通道的数据转换为1通道,在建立在每一行数据元素之间在内存里是连续存储的,每个像素三通道像素按顺序存储。也就是一幅图像数据最开始的三个值,是最左上角的那像素的三个通道的值。

    但是这种用法不能用在行与行之间,因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。但是我们可以申明一个连续的空间来存储图像,这个话题引入下面最为高效的遍历图像的机制。

    三、更高效的方法

    上面已经提到过了,一般来说图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。

    复制代码
     1 void colorReduce(const Mat& image,Mat& outImage,int div)
     2 {
     3     int nr=image.rows;
     4     int nc=image.cols;
     5     outImage.create(image.size(),image.type());
     6     if(image.isContinuous()&&outImage.isContinuous())
     7     {
     8         nr=1;
     9         nc=nc*image.rows*image.channels();
    10     }
    11     for(int i=0;i<nr;i++)
    12     {
    13         const uchar* inData=image.ptr<uchar>(i);
    14         uchar* outData=outImage.ptr<uchar>(i);
    15         for(int j=0;j<nc;j++)
    16         {
    17             *outData++=*inData++/div*div+div/2;
    18         }
    19     }
    20 }
    复制代码

    用指针除了用上面的方法外,还可以用指针来索引固定位置的像素:

    image.step返回图像一行像素元素的个数(包括空白元素),image.elemSize()返回一个图像像素的大小。

    &image.at<uchar>(i,j)=image.data+i*image.step+j*image.elemSize();

    四、用迭代器来遍历。

    下面的方法可以让我们来为图像中的像素声明一个迭代器:

    MatIterator_<Vec3b> it;

    Mat_<Vec3b>::iterator it;

    如果迭代器指向一个const图像,则可以用下面的声明:

    MatConstIterator<Vec3b> it; 或者

    Mat_<Vec3b>::const_iterator it;

    下面我们用迭代器来简化上面的colorReduce程序:

    复制代码
     1 void colorReduce(const Mat& image,Mat& outImage,int div)
     2 {
     3     outImage.create(image.size(),image.type());
     4     MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();
     5     MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();
     6     MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
     7     MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();
     8     while(it_in!=itend_in)
     9     {
    10         (*it_out)[0]=(*it_in)[0]/div*div+div/2;
    11         (*it_out)[1]=(*it_in)[1]/div*div+div/2;
    12         (*it_out)[2]=(*it_in)[2]/div*div+div/2;
    13         it_in++;
    14         it_out++;
    15     }
    16 }
    复制代码

    如果你想从第二行开始,则可以从image.begin<Vec3b>()+image.rows开始。

    上面4种方法中,第3种方法的效率最高!

    五、图像的邻域操作

    很多时候,我们对图像处理时,要考虑它的邻域,比如3*3是我们常用的,这在图像滤波、去噪中最为常见,下面我们介绍如果在一次图像遍历过程中进行邻域的运算。

    下面我们进行一个简单的滤波操作,滤波算子为[0 –1 0;-1 5 –1;0 –1 0]。

    它可以让图像变得尖锐,而边缘更加突出。核心公式即:sharp(i.j)=5*image(i,j)-image(i-1,j)-image(i+1,j

    )-image(i,j-1)-image(i,j+1)。

    复制代码
     1 void ImgFilter2d(const Mat &image,Mat& result)
     2 {
     3     result.create(image.size(),image.type());
     4     int nr=image.rows;
     5     int nc=image.cols*image.channels();
     6     for(int i=1;i<nr-1;i++)
     7     {
     8         const uchar* up_line=image.ptr<uchar>(i-1);//指向上一行
     9         const uchar* mid_line=image.ptr<uchar>(i);//当前行
    10         const uchar* down_line=image.ptr<uchar>(i+1);//下一行
    11         uchar* cur_line=result.ptr<uchar>(i);
    12         for(int j=1;j<nc-1;j++)
    13         {
    14             cur_line[j]=saturate_cast<uchar>(5*mid_line[j]-mid_line[j-1]-mid_line[j+1]-
    15                 up_line[j]-down_line[j]);
    16         }
    17     }
    18     // 把图像边缘像素设置为0
    19     result.row(0).setTo(Scalar(0));
    20     result.row(result.rows-1).setTo(Scalar(0));
    21     result.col(0).setTo(Scalar(0));
    22     result.col(result.cols-1).setTo(Scalar(0));
    23 }
    复制代码

    image

    上面的程序有以下几点需要说明:

    1,staturate_cast<typename>是一个类型转换函数,程序里是为了确保运算结果还在uchar范围内。

    2,row和col方法返回图像中的某些行或列,返回值是一个Mat。

    3,setTo方法将Mat对像中的点设置为一个值,Scalar(n)为一个灰度值,Scalar(a,b,c)为一个彩色值。

    六、图像的算术运算

    Mat类把很多算数操作符都进行了重载,让它们来符合矩阵的一些运算,如果+、-、点乘等。

    下面我们来看看用位操作和基本算术运算来完成本文中的colorReduce程序,它更简单,更高效。

    将256种灰度阶降到64位其实是抛弃了二进制最后面的4位,所以我们可以用位操作来做这一步处理。

    首先我们计算2^8降到2^n中的n:int n=static_cast<int>(log(static_cast<double>(div))/log(2.0));

    然后可以得到mask,mask=0xFF<<n;

    用下面简直的语句就可以得到我们想要的结果:

    result=(image&Scalar(mask,mask,mask))+Scalar(div/2,div/2,div/2);

    很多时候我们需要对图像的一个通信单独进行操作,比如在HSV色彩模式下,我们就经常把3个通道分开考虑。

    1 vector<Mat> planes;
    2 // 将image分为三个通道图像存储在planes中
    3 split(image,planes);
    4 planes[0]+=image2;
    5 // 将planes中三幅图像合为一个三通道图像
    6 merge(planes,result);

    作者:☆Ronny丶

    出处:http://www.cnblogs.com/ronny/

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。


    展开全文
  • 在开始寻找高效遍历方法之前,先来了解一下Mat对象的数据组织形式与像素块数据的存储方式,Mat对象由两个部分组成,元数据头部加像素数据块部分,图示如下: 在OpenCV C++中Mat对象的内存管理由OpenCV框架自动...
  • OpenCV3 之 遍历图像像素的14种方法

    千次阅读 2019-09-06 10:47:08
    直接上代码: /*------------------------------------------------------------------------------------------*\ This file contains material supporting chapter 2 of the cookbook: Computer Vision ...
  • #include&lt;opencv2/imgproc/imgproc.hpp&gt; #include&lt;opencv2/highgui/highgui.hpp&gt; #include&lt;opencv2/core/core.hpp&gt; #include&lt;cmath&...usi...
  • Mat colorReduce0(cv::Mat &image, int div = 64) { int nr = image.rows; // number of rows int nc = image.cols * image.channels(); // total number of elements per line for (int j = 0;...
  • 遍历图像像素点的方法

    千次阅读 2015-05-30 17:13:41
    一:椒盐噪点的例子(随机遍历图像的某个像素点) void salt(Mat &image,int n){ for(int k=0;k //rand():随机数产生函数 int i=rand()%image.cols; int j=rand()%iimage.rows; if(image.channels...
  • 图像处理时经常会用到遍历图像像素点的方式,同样是遍历图像像素点,共有很多中方法可以做到;在这些方法中,有相对高效的,也有低效的;不同场景使用不同方法。 其中: Mat_对应的是CV_8U,Mat_对应的是CV_8S,Mat...
  • 图像处理中不可避免的要涉及到对图像像素的操作,这篇文章将介绍对图像像素的访问及遍历图像像素的方法。1.颜色空间缩减及查找表设想一种简单的C\C++类型的无符号字符型矩阵的存储结构,对于单通道图像而言,图像...
  • C++/opencv遍历图像像素值,创建并绘制新的图像 代码的主要功能: 主要是给原图,图上一层颜色,不同的类别对应不同的颜色 ori_img: label_img: 通过读取 label_img生成彩色图: 和原图融合: 1、有 ori.jpg原图...
  • opencv遍历像素的方式

    2019-07-20 13:50:46
    OpenCV优化:图像遍历4种方式 ...我们在实际应用中对图像进行的操作,往往并不是将图像作为一个整体进行操作,而是对图像中的所有点或特殊点进行运算,所以遍历图像就显得很重要,如何高效的遍历图像是一个很值...
  • opencv2-遍历图像像素的14种方法

    千次阅读 2015-10-20 20:14:06
    opencv2-遍历图像像素的14种方法
  • Emgucv以及Opencv封装了大量的API,但大多数图像处理算法内部都是从操作每个像素开始的。有时候在工程应用中,我们不得不遍历图像的每个像素来实现我们的算法。 举一个应用场景说明:一张图像的矩阵元素若存储的是...
  • python遍历灰度图像像素方法总结

    千次阅读 2020-07-11 21:35:22
    import numpy as np import matplotlib.pyplot as plt import cv2 import time ...# 以遍历每个像素取反为例 # 方法1 t1 = time.time() img1 = np.copy(img) rows,cols = img1.shape[:2] for row ...
  • 版权声明:转载请注明原作者及出处import numpy as np from cv2 import cv2 #或者用...遍历图像每个像素的每个通道""" print(img.shape) #打印图像的高,宽,通道数(返回一个3元素的tuple) h...
  • python中使用PIL库对图片像素遍历以及像素值改变将图片中特定像素值的部分改为另一种像素值读取图片的像素值改变颜色实验图片展示完整代码 将图片中特定像素值的部分改为另一种像素值 实验目的:将一张图片中白色...
  • OpenCV学习C++接口:图像遍历+像素压缩 编译环境:VS2010+OpenCV2.3.1 学习体会: 当Mat为多通道时,如3通道,如果我们将其内容输出到终端,则可以看出其列数为Mat::cols的n倍,当然n为Mat的通道数。虽是...
1 2 3 4 5 ... 20
收藏数 18,912
精华内容 7,564
关键字:

图像处理 遍历所有像素