opencv:像素操作

版权声明:本文为博主原创文章,未经博主允许不得转载 https://blog.csdn.net/qit1314/article/details/85955128

图像的基本元素是像素。
在opencv中能够直接对cv::Mat类型的图像调用at函数读取或赋值某个像素
//在一张图像上增加椒盐噪声,image为输入图像。n为噪点个数

void salt(Mat &image, int n)
{
    for(int k = 0;k < n;k++)
    {
        //随机产生白色噪点
        int i = qrand()%image.cols;
        int j = qrand()%image.rows;
        //假设是灰度图每一个像素的存取类型为uchar,即8bit整型数
        if(image.channels() == 1){     
            image.at<uchar>(j,i) = 255;
        }
        //彩色图像有三个通道,像素存取类型为cv::Vec3b,即由三个uchar组成的向量。这里用下标[i]訪问每一个通道
        else{
            image.at<Vec3b>(j,i)[0] = 255;
            image.at<Vec3b>(j,i)[1] = 255;
            image.at<Vec3b>(j,i)[2] = 255;
        }
    }
}

需要注意的是:
确保制定的数据类型和壶镇中的数据类型一致。at方法本身不会进行任何数据类型转换。

我们还是以颜色衰减函数为例,分别用以上四种方法遍历实现。看看执行时间有何不同:
//at方法

void colorReduce1(Mat&image, int div = 64)
{
    int nl = image.rows; //图像的行数
    //图像每行的像素数
    int nc = image.cols * image.channels();
    for(int j =0;j<nl-2;j++)
    {
        for(int i =0;i<nc-2;i++)
        {
            image.at<Vec3b>(j,i)[0] = image.at<Vec3b>(j,i)[0]/div*div;
            image.at<Vec3b>(j,i)[1] = image.at<Vec3b>(j,i)[1]/div*div;
            image.at<Vec3b>(j,i)[2] = image.at<Vec3b>(j,i)[2]/div*div;
        }
    }
}

//行首指针方法
void colorReduce2(Mat&image, int div = 64)
{
    int nl = image.rows; //图像的行数
    //图像每行的像素数
    int nc = image.cols * image.channels();
    for(int j =0;j<nl;j++)
    {
        //得到第j行的首地址
        uchar* data = image.ptr<uchar>(j);
        //遍历每行的像素
        for(int i =0;i<nc;i++)
        {
            data[i] = data[i]/div*div;    //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
        }
    }
}

//一维数组
void colorReduce3(Mat&image, int div = 64)
{
    int nl = image.rows; //图像的行数
    //图像每行的像素数
    int nc = image.cols * image.channels();

    //假设图像连续
    if(image.isContinuous())
    {
        //reshape函数用于改变矩阵维度
        //图像行数为1,列数为原先的行数乘上列数
        image.reshape(1,image.cols*image.rows);
    }

    for(int j =0;j<nl;j++)
    {
        //得到第j行的首地址
        uchar* data = image.ptr<uchar>(j);
        //遍历每行的像素
        for(int i =0;i<nc;i++)
        {
            data[i] = data[i]/div*div;    //将每一个像素值都变为div的倍数,即将颜色数缩减了div倍
        }
    }
}

//迭代器方法
void colorReduce4(Mat&image, int div = 64)
{
    //得到初始位置的迭代器
    Mat_<Vec3b>::iterator it = image.begin<Vec3b>();
    //得到终止位置的迭代器
    Mat_<Vec3b>::iterator itend = image.end<Vec3b>();
    //遍历全部像素
    for(; it != itend; ++it){
        (*it)[0] = (*it)[0]/div*div;
        (*it)[1] = (*it)[1]/div*div;
        (*it)[2] = (*it)[2]/div*div;
    }
}

//測试4种像素遍历方式执行时间
void calrunTime(int v,Mat&image)
{
    double duration;
    duration = static_cast<double>(getTickCount());
    for(int i = 0;i<10;i++)   //执行十次取平均值
    {
        switch(v)
        {
        case 1:
            colorReduce1(image);
            break;
        case 2:
            colorReduce2(image);
            break;
        case 3:
            colorReduce3(image);
            break;
        case 4:
            colorReduce4(image);
            break;
        default:
            break;
        }
    }
    duration = static_cast<double>(getTickCount()) - duration;
    duration /= getTickFrequency()/100;        //执行时间,以ms为单位
    qDebug()<<"duration"<<v<<":"<<duration<<"ms";

结论:用指针最省时间,所以应当用指针来操作像素。

猜你喜欢

转载自blog.csdn.net/qit1314/article/details/85955128