亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

OpenCV?通過Mat遍歷圖像的方法匯總

 更新時(shí)間:2022年02月25日 14:45:01   作者:一杯清酒邀明月  
對(duì)圖像中的所有點(diǎn)或特殊點(diǎn)進(jìn)行運(yùn)算,所以遍歷圖像就顯得很重要,如何高效的遍歷圖像是一個(gè)很值得探討的問題,本文給大家?guī)砹硕喾N方法操作OpenCV?通過Mat遍歷圖像,感興趣的朋友一起看看吧

我們?cè)趯?shí)際應(yīng)用中對(duì)圖像進(jìn)行的操作,往往并不是將圖像作為一個(gè)整體進(jìn)行操作,而是對(duì)圖像中的所有點(diǎn)或特殊點(diǎn)進(jìn)行運(yùn)算,所以遍歷圖像就顯得很重要,如何高效的遍歷圖像是一個(gè)很值得探討的問題。

Color Reduce
還是使用經(jīng)典的Reduce Color的例子,即對(duì)圖像中的像素表達(dá)進(jìn)行量化。如常見的RGB24圖像有256×256×256中顏色,通過Reduce Color將每個(gè)通道的像素減少8倍至256/8=32種,則圖像只有32×32×32種顏色。假設(shè)量化減少的倍數(shù)是N,則代碼實(shí)現(xiàn)時(shí)就是簡(jiǎn)單的value/N*N,通常我們會(huì)再加上N/2以得到相鄰的N的倍數(shù)的中間值,最后圖像被量化為(256/N)×(256/N)×(256/N)種顏色。

并對(duì)圖像降色彩后的彩色直方圖進(jìn)行統(tǒng)計(jì)。

方法一、直接對(duì)圖像像素修改.at<typename>(i,j)

Mat類提供了一個(gè)at的方法用于取得圖像上的點(diǎn),它是一個(gè)模板函數(shù),可以取到任何類型的圖像上的點(diǎn)。

void colorReduce(Mat& image,int div)
{
    for(int i=0;i<image.rows;i++)
    {
        for(int j=0;j<image.cols;j++)
        {
            image.at<Vec3b>(i,j)[0]=image.at<Vec3b>(i,j)[0]/div*div+div/2;
            image.at<Vec3b>(i,j)[1]=image.at<Vec3b>(i,j)[1]/div*div+div/2;
            image.at<Vec3b>(i,j)[2]=image.at<Vec3b>(i,j)[2]/div*div+div/2;
        }
    }
}

通過上面的例子我們可以看出,at方法取圖像中的點(diǎn)的用法:

image.at<uchar>(i,j):取出灰度圖像中i行j列的點(diǎn)。
image.at<Vec3b>(i,j)[k]:取出彩色圖像中i行j列第k通道的顏色點(diǎn),k=[0,1,2],分別代表B,G,R。

其中uchar,Vec3b都是圖像像素值的類型,不要對(duì)Vec3b這種類型感覺害怕,其實(shí)在core里它是通過typedef Vec<T,N>來定義的,N代表元素的個(gè)數(shù),T代表類型。

更簡(jiǎn)單一些的方法:OpenCV定義了一個(gè)Mat的模板子類為Mat_,它重載了operator()讓我們可以更方便的取圖像上的點(diǎn)。

Mat_<uchar> im=image;
im(i,j)=im(i,j)/div*div+div/2;

二、用指針.ptr<uchar>(k)來遍歷輸入圖像,數(shù)組[]生成輸出圖像

上面的例程中可以看到,我們實(shí)際喜歡把原圖傳進(jìn)函數(shù)內(nèi),但是在函數(shù)內(nèi)我們對(duì)原圖像進(jìn)行了修改,而將原圖作為一個(gè)結(jié)果輸出,很多時(shí)候我們需要保留原圖,這樣我們需要一個(gè)原圖的副本。

void colorReduce(const Mat& image,Mat& outImage,int div)
{
    // 創(chuàng)建與原圖像等尺寸的圖像
    outImage.create(image.size(),image.type());
    int nr=image.rows;
    // 將3通道轉(zhuǎn)換為1通道
    int nl=image.cols*image.channels();
    for(int k=0;k<nr;k++)
    {
        // 每一行圖像的指針
        const uchar* inData=image.ptr<uchar>(k);
        uchar* outData=outImage.ptr<uchar>(k);
        for(int i=0;i<nl;i++)
        {
            outData[i]=inData[i]/div*div+div/2;
        }
    }
}

從上面的例子中可以看出,取出圖像中第i行數(shù)據(jù)的指針:image.ptr<uchar>(i)。

值得說明的是:程序中將3通道的數(shù)據(jù)轉(zhuǎn)換為1通道,在建立在每一行數(shù)據(jù)元素之間在內(nèi)存里是連續(xù)存儲(chǔ)的,每個(gè)像素三通道像素按順序存儲(chǔ)。也就是一幅圖像數(shù)據(jù)最開始的三個(gè)值,是最左上角的那像素的三個(gè)通道的值。

但是這種用法不能用在行與行之間,因?yàn)閳D像在OpenCV里的存儲(chǔ)機(jī)制問題,行與行之間可能有空白單元。這些空白單元對(duì)圖像來說是沒有意思的,只是為了在某些架構(gòu)上能夠更有效率,比如intel MMX可以更有效的處理那種個(gè)數(shù)是4或8倍數(shù)的行。但是我們可以申明一個(gè)連續(xù)的空間來存儲(chǔ)圖像,這個(gè)話題引入下面最為高效的遍歷圖像的機(jī)制。

三、用指針.ptr<uchar>(k)來遍歷輸入圖像,指針方式生成輸出圖像

與上述方法二遍歷圖像的方法相同,而生成輸出圖像的方式從數(shù)組換成了指針的方式。因此只需改動(dòng)一句話。

void colorReduce(const Mat& image,Mat& outImage,int div)
{
    // 創(chuàng)建與原圖像等尺寸的圖像
    outImage.create(image.size(),image.type());
    int nr=image.rows;
    // 將3通道轉(zhuǎn)換為1通道
    int nl=image.cols*image.channels();
    for(int k=0;k<nr;k++)
    {
        // 每一行圖像的指針
        const uchar* inData=image.ptr<uchar>(k);
        uchar* outData=outImage.ptr<uchar>(k);
        for(int i=0;i<nl;i++)
        {
            *outData++ = *inData++ / div*div + div / 2;
        }
    }
}

四、用指針.ptr<uchar>(k)來遍歷輸入圖像,指針方式結(jié)合位運(yùn)算生成輸出圖像

與上述方法遍歷圖像的方法相同,而生成輸出圖像的方式從加減乘除基本四則運(yùn)算的方式換成了位運(yùn)算的方式。

這里特別需要注意的是,位運(yùn)算的優(yōu)先級(jí)是低于乘除加減的,所以一定要在位運(yùn)算加括號(hào)。

void colorReduce(const Mat& image, Mat& outImage, int div)
{
    // 創(chuàng)建與原圖像等尺寸的圖像
    outImage.create(image.size(), image.type());
    int nr = image.rows;
    // 將3通道轉(zhuǎn)換為1通道
    int nl = image.cols*image.channels();
    //對(duì)數(shù)換底公式log a(b) = log b/log a
    int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0));
    // mask used to round the pixel value  e.g. for div=16, mask= 0xF0
    uchar mask = 0xFF << n;
    for (int k = 0; k<nr; k++)
    {
        // 每一行圖像的指針
        const uchar* inData = image.ptr<uchar>(k);
        uchar* outData = outImage.ptr<uchar>(k);
        for (int i = 0; i<nl; i++)
        {
            //進(jìn)行位運(yùn)算時(shí)要注意加括號(hào),位運(yùn)算優(yōu)先級(jí)低于+-*/
            *outData++ = (*inData++ & mask)  + div / 2;
        }
    }
}

五、用指針.ptr<uchar>(k)來遍歷輸入圖像,指針方式結(jié)合取模運(yùn)算生成輸出圖像

與上述方法遍歷圖像的方法相同,而生成輸出圖像的方式從位運(yùn)算的方式換成了取模運(yùn)算的方式。

void colorReduce(const Mat& image, Mat& outImage, int div)
{
    // 創(chuàng)建與原圖像等尺寸的圖像
    outImage.create(image.size(), image.type());
    int nr = image.rows;
    // 將3通道轉(zhuǎn)換為1通道
    int nl = image.cols*image.channels();

    int n = static_cast<int>(log(static_cast<double>(div)) / log(2.0));
    // mask used to round the pixel value  e.g. for div=16, mask= 0xF0
    uchar mask = 0xFF << n;
    for (int k = 0; k<nr; k++)
    {
        // 每一行圖像的指針
        const uchar* inData = image.ptr<uchar>(k);
        uchar* outData = outImage.ptr<uchar>(k);
        for (int i = 0; i<nl; i++)
        {
            int Data = *inData++;
            *outData++ = Data - Data%div + div / 2;
        }
    }
}

六、連續(xù)圖像isContinuous()函數(shù)方法。

上面已經(jīng)提到過了,一般來說圖像行與行之間往往存儲(chǔ)是不連續(xù)的,但是有些圖像可以是連續(xù)的,Mat提供了一個(gè)檢測(cè)圖像是否連續(xù)的函數(shù)isContinuous()。當(dāng)圖像連通時(shí),我們就可以把圖像完全展開,看成是一行。

void colorReduce(const Mat& image,Mat& outImage,int div)
{
    int nr=image.rows;
    int nc=image.cols;
    outImage.create(image.size(),image.type());
    if(image.isContinuous()&&outImage.isContinuous())
    {
        nr=1;
        nc=nc*image.rows*image.channels();
    }
    for(int i=0;i<nr;i++)
    {
        const uchar* inData=image.ptr<uchar>(i);
        uchar* outData=outImage.ptr<uchar>(i);
        for(int j=0;j<nc;j++)
        {
            *outData++=*inData++/div*div+div/2;
        }
    }
}

用指針除了用上面的方法外,還可以用指針來索引固定位置的像素:

image.step返回圖像一行像素元素的個(gè)數(shù)(包括空白元素),image.elemSize()返回一個(gè)圖像像素的大小。

image.at<uchar>(i,j)=image.data+i*image.step+j*image.elemSize();

七、迭代器Mat_iterator方法。

下面的方法可以讓我們來為圖像中的像素聲明一個(gè)迭代器:

MatIterator_<Vec3b> it;
 Mat_<Vec3b>::iterator it;

如果迭代器指向一個(gè)const圖像,則可以用下面的聲明:

MatConstIterator<Vec3b> it;
或者
Mat_<Vec3b>::const_iterator it;

下面我們用迭代器來簡(jiǎn)化上面的colorReduce程序:

void colorReduce(const Mat& image,Mat& outImage,int div)
{
    outImage.create(image.size(),image.type());
    MatConstIterator_<Vec3b> it_in=image.begin<Vec3b>();
    MatConstIterator_<Vec3b> itend_in=image.end<Vec3b>();
    MatIterator_<Vec3b> it_out=outImage.begin<Vec3b>();
    MatIterator_<Vec3b> itend_out=outImage.end<Vec3b>();
    while(it_in!=itend_in)
    {
        (*it_out)[0]=(*it_in)[0]/div*div+div/2;
        (*it_out)[1]=(*it_in)[1]/div*div+div/2;
        (*it_out)[2]=(*it_in)[2]/div*div+div/2;
        it_in++;
        it_out++;
    }
}

如果你想從第二行開始,則可以從

image.begin<Vec3b>()+image.rows

開始。

上面7種方法中,第4種方法的效率最高!

到此這篇關(guān)于OpenCV 通過Mat遍歷圖像的幾種方法的文章就介紹到這了,更多相關(guān)OpenCV遍歷圖像內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論