1. 程式人生 > >OpenCV計算機視覺之訪問畫素值

OpenCV計算機視覺之訪問畫素值

#include <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

using namespace std;
using namespace cv;

void salt(cv::Mat image, int n) {
	int i,j;
	for (int k=0; k<n; k++) {
		// rand()是隨機數生成器
		i= std::rand()%image.cols;
		j= std::rand()%image.rows;
			if (image.type() == CV_8UC1) { // 灰度影象
			image.at<uchar>(j,i)= 255;
			} else if (image.type() == CV_8UC3) { // 彩色影象
				image.at<cv::Vec3b>(j,i)[0]= 255;
				image.at<cv::Vec3b>(j,i)[1]= 255;
				image.at<cv::Vec3b>(j,i)[2]= 255;
			}
	}
}

int main()
{
	// 開啟影象
	cv::Mat img= cv::imread("boldt.jpg",1);
	// 呼叫函式以新增噪聲
	salt(img,3000);
	// 顯示影象
	cv::namedWindow("Image");
	cv::imshow("Image",img);

	waitKey(0);
	return 0;
}

加入椒鹽噪聲(salt-and-pepper noise)的程式可以根據傳入的影象型別選擇合適的型別訪問Mat資料結構中的畫素值。當傳入的為灰度圖時,使用    image.at<uchar>(j,i)= 255;    來訪問畫素,效果如下圖所示:

當傳入的為彩色圖時,使用    image.at<cv::Vec3b>(j,i)[n]= 255;    來訪問畫素,效果如下圖所示:

 這裡訪問畫素的方法是:利用cv::Mat的at(int y,int x)方法可以訪問元素,at(int y,int x)方法是一個模板,所以在使用的時候必須指定訪問的型別。簡單的列舉一下各種型別:

/* \typedef

   Shorter aliases for the most popular specializations of Vec<T,n>
*/
typedef Vec<uchar, 2> Vec2b;
typedef Vec<uchar, 3> Vec3b;
typedef Vec<uchar, 4> Vec4b;

typedef Vec<short, 2> Vec2s;
typedef Vec<short, 3> Vec3s;
typedef Vec<short, 4> Vec4s;

typedef Vec<ushort, 2> Vec2w;
typedef Vec<ushort, 3> Vec3w;
typedef Vec<ushort, 4> Vec4w;

typedef Vec<int, 2> Vec2i;
typedef Vec<int, 3> Vec3i;
typedef Vec<int, 4> Vec4i;
typedef Vec<int, 6> Vec6i;
typedef Vec<int, 8> Vec8i;

typedef Vec<float, 2> Vec2f;
typedef Vec<float, 3> Vec3f;
typedef Vec<float, 4> Vec4f;
typedef Vec<float, 6> Vec6f;

typedef Vec<double, 2> Vec2d;
typedef Vec<double, 3> Vec3d;
typedef Vec<double, 4> Vec4d;
typedef Vec<double, 6> Vec6d;

這裡還用到了一個type()方法,此方法返回Mat畫素值型別,下面簡單列舉一下其型別:

#define CV_8UC1 CV_MAKETYPE(CV_8U,1)
#define CV_8UC2 CV_MAKETYPE(CV_8U,2)
#define CV_8UC3 CV_MAKETYPE(CV_8U,3)
#define CV_8UC4 CV_MAKETYPE(CV_8U,4)
#define CV_8UC(n) CV_MAKETYPE(CV_8U,(n))

#define CV_8SC1 CV_MAKETYPE(CV_8S,1)
#define CV_8SC2 CV_MAKETYPE(CV_8S,2)
#define CV_8SC3 CV_MAKETYPE(CV_8S,3)
#define CV_8SC4 CV_MAKETYPE(CV_8S,4)
#define CV_8SC(n) CV_MAKETYPE(CV_8S,(n))

#define CV_16UC1 CV_MAKETYPE(CV_16U,1)
#define CV_16UC2 CV_MAKETYPE(CV_16U,2)
#define CV_16UC3 CV_MAKETYPE(CV_16U,3)
#define CV_16UC4 CV_MAKETYPE(CV_16U,4)
#define CV_16UC(n) CV_MAKETYPE(CV_16U,(n))

#define CV_16SC1 CV_MAKETYPE(CV_16S,1)
#define CV_16SC2 CV_MAKETYPE(CV_16S,2)
#define CV_16SC3 CV_MAKETYPE(CV_16S,3)
#define CV_16SC4 CV_MAKETYPE(CV_16S,4)
#define CV_16SC(n) CV_MAKETYPE(CV_16S,(n))

#define CV_32SC1 CV_MAKETYPE(CV_32S,1)
#define CV_32SC2 CV_MAKETYPE(CV_32S,2)
#define CV_32SC3 CV_MAKETYPE(CV_32S,3)
#define CV_32SC4 CV_MAKETYPE(CV_32S,4)
#define CV_32SC(n) CV_MAKETYPE(CV_32S,(n))

#define CV_32FC1 CV_MAKETYPE(CV_32F,1)
#define CV_32FC2 CV_MAKETYPE(CV_32F,2)
#define CV_32FC3 CV_MAKETYPE(CV_32F,3)
#define CV_32FC4 CV_MAKETYPE(CV_32F,4)
#define CV_32FC(n) CV_MAKETYPE(CV_32F,(n))

#define CV_64FC1 CV_MAKETYPE(CV_64F,1)
#define CV_64FC2 CV_MAKETYPE(CV_64F,2)
#define CV_64FC3 CV_MAKETYPE(CV_64F,3)
#define CV_64FC4 CV_MAKETYPE(CV_64F,4)
#define CV_64FC(n) CV_MAKETYPE(CV_64F,(n))

cv::Mat_模板類因為每次呼叫都必須在模板引數中指明返回型別, 所以使用cv::Mat類的at方法有時會顯得冗長。 如果已經知道矩陣的型別, 就可以使用cv::Mat_類(cv::Mat類的模板子類) 。 cv::Mat_類定義了一些新的方法, 但沒有定義新的資料屬性, 因此這兩個類的指標或引用可以直接互相轉換。 在新方法中有一個operator(), 可用它直接訪問矩陣的元素。 因此可以這樣寫程式碼 (其中image是一個對應uchar矩陣的cv::Mat變數) :

// 用Mat_模板操作影象
cv::Mat_<uchar> im2(image);
im2(50,100)= 0; // 訪問第50行、 第100列處那個值

在建立cv::Mat_變數時, 我們就定義了它的元素型別, 因此在編譯時就已經知道operator()的返回型別。 使用操作符operator()和 使用at方法產生的結果是完全相同的, 只是前者的程式碼更簡短。