OpenCV——像素定位,遍歷與操做

在Mat中訪問獨立元素,只須要輸入行號和列號便可,下面經過一個例子來講明最基本的像素操做。安全

我針對一個圖像,先加入鹽噪聲,而後使用均值濾波手動緩解噪聲的影響:函數

加鹽噪音的方法:spa

void salt(cv::Mat image, int n)//(圖像矩陣,噪音點的個數)
{
    int i, j;            
    for (int k = 0; k < n; k++) //n個噪音點,n個循環
    {
        i = rand() % image.rows;    //隨機找一行
        j = rand() % image.cols;    //隨機找一列

        if (image.type() == CV_8UC1)//對於灰度圖像
            image.at<uchar>(i, j) =255;    //將這個點變爲白點
        else if (image.type() == CV_8UC3)//對於三通道彩色圖像
        {
            image.at<cv::Vec3b>(i, j)[0] = 255;//將三個
            image.at<cv::Vec3b>(i, j)[1] = 255;//通道都
            image.at<cv::Vec3b>(i, j)[2] = 255;//調成最大值
        }
    }
}

重點函數:訪問像素:image.at<uchar>(i, j)//image.at<cv::Vec3b>(i, j)[0]指針

手動編寫的均值濾波,將一個點的值變爲周圍9個點的平均值:code

void averagefilter(cv::Mat image)    //3X3均值濾波
{
    for (int i = 1; i < image.rows - 1; i++) //從2到max-1行
    {
        for (int j = 1; j < image.cols - 1; j++)//從2到max-1列
        {
            if (image.type() == CV_8UC1)
                image.at<uchar>(i, j) = image.at<uchar>(i, j) * 1 / 9 + image.at<uchar>(i - 1, j) * 1 / 9 +image.at<uchar>(i+1, j) * 1 / 9+\
                                    image.at<uchar>(i, j - 1) * 1 / 9 + image.at<uchar>(i - 1, j - 1) * 1 / 9 + image.at<uchar>(i+1, j-1) * 1 / 9+\
                                    image.at<uchar>(i, j+1) * 1 / 9+ image.at<uchar>(i-1, j+1) * 1 / 9 +image.at<uchar>(i+1, j+1) * 1 / 9;
            else if (image.type() == CV_8UC3)
            {
                image.at<cv::Vec3b>(i, j)[0] = image.at<cv::Vec3b>(i, j)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[0] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j - 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[0] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j + 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[0] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[0] * 1 / 9;

                image.at<cv::Vec3b>(i, j)[1] = image.at<cv::Vec3b>(i, j)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[1] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j - 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[1] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j + 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[1] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[1] * 1 / 9;

                image.at<cv::Vec3b>(i, j)[2] = image.at<cv::Vec3b>(i, j)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j)[2] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j - 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j - 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j - 1)[2] * 1 / 9 + \
                    image.at<cv::Vec3b>(i, j + 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i - 1, j + 1)[2] * 1 / 9 + image.at<cv::Vec3b>(i + 1, j + 1)[2] * 1 / 9;
            }
        }
    }
}

主函數:blog

cv::Mat image = cv::imread("D://ALL_ESCAPE.jpg");
    salt(image, 3000);
    cv::namedWindow("Salted");
    cv::imshow("Salted", image);
    cv::waitKey(1);

    averagefilter(image);
    cv::namedWindow("Filtered");
    cv::imshow("Filtered", image);
    cv::waitKey(0);

    return 0;

這回咱們找一個暗一些的圖來作實驗,來個1000個點的:it

 

 用指針能夠更高效的掃描圖像,下面看一個減小圖像中位深度的例子:io

首先爲了方便運算,OpenCV中 cv::Mat提供了一個方法能夠直接訪問圖像中某一行的地址:class

uchar *pointer = image.ptr<uchar>(j) // image: image matrix   j:number of column循環

而對於列來講, pointer[0]表示第一列的第一個通道元素,pointer[1]表示第一列第二通道,pointer[5]表示第二列第三通道。

也就是說這個圖像若爲100*100的RGB圖像, 則其指針爲100*300;最後一列能夠表示爲pointer[299];

下面是程序,一個就地轉換的實例:

void Quantization(cv::Mat image, int div)
{
    int row_number = image.rows;         
    int col_number = image.cols*image.channels();

    for (int i = 0; i < row_number; i++)
    {
        uchar *row_pointer = image.ptr<uchar>(i);
        for (int j = 0; j < col_number; j++)
        {
            row_pointer[j] = row_pointer[j] / div*div + div / 2;
        }
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    cv::Mat image;
    image = cv::imread("D://Scene.jpg");
    cv::namedWindow("Original");
    cv::imshow("Original", image);
    cv::waitKey(1);
    Quantization(image, 64);
    cv::namedWindow("Reduced");
    cv::imshow("Reduced", image);
    cv::waitKey(0);
    return 0;
}

重點函數:uchar *row_pointer = image.ptr<uchar>(i);//i:行數 ; row_pointer[列數/通道數]

運行結果以下:

 

在上面的例子中,我使用了循環來遍歷全部的像素,OpenCV中提供了迭代器來封裝這些循環,可使操做更加安全:

void ColorReduce(cv::Mat &image, int div = 64)
{
    cv::Mat_<cv::Vec3b>::iterator it = image.begin<cv::Vec3b>();
    cv::Mat_<cv::Vec3b>::iterator itend = image.end<cv::Vec3b>();
    for (; it != itend; ++it)
    {
        (*it)[0] = (*it)[0] / div*div + div / 2;
        (*it)[1] = (*it)[1] / div*div + div / 2;
        (*it)[2] = (*it)[2] / div*div + div / 2;
    }
}

從循環中咱們能夠看到it其實是一個地址

若是執行cout<<*it;一次,咱們能夠看到輸出是:

[205, 205, 205],是一個像素點上的3個通道的向量,這個向量就由函數cv::Mat_<cv::Vec3b>::iterator定義,因爲<cv::Vec3b>的存在,因此三個通道都被看作是一個總體。

爲方便你們理解uchar與<cv::Vec3b>的區別,我將最開始的減色程序改寫成<cv::Vec3b>的形式:

void Quantization(cv::Mat image, int div)
{
    int row_number = image.rows;         
    int col_number = image.cols;

    for (int i = 0; i < row_number; i++)
    {
        cv::Vec3b *row_pointer = image.ptr<cv::Vec3b>(i);
        for (int j = 0; j < col_number; j++)
        {
            row_pointer[j][0] = row_pointer[j][0] / div*div + div / 2;
            row_pointer[j][1] = row_pointer[j][1] / div*div + div / 2;
            row_pointer[j][2] = row_pointer[j][2] / div*div + div / 2;
        }
    }
}
相關文章
相關標籤/搜索