• OpenCV探索之路(十七):Mat和IplImage访问像素的方法总结


    在opencv的编程中,遍历访问图像元素是经常遇到的操作,掌握其方法非常重要,无论是Mat类的像素访问,还是IplImage结构体的访问的方法,都必须扎实掌握,毕竟,图像处理本质上就是对像素的各种操作,访问元素就是各种图像处理算法的第一步。

    首先先看看图像的是怎么存储的。

    单通道图像

    多通道图像

    Mat访问图像元素方法汇总

    1.用指针访问元素

    在大多数图像处理任务中, 执行计算时你都需要对图像的所有像素进行扫描。 当需要访问的像素数量非常庞大, 你必须采用高效的方式来执行这个任务来提高效率。 如果你需要高效扫描大图片的数据,那么请使用指针方式。

    #include<opencv2opencv.hpp>   
    #include<opencv2highguihighgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	Mat img = imread("lena.jpg", 1); 
    	if (img.empty())
    	{
    		cout << "fail to read image" << endl;
    		return -1;
    	}
    	Mat img1 = img.clone();
    	int div = 64;
    
    	/* 方法1:用指针访问 */
    	//多通道访问法1
    	int rows = img1.rows;
    	int cols = img1.cols; 
    	for (int i = 0; i < rows; i++)
    	{
    		//uchar* p = img1.ptr<uchar>(i);  //获取第i行的首地址
    		for (int j = 0; j < cols; j++)
    		{
    			//在这里操作具体元素
    			uchar *p = img1.ptr<uchar>(i, j);
    			p[0] = p[0] / div*div + div / 2;
    			p[1] = p[1] / div*div + div / 2;
    			p[2] = p[2] / div*div + div / 2;
    		}
    	}
    
    	imshow("lean", img1);
    
    
    	//多通道访问法2
    	Mat img3 = img.clone();
    	int channels = img3.channels(); //获取通道数
    	int rows3 = img3.rows;
    	int cols3 = img3.cols* channels; //注意,是列数*通道数
    	for (int i = 0; i < rows3; i++)
    	{
    		uchar* p = img3.ptr<uchar>(i);  //获取第i行的首地址
    		for (int j = 0; j < cols3; j++)
    		{
    			//在这里操作具体元素
    			p[j] = p[j] / div*div + div / 2;
    			p[j+1] = p[j+1] / div*div + div / 2;
    			p[j+2] = p[j+2] / div*div + div / 2;
    		}
    	}
    
    	imshow("lean3", img3);
    
    	//单通道图像
    	Mat img2 = img.clone();
    	cvtColor(img2, img2, COLOR_BGR2GRAY);
    	for (int i = 0; i < img2.rows; i++)
    	{
    		uchar* p = img2.ptr<uchar>(i);  //获取第i行的首地址
    		for (int j = 0; j < img2.cols; j++)
    		{
    			//在这里操作具体元素
    			p[j] = p[j] / div*div + div / 2;
    		}
    	}
    
    	imshow("lean2", img2);
    	waitKey(0);
    	return 0;
    }
    
    

    2.用迭代器访问元素

    在面向对象编程时, 我们通常用迭代器对数据集合进行循环遍历。 标准模板库(STL) 对每个集合类都定义了对应的迭代器类, OpenCV也提供了cv::Mat的迭代器类, 并且与C++ STL中的标准迭代器兼容。

    #include<opencv2opencv.hpp>   
    #include<opencv2highguihighgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	Mat img = imread("lena.jpg",1); //载入灰度图
    	Mat img1 = img.clone();
    	int div = 64;
    	/* 方法2:用迭代器访问 */
    
    	/******************多通道的可以这么写***************/
    	Mat_<Vec3b>::iterator it = img1.begin<Vec3b>();  //获取起始迭代器
    	Mat_<Vec3b>::iterator it_end = img1.end<Vec3b>();  //获取结束迭代器
    	for (; it != it_end; it++)
    	{
    		//在这里分别访问每个通道的元素
    		(*it)[0] = (*it)[0] / div*div + div / 2;
    		(*it)[1] = (*it)[1] / div*div + div / 2;
    		(*it)[1] = (*it)[1] / div*div + div / 2;
    	}
    
    	imshow("lean", img1);
    
    
    	/******************单通道的可以这么写***************/
    	Mat img2;
    	cvtColor(img, img2, COLOR_RGB2GRAY); //转化为单通道灰度图
    
    	Mat_<uchar>::iterator it2 = img2.begin<uchar>();  //获取起始迭代器
    	Mat_<uchar>::iterator it_end2 = img2.end<uchar>();  //获取结束迭代器
    	for (; it2 != it_end2; it2++)
    	{
                //在这里分别访问每个通道的元素
                *it2 = *it2 / div*div + div / 2;
    	}
    	imshow("lena2", img2);
    
    	waitKey(0);
    	return 0;
    }
    }
    

    若要从图像的第二行开始,程序该怎么修改? 我们可以用

    image.begin<cv::Vec3b>()+image.cols
    

    初始化cv::Mat迭代器。 获得集合结束位置的方法也类似, 只是改用end方法。 但是, 用end方法得到的迭代器已经超出了集合范围, 因此必须在结束位置停止迭代过程。 结束的迭代器也能使用数学计算, 例如, 如果你想在最后一行前就结束迭代, 可使用

    image.end<cv::Vec3b>()-image.cols
    

    3.动态地址+at()访问元素

    #include<opencv2opencv.hpp>   
    #include<opencv2highguihighgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	Mat img = imread("lena.jpg",1); 
    	Mat img1 = img.clone();
    	int div = 64;
    	/* 方法3:用at访问 */
    
    	/****************访问多通道元素*********************/
    	int rows = img1.rows;
    	int cols = img1.cols;
    	for (int i = 0; i < rows; i++)
    	{
    		for (int j = 0; j < cols; j++)
    		{
    			//在这里访问每个通道的元素,注意,成员函数at(int y,int x)的参数
    			img1.at<Vec3b>(i,j)[0] = img1.at<Vec3b>(i, j)[0] / div*div + div / 2;
    			img1.at<Vec3b>(i, j)[1] = img1.at<Vec3b>(i, j)[1] / div*div + div / 2;
    			img1.at<Vec3b>(i, j)[2] = img1.at<Vec3b>(i, j)[2] / div*div + div / 2;
    
    		}
    	}
    
    	imshow("lena", img1);
    
    	/****************访问单通道元素*********************/
    	Mat img2;
    	cvtColor(img, img2, COLOR_RGB2GRAY);
    
    	for (int i = 0; i < rows; i++)
    	{
    		for (int j = 0; j < cols; j++)
    		{
    			//在这里访问每个通道的元素,注意,成员函数at(int y,int x)的参数
    			img2.at<uchar>(i, j) = img2.at<uchar>(i, j) / div*div + div / 2;
    		}
    	}
    
    	imshow("lena2", img2);
    
    	waitKey(0);
    	return 0;
    }
    
    

    IplImage访问元素方法汇总

    1.使用cvGet2D()函数访问

    #include<opencv2opencv.hpp>   
    #include<opencv2highguihighgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	/*访问单通道元素*/
    	IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1); //单通道图像
    	CvScalar s;
    	double tmp;
    	for (int i = 0; i < img->height; i++)
    	{
    		for (int j = 0; j < img->width; j++)
    		{
    			//可以在这里访问元素
    			tmp = cvGet2D(img, i, j).val[0];
    			cvSet2D(img, i, j, 255);  //第三个参数是要设置的值
    		}
    	}
    	cvShowImage("img", img);
    
    
    	/*访问多通道元素*/
    	IplImage* img2 = cvCreateImage(cvSize(640, 480), IPL_DEPTH_32F, 3);
    	double tmpb, tmpg, tmpr;
    	for (int i = 0; i < img->height; i++)
    	{
    		for (int j = 0; j < img->width; j++)
    		{
    			tmpb = cvGet2D(img, i, j).val[0];
    			tmpg = cvGet2D(img, i, j).val[1];
    			tmpr = cvGet2D(img, i, j).val[2];
    
    			cvSet2D(img2, i, j, CvScalar(255,255,255));  //第三个参数是要设置的值,三个通道一起设置
    		}
    	}
    	cvShowImage("img2", img2);
    
    	waitKey(0);
    	return 0;
    }
    
    

    2.指针方式直接访问

    追求高效率地访问元素请使用该方法。

    #include<opencv2opencv.hpp>   
    #include<opencv2highguihighgui.hpp>
    
    using namespace std;
    using namespace cv;
    
    int main(int argc, char** argv)
    {
    	/*访问多通道元素*/
    	IplImage* img = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3);
    	uchar* data = (uchar *)img->imageData;
    	int step = img->widthStep / sizeof(uchar);
    	int channels = img->nChannels;
    	uchar b, g, r;
    	for (int i = 0; i < img->height; i++)
    	{
    		for (int j = 0; j < img->width; j++)
    		{
    			//获得元素的值
    			b = data[i*step + j*channels + 0];
    			g = data[i*step + j*channels + 1];
    			r = data[i*step + j*channels + 2];
    
    			//修改元素的值
    			data[i*step + j*channels + 0] = 255;
    		}
    	}
    
    	cvShowImage("img", img);
    
    
    	/*访问单通道元素*/
    	IplImage* img2 = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 1);
    	uchar* data2 = (uchar *)img2->imageData;
    	int step2 = img2->widthStep / sizeof(uchar);
    	uchar v;
    	for (int i = 0; i < img2->height; i++)
    	{
    		for (int j = 0; j < img2->width; j++)
    		{
    			//获得元素的值
    			v = data2[i*step2 + j];
    
    			//修改元素的值
    			data2[i*step2 + j] = 255;
    		}
    	}
    
    	cvShowImage("img2", img2);
    
    
    
    	waitKey(0);
    	return 0;
    }
    
    
  • 相关阅读:
    重建二叉树
    字符串移位包含的问题
    整数的逆序存储
    容器的综合应用:文本查询程序(摘自C++ Primer)
    vsprintf 变参函数可以用
    常用项目依赖(前端)
    eslint一些常见配置
    Jscrpit中的原型对象
    html网页自适应手机屏幕大小
    A Bit of Fun
  • 原文地址:https://www.cnblogs.com/skyfsm/p/7082914.html
Copyright © 2020-2023  润新知