OpenCV——像素定位,遍历与操作

在Mat中访问独立元素,只需要输入行号和列号即可,下面通过一个例子来说明最基本的像素操作。

我针对一个图像,先加入盐噪声,然后使用均值滤波手动缓解噪声的影响:

加盐噪音的方法:

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个点的平均值:

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;
            }
        }
    }
}

主函数:

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个点的:

 

 用指针可以更高效的扫描图像,下面看一个减少图像中位深度的例子:

首先为了方便运算,OpenCV中 cv::Mat提供了一个方法可以直接访问图像中某一行的地址:

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;
        }
    }
}

 

posted @ 2016-09-27 23:35  铁杆  阅读(1518)  评论(0编辑  收藏  举报