opencv2操作像素的几种方法

opencv2 是C++接口,这就让我有了顺便复习C++的机会啊。好吧,一切重头开始来过吧。


opencv所有的操作都是从操作图像开始的,所以就要从图像的组成元素之图像像素说起啦。所以就开始介绍几种处理像素的方法:


1,at

直接访问图像位置的像素 例子就是使用at直接给图像位置(j,i)的像素赋值255,结果就是图像上分布了椒盐噪声。后面会讲到哪种方法处理椒盐噪声效果好了。

#include <opencv2/opencv.hpp>

using namespace cv;

class class_ch2{
private:
	Mat src;
	Mat result;
public:
	class_ch2(const Mat& img):src(img),result(img){
		assert(img.size>0);
		assert(src.size>0);
		assert(result.size>0);
	}

	void salt_proc(int n){
		for (int k=0;k<n;k++){
			int i = rand()%src.cols;
			int j = rand()%src.rows;
			if (src.channels() == 1){
				result.at<uchar>(j,i) = 255;
			} 
			else{
				result.at<Vec3b>(j,i)[0] = 255;
				result.at<Vec3b>(j,i)[1] = 255;
				result.at<Vec3b>(j,i)[2] = 255;
			}
		}
	}

	Mat get_result(){
		return result;
	}
};
int main(){

	Mat src = imread("bridgit.jpg");
	imshow("src",src);
	class_ch2 cla_ch2(src);
	cla_ch2.salt_proc(3000);
	imshow("result",cla_ch2.get_result());

	waitKey(0);
}

注意哦!!!!!!!!!!!!!!!!!

如果事先知道了图像类型,那么就可以使用

Mat_<uchar> src2 = src;
src2(20,20) = 255;   而不用 img.at<uchar>(j,i) = 255; 简洁了许多,但是很多时候我们不确定我们使用的图像在处理过程中是否会改变类型,所以就先用Mat了,如果已经确定了处理图像的类型,那么请不要介意,尽情的使用Mat_吧!!!


2,指针

在C版本的opencv中,貌似都是指针,因为直接定义就是iplimage* img = 。。。。。所以大部分都是使用指针呢,所以指针并不陌生哦,只是在C++版本中多了类型控制,现在这个例子就是指针处理图像,让图像颜色级数降低为原级数的pow(2,n)分之一;

	void colorReduce(const int& n){
		//div_num = pow(2,n);
		src.copyTo(result);
		uchar mask = 0xFF<<n;
		int height = result.rows;
		int width = result.cols*result.channels();
		for (int j= 0;j<height;j++){
			uchar *pt_data = result.ptr<uchar>(j);
			for (int i=0;i<width;i++){
				pt_data[i] = (pt_data[i]&mask) + pow(2.0,n)/2;
			}
		}
	}


好像水彩画的赶脚啊。这个是n=5的效果。因为位运算的效率很高,所以优先使用位运算。


3,isContinuous 判断图像是否在行尾有填补

因为出于效率的考虑,图像的一行经常被填补为4或者8的倍数,利于运算,所以当图像没有被填补的时候,就可以被认为是一个大小为宽X高的一维数组。

void colorReduce(const int& n){
		//div_num = pow(2,n);
		src.copyTo(result);
		uchar mask = 0xFF<<n;
		int height = result.rows;
		int width = result.cols*result.channels();
		<span style="color:#3333ff;">if (result.isContinuous()){
			width = height * width;
			height = 1;
		}</span>
		for (int j= 0;j<height;j++){
			uchar *pt_data = result.ptr<uchar>(j);
			for (int i=0;i<width;i++){
				pt_data[i] = (pt_data[i]&mask) + pow(2.0,n)/2;
			}
		}
	}
效果跟上面一样,就是效率的问题。对于连续图像来说很高效。

4,迭代器

既然是C++版本的,咋么能少了迭代器呢,模板都已经出现了的说。

	void colorReduce_it(const int& n){
		src.copyTo(result);
		uchar mask = 0xFF<<n;
		Mat_<Vec3b>::iterator it = result.begin<Vec3b>();
		Mat_<Vec3b>::iterator end = result.end<Vec3b>();
		for (;it!=end;it++){
			(*it)[0] = ((*it)[0]&mask) + pow(2.0,n)/2;
			(*it)[1] = ((*it)[1]&mask) + pow(2.0,n)/2;
			(*it)[2] = ((*it)[2]&mask) + pow(2.0,n)/2;
		}
	}
效果呢,就是跟上面一样,不过速度哦,就慢了。

如果之前对result是这样定义的话:

Mat_<Vec3b> result = src;那么两个迭代器的定义就是这样子的了。
<pre name="code" class="cpp">Mat_<Vec3b>::iterator it = result.begin();
Mat_<Vec3b>::iterator end = result.end();

最后对这几个遍历方法做个总结对比:

最具效率的一种方法:

	void colorReduce_fastest(int div = 32){
		src.copyTo(result);

		int height = result.rows;
		int width = result.cols;
		if (result.isContinuous()){
			width = height * width;
			height = 1;
		}

		int n = static_cast<int>(log(static_cast<double>(div))/log(2.0));
		uchar mask = 0xFF<<n;
		
		for (int j= 0;j<height;j++){
			uchar *pt_data = result.ptr<uchar>(j);
			for (int i=0;i<width;i++){
				*pt_data++ = (*pt_data&mask) + div/2;
				*pt_data++ = (*pt_data&mask) + div/2;
				*pt_data++ = (*pt_data&mask) + div/2;
			}
		}
	}

结果跟前面一样,但是速度是最快滴O(∩_∩)O哈哈~

像素的几种方法遍历方法就上面了哦。继续加油↖(^ω^)↗



发布了33 篇原创文章 · 获赞 9 · 访问量 5万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章