作者:咕唧咕唧liukun321
來自:http://blog.csdn.NET/liukun321
本質上說一張圖像就是由數值組成的矩陣。OpenCV 2.x由 cv::Mat 這個數據結構來表示一張圖像。矩陣的每一個元素代表了一個像素。對於彩色圖像而言矩陣的元素是一個三元數。對圖像有了這個新的認識,下面可以試着藉助opencv處理圖像了。
先來看一下今天要處理的圖像:今天的主題是存取像素,首先來看一下如何存取像素值。其實對於像素值的操作都可以由cv::Mat類中成員直接或間接實現,cv::Mat有若干成員函數可以獲取圖像的數據及屬性。
操作單個像素方法:
at(int y, int x)
cv::mat的成員函數: at(int y, int x)可以用來存取圖像中對應座標爲(x,y)的元素座標。但是在使用它時要注意,在編譯期必須要已知圖像的數據類型,這是因爲cv::mat可以存放任意數據類型的元素。因此at方法的實現是用模板函數來實現的。
使用方法:假設提前已知一幅圖像img的數據類型爲 unsigned char型灰度圖(單通道),要對座標爲(10,12)的像素重新賦值爲128,則對應操作如下:
- img.at<uchar>(12,10) = 128;
img.at<uchar>(12,10) = 128;
如果要操作的圖片img是一幅數據類型同樣爲unsigned char的彩色圖片,再次要求將座標(10,12)的像素賦值爲128。這個操作跟上面的就有點區別了,需要對這個像素三個通道的每個對應元素賦值,Opencv中圖像三原色在內存中的排列順序爲B-G-R(見下面註釋),操作過程如下:
- img.at<cv::Vec3b>(12,10) [0]= 128;//B
- img.at< cv::Vec3b >(12,10) [1]= 128;//G
- img.at< cv::Vec3b >(12,10) [2]= 128;//R
img.at<cv::Vec3b>(12,10) [0]= 128;//B
img.at< cv::Vec3b >(12,10) [1]= 128;//G
img.at< cv::Vec3b >(12,10) [2]= 128;//R
瞭解了at方法的用法,下面就嘗試一下使用at方法對剛纔的圖片做一個簡單的處理(將圖像中加入椒鹽噪點)。椒鹽噪點是一種特殊的噪點,是隨機的將圖像的部分像素設置爲黑色或白色。
既然灰度圖與彩色圖像對單個元素的操作方式不同,這就需要有一個圖像類型判斷的過程。
- cv::Mat image = cv::imread("test.jpg");
- if(image.channles() == 1)
- {
- //灰度圖
- }else{
- //彩色圖
- }
cv::Mat image = cv::imread("test.jpg");
if(image.channles() == 1)
{
//灰度圖
}else{
//彩色圖
}
清楚了這些過程,下面就來看看添加椒鹽噪點函數的實現過程:
- #include <opencv2/opencv.hpp>
- #include<cstdlib>
- using namespace cv;
- void salt(Mat &img,int saltNum)
- {
- int x,y;
- int i ;
- for(i = 0;i < saltNum; i++)
- {
- x = rand()%img.cols;
- y = rand()%img.rows;
- if(img.channels() == 1)
- {
- img.at<uchar>(y,x) = 255;
- }else if(img.channels() == 3)
- {
- img.at<Vec3b>(y,x)[0] = 255;
- img.at<Vec3b>(y,x)[1] = 255;
- img.at<Vec3b>(y,x)[2] = 255;
- }
- }
- }
- int main()
- {
- Mat image = imread("../test.jpg");
- Mat result;
- result = image.clone();
- salt(result,3000);
- namedWindow("src(http://blog.csdn.net/liukun321)" , CV_WINDOW_AUTOSIZE);
- imshow("src(http://blog.csdn.net/liukun321)", image);
- imshow("dst(http://blog.csdn.net/liukun321)", result);
- waitKey();
- return 0;
- }
#include <opencv2/opencv.hpp>
#include<cstdlib>
using namespace cv;
void salt(Mat &img,int saltNum)
{
int x,y;
int i ;
for(i = 0;i < saltNum; i++)
{
x = rand()%img.cols;
y = rand()%img.rows;
if(img.channels() == 1)
{
img.at<uchar>(y,x) = 255;
}else if(img.channels() == 3)
{
img.at<Vec3b>(y,x)[0] = 255;
img.at<Vec3b>(y,x)[1] = 255;
img.at<Vec3b>(y,x)[2] = 255;
}
}
}
int main()
{
Mat image = imread("../test.jpg");
Mat result;
result = image.clone();
salt(result,3000);
namedWindow("src(http://blog.csdn.net/liukun321)" , CV_WINDOW_AUTOSIZE);
imshow("src(http://blog.csdn.net/liukun321)", image);
imshow("dst(http://blog.csdn.net/liukun321)", result);
waitKey();
return 0;
}
程序運行後的效果圖:
原圖
加入椒鹽噪聲後效果
其實除了at方法操作像素,還可以使用opencv提供的類cv::Mat_ 來實現。cv::Mat_是一個模板子類。這個類定義了很多額外的方法,但是沒有提供公共的成員變量。如果已知了矩陣的類型,使用cv::Mat_會帶來很多便利。它的使用方法如下:
- cv::Mat_<uchar> img = imread("test.jpg");
- img(10,12) = 128;//10行 12列
cv::Mat_<uchar> img = imread("test.jpg");
img(10,12) = 128;//10行 12列
還有一種操作像素的方法:使用Mat類的ptr()方法配合cols 、rows、step、elemSize等成員變量,直接進行指針操作。下面先來說說這幾個成員變量
cols代表圖像的列數
rows代表圖像的高度
step 代表以字節爲單位的圖像寬度
elemSize 代表像素的大小 (比如一個三通道uchar 型矩陣,返回值爲3)
prt()方法同樣是個模板類,需要編譯期已知像素點的類型:
- cv::Mat_<uchar> img = imread("test.jpg");
- uchar* addr = img.ptr<uchar>(10);//返回10行的地址
- addr +=12;//單通道灰度圖
- *addr = 128;
cv::Mat_<uchar> img = imread("test.jpg");
uchar* addr = img.ptr<uchar>(10);//返回10行的地址
addr +=12;//單通道灰度圖
*addr = 128;
同樣完成了對第10行第12列像素的操作。若圖象爲三通道彩色圖:
- cv::Mat_<uchar> img = imread("test.jpg");
- uchar* addr = img.ptr<uchar>(10);//返回10行的地址
- addr +=12* img.elemSize;//單通道灰度圖
- *addr = 128;
cv::Mat_<uchar> img = imread("test.jpg");
uchar* addr = img.ptr<uchar>(10);//返回10行的地址
addr +=12* img.elemSize;//單通道灰度圖
*addr = 128;
addr +=12* img.elemSize是因爲彩色圖象在內存中的存儲方式:圖像緩衝區中的前三個字節對應圖像左上角第一個像素的三個通道值,接下來的三個字節對應第一行的第二個像素,以此類推。而且注意Opencv默認是使用BGR的通道順序。
到此已經介紹了3中操作圖像中像素的方法。除這三種以外還有一種使用迭代器的操作。今天就不再介紹了。