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