Opencv计算机视觉编程攻略-第二节 图像像素操作

1.访问像素值

以椒盐噪声为例展示像素值访问的几种方法

void salt(cv::Mat image, int n) {
    
    

	// C++11 random number generator
	std::default_random_engine generator;
	std::uniform_int_distribution<int> randomRow(0, image.rows - 1);
	std::uniform_int_distribution<int> randomCol(0, image.cols - 1);
	
	// use image with a Mat_ template 使用模板操作做转换后可直接访问
	cv::Mat_<uchar> img(image);

	int i,j;
	for (int k=0; k<n; k++) {
    
    

		// random image coordinate
		i= randomCol(generator);
		j= randomRow(generator);

        // 自适应通道数
		if (image.type() == CV_8UC1) {
    
     // gray-level image

			// single-channel 8-bit image
			image.at<uchar>(j,i)= 255; 

		} else if (image.type() == CV_8UC3) {
    
     // color image

			// 3-channel image
			image.at<cv::Vec3b>(j,i)[0]= 255; 
			image.at<cv::Vec3b>(j,i)[1]= 255; 
			image.at<cv::Vec3b>(j,i)[2]= 255; 
			// 直接使用位置访问 下列为单通道赋值
			img(j,i)= 255; 

			// or simply:
			// image.at<cv::Vec3b>(j, i) = cv::Vec3b(255, 255, 255);
		}
	}
}

2.用指针扫描图像

以处理图像像素为例展示指针扫描图像,使用指针并结合位运算的速度远高于逐个计算,double duration = (cv::getTickCount() - start) / cv::getTickFrequency()使用该语句实现算法是耗时分析:

void colorReduceIO(const cv::Mat &image, // input image
	               cv::Mat &result,      // output image
	               int div = 64) {
    
    

	int nl = image.rows; // number of lines
	int nc = image.cols; // number of columns
	int nchannels = image.channels(); // number of channels

	// allocate output image if necessary
	result.create(image.rows, image.cols, image.type());
    //1.指针扫描处理
	for (int j = 0; j<nl; j++) {
    
    
		// get the addresses of input and output row j
		// 每一行的开头地址
		const uchar* data_in = image.ptr<uchar>(j);
		uchar* data_out = result.ptr<uchar>(j);
		// 在opencv中按照BGR-BGR逐个排列
		for (int i = 0; i<nc*nchannels; i++) {
    
    
			// process each pixel ---------------------
			data_out[i] = data_in[i] / div*div + div / 2;
			// end of pixel processing ----------------
		} // end of line
	}
	//2.图像实现运算符重载
	 int n= static_cast<int>(log(static_cast<double>(div))/log(2.0) + 0.5);
      // mask used to round the pixel value
      uchar mask= 0xFF<<n; // e.g. for div=16, mask= 0xF0
      // perform color reduction
      image=(image&cv::Scalar(mask,mask,mask))+cv::Scalar(div/2,div/2,div/2);
     //3. 整图对象遍历
      cv::MatIterator_<cv::Vec3b> it= image.begin<cv::Vec3b>();
      cv::MatIterator_<cv::Vec3b> itend= image.end<cv::Vec3b>();
      const cv::Vec3b offset(div/2,div/2,div/2);
      for ( ; it!= itend; ++it) {
    
    
        // process each pixel ---------------------
        *it= *it/div*div+offset;
        // end of pixel processing ----------------
      }
}

3.扫描图像并访问相邻像素

在图像运算中,时长需要访问周边位置的像素,比如边缘提取、边缘锐化,以边缘锐化为例,展示如何访问相邻像素:

void sharpen2D(const cv::Mat &image, cv::Mat &result) {
    
    

	// 1. 直接使用filter2D进行卷积计算
	cv::Mat kernel(3,3,CV_32F,cv::Scalar(0));
	// assigns kernel values
	kernel.at<float>(1,1)= 5.0;
	kernel.at<float>(0,1)= -1.0;
	kernel.at<float>(2,1)= -1.0;
	kernel.at<float>(1,0)= -1.0;
	kernel.at<float>(1,2)= -1.0;
	//filter the image
	cv::filter2D(image,result,image.depth(),kernel);
	//2.通过指针实现高效运算
	result.create(image.size(), image.type()); // allocate if necessary
	int nchannels= image.channels();

	for (int j= 1; j<image.rows-1; j++) {
    
     // for all rows (except first and last)

		const uchar* previous= image.ptr<const uchar>(j-1); // previous row
		const uchar* current= image.ptr<const uchar>(j);	// current row
		const uchar* next= image.ptr<const uchar>(j+1);		// next row

		uchar* output= result.ptr<uchar>(j);	// output row

		for (int i=nchannels; i<(image.cols-1)*nchannels; i++) {
    
    

			// apply sharpening operator
			*output++= cv::saturate_cast<uchar>(5*current[i]-current[i-nchannels]-current[i+nchannels]-previous[i]-next[i]); 
		}
	}

	// Set the unprocess pixels to 0 屏蔽边缘像素
	result.row(0).setTo(cv::Scalar(0));
	result.row(result.rows-1).setTo(cv::Scalar(0));
	result.col(0).setTo(cv::Scalar(0));
	result.col(result.cols-1).setTo(cv::Scalar(0));
}

4.实现简单的图像运算

Mat进行实现图像的一些简单操作:

	cv::addWeighted(image1,0.7,image2,0.9,0.,result);
	// using overloaded operator
	result= 0.7*image1+0.9*image2;
	//两种操作等效

5.图像重映射

在日常处理中,诸如投影变换,重采样,等等变换中,需要根据对应像素坐标位置计算目标位置,可通过重映射来快速实现:
void wave(const cv::Mat &image, cv::Mat &result) {
    
    

	// the map functions
	cv::Mat srcX(image.rows,image.cols,CV_32F); // x-map
	cv::Mat srcY(image.rows,image.cols,CV_32F); // y-map

	// creating the mapping
	for (int i=0; i<image.rows; i++) {
    
    
		for (int j=0; j<image.cols; j++) {
    
    
		    //使用指针会更加高效 这里没有
			srcX.at<float>(i,j)= j;
			srcY.at<float>(i,j)= i+3*sin(j/6.0);
			// horizontal flipping
			// srcX.at<float>(i,j)= image.cols-j-1;
			// srcY.at<float>(i,j)= i;
		}
	}

	// applying the mapping 重映射函数
	cv::remap(image,  // source image
		      result, // destination image
			  srcX,   // x map
			  srcY,   // y map
			  cv::INTER_LINEAR); // interpolation method
}

该节相关代码已上传到个人文档,按需下载
链接: 点击下载

猜你喜欢

转载自blog.csdn.net/weixin_44242403/article/details/146488065
今日推荐