OpenCv 如何对图像的像素进行操作
對圖像的像素進行操作,我們可以實現空間增強,反色等目的。讓我們先來看一下內存空間中圖像矩陣,也就是Mat的矩陣數值部分是怎么存儲的:
如果圖像是一幅灰度圖像,他就像這樣,從左到右,從上到下,依次是矩陣的每一行每一列,這時候矩陣M(i,j)的值自然就是當前點的灰度值了。
而對于一幅彩色圖像,由于它的像素分量channel并不是一個,所以每一列又分為了幾個channel。拿常見的RGB圖像來說,就像這樣:
從這張圖上,就可以比較清楚地看出來在內存中矩陣是如何存儲多channel圖像的了。這里要注意的是在RGB模型中,每一個子列依次為BGR,也就是正好是顛倒的,第一個分量是藍色,第二個是綠色,第三個是紅色。
清楚了圖像在內存中的存儲方式,我們也就可以來進行像素值的操作了。在這里,我們舉這樣一個例子。我們對一幅灰度圖像的灰度值進行變換:
小于100的灰度值被統一映射為0;100到200之間的灰度值被映射為100;大于200的灰度值被映射為200.
主函數如下:
<pre name="code" class="cpp">int main() {string picName="lena.jpg";Mat A=imread (picName,CV_LOAD_IMAGE_GRAYSCALE); //讀入灰度圖像uchar table[256]; //映射表,規定了變換前后灰度值的對應關系 table[gray_value_before]=gray_value_afterfor (int i=0;i<256;i++){table[i]=i/100*100; //這里利用了C++的語言特性i/100只會留下整數部分}imshow("變換前",A);Mat B=ChangeImg (A,table); //變換函數imshow ("變換后",B);waitKey ();return 0; }
首先,我們用指針方式對圖像的像素點灰度值進行操作:
<pre name="code" class="cpp">Mat ChangeImg(Mat &img,const uchar* table) {CV_Assert(img.depth ()!=sizeof(uchar)); //聲明只對深度8bit的圖像操作int channels=img.channels (); //獲取圖像channelint nrows=img.rows; //矩陣的行數int ncols=img.cols*channels; //矩陣的總列數=列數*channel分量數if (img.isContinuous ()) //判斷矩陣是否連續,若連續,我們相當于只需要遍歷一個一維數組{ncols*=nrows;nrows=1; //一維數組}//遍歷像素點灰度值for (int i=0;i<nrows;i++){uchar *p=img.ptr<uchar>(i); //獲取行地址for (int j=0;j<ncols;j++){p[j]=table[p[j]]; //修改灰度值}}return img; }
這里,我們獲取了每一行開始處的指針,然后遍歷至該行末尾。如果矩陣是以連續方式存儲的,我們只需請求一次指針、然后一路遍歷下去就行。彩色圖像的情況有必要加以注意:因為三個通道的原因,我們需要遍歷的元素數目也是3倍。
或者,我們可以使用data。data會從Mat中返回指向矩陣第一行第一列的指針。注意如果該指針為NULL則表明對象里面無輸入,所以這是一種簡單的檢查圖像是否被成功讀入的方法。當矩陣是連續存儲時,我們就可以通過遍歷 data 來掃描整個圖像。例如,一個灰度圖像,其操作如下:
uchar* p = img.data; for( unsigned int i =0; i < ncol*nrows; ++i)*p++ = table[*p];
或者,更安全的方法,我們可以使用迭代器。在迭代法中,所需要做的僅僅是獲得圖像矩陣的begin和end,然后增加迭代直至從begin到end。將*操作符添加在迭代指針前,即可訪問當前指向的內容。
Mat& ScanImageAndReduceIterator(Mat& I, const uchar* const table) {// accept only char type matricesCV_Assert(I.depth() != sizeof(uchar)); const int channels = I.channels();switch(channels){case 1: {MatIterator_<uchar> it, end; for( it = I.begin<uchar>(), end = I.end<uchar>(); it != end; ++it)*it = table[*it];break;}case 3: {MatIterator_<Vec3b> it, end; for( it = I.begin<Vec3b>(), end = I.end<Vec3b>(); it != end; ++it){(*it)[0] = table[(*it)[0]];(*it)[1] = table[(*it)[1]];(*it)[2] = table[(*it)[2]];}}}return I; }
注意,在這里對3通道的圖像進行操作的時候,使用到了Vec3b。Vec3b作為一個對三元向量的數據結構,用在這里正好是能夠表示RGB的三個分量。如果對于彩色圖像,仍然用uchar的話,則只能獲得3通道中的B分量。比如我們可以這樣打印出圖像的RGB三個分量:
for (int i=0;i<img.rows;i++){const Vec3b* Mpoint=img.ptr <Vec3b>(i);for (int j=0;j<img.cols;j++){Vec3b intensity=*(Mpoint+j);cout<<"R:"<<int(intensity[2])<<" G"<<int(intensity[1])<<" B"<<int(intensity[0])<<" ";}cout<<endl;}
這里使用指針,當然也可以使用上面的迭代器。
然而,OpenCV里面已經有了相應函數可以讓我們更加方便地對像素進行操作,那便是LUT函數,而且推薦使用OpenCV的內建函數,因為已經針對芯片做了優化設計,使得速度有很大提升。
函數原型為:void LUT(InputArray src, InputArray lut, OutputArray dst, int interpolation=0 )
實現的映射關系如下所示:
也就是說比如原來src中值為1會映射為table[1]所對應的值再加上d。
所以上面的操作,我們其實只需要使用LUT函數就可以了。結合我們自己設計的table表,就能夠實現對圖像的操作。
int main() {string picName="lena.jpg";Mat A=imread (picName,CV_LOAD_IMAGE_GRAYSCALE); //讀入灰度圖像Mat lookUpLut(1,256,CV_8UC1); //建立一個256個元素的映射表imshow ("變換前",A);for (int i=0;i<256;i++){lookUpLut.at<uchar>(i)=i/100*100;}Mat B;LUT (A,lookUpLut,B);imshow ("變換后",B);waitKey ();return 0; }
下面的圖就是效果啦~~~
總結
以上是生活随笔為你收集整理的OpenCv 如何对图像的像素进行操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SSD算法 模板 匹配
- 下一篇: MATLAB中的分类器