OpenCV中Mat类模板的一些基本操作(实例)

目录

1.操作图像像素

(1)at方式访问图像像素

(2)迭代器访问图像中像素

(3)指针访问图像像素

(4)整行整列像素值的赋值


Windows下的CodeBlocks配置Opencv环境

OpenCV的数据结构

OpenCV下的Mat类模板基础(实例)

1.操作图像像素

(1)at方式访问图像像素

        以减少图像中颜色数量为例子。假设图像为256种颜色,将它变成64中颜色,只需要将原来的颜色除以div = 4以后再乘以div = 4,最后加上div / 2就可以实现该操作。

  • at方法:cv::Mat中的at(x,y)函数模版用来操作指定位置的矩阵元素,在使用时需要指定函数返回的数据类型。
    • image.at<uchar>(i,j) = 255;
    • iamge.at<cv:Vec3b>(i,j)[channel] = value;

        提示:OpenCV中的彩色图像的通道排列不是RGB,而是BGR,所以outputImage.at<Vec3b>(i,j)[0]代表的是该点的B分量。

#include <iostream>
#include<opencv2/core.hpp>
#include<opencv2/highgui.hpp>

using namespace cv;
using namespace std;

void colorReduce(Mat&intputImage,Mat&outputImage,int div){

    outputImage = intputImage.clone();
    int rows = intputImage.rows;
    int cols = intputImage.cols;

    //循环遍历图像中的元素
    for(int i = 0 ; i < rows; i++){
        for(int j = 0; j < cols; j++){
            outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0] / div * div + div / 2;
            outputImage.at<Vec3b>(i,j)[1] = outputImage.at<Vec3b>(i,j)[1] / div * div + div / 2;
            outputImage.at<Vec3b>(i,j)[2] = outputImage.at<Vec3b>(i,j)[2] / div * div + div / 2;
        }
    }
}


int main()
{
    //读取图像
    Mat image = imread("../images/GuiZhou.jpg");
    //创建一个窗口
    namedWindow("Original image!");
    //显示图像
    imshow("Original image!",image);

//    Mat outputImage(image.rows,image.cols,CV_8UC3);
    Mat outputImage;
    int div = 4;

    //调用函数
    colorReduce(image,outputImage,div);

    //创建窗口
    namedWindow("result!");
    imshow("result!",outputImage);

    waitKey(0);
    return 0;
}

原始图像

经过函数操作之后的输出: 

提示:可以看到上面的图像并没有什么变化,但是如果增大div的值变化就明显了。 

(2)迭代器访问图像中像素

        类似于STL库中的用法,熟悉C++的读者应该对于STL库比较清楚。这里同样可以使用迭代器访问图像中的像素。

  • cv::MatIterator<cv::Vec3b>it;
  • cv::Mat_<cv:Vec3b>::iterator it;

和上面同样的例子(减少图像中的像素)

void colorReduce(Mat&inputImage,Mat&outputImage,int div){

    outputImage = inputImage.clone();
    //模版指定类型(方法一)
    Mat_<Vec3b>::iterator it = inputImage.begin<Vec3b>();
    Mat_<Vec3b>::iterator itend = inputImage.end<Vec3b>();
    //方法二:指明cimage类型的方法之后,就可以不写begin和end的类型
    Mat_<Vec3b>cimage = outputImage;
    Mat_<Vec3b>::iterator itout = cimage.begin();
    Mat_<Vec3b>::iterator itoutend = cimage.end();

    for(;it != itend; itout ++,it ++){
        (*itout)[0] = (*it)[0] / div * div + div / 2;
        (*itout)[1] = (*it)[1] / div * div + div / 2;
        (*itout)[2] = (*it)[2] / div * div + div / 2;
    }
//    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();
//    Mat_<Vec3b>::iterator itend = outputImage.end<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;
//    }

}

(3)指针访问图像像素

        cv::Mat提供了一个prt(int i)函数模版以获取指定行数据的首地址。使用prt(int i)函数返回指定的数据类型。

  • image.prt<uchar>(i);

和上面同样的例子(减少图像中的像素)

void colorReduce(Mat&inputImage,Mat&outputImage,int div){

    outputImage = inputImage.clone();
    int rows = outputImage.rows;
    int cols = outputImage.cols * outputImage.channels();

    for(int i = 0; i < rows; i++){
        uchar*data = inputImage.ptr<uchar>(i);
        uchar*dataout = outputImage.ptr<uchar>(i);
        for(int j = 0; j < cols; j++){
            dataout[j] = dataout[j] / div * div + div / 2;
        }
    }

}

        通常若图像的宽度占有的字节数不是4或者8的整数倍时,会在每一行进行数据补齐。

然而,若图像再行上不需要进行数据补齐,则一幅图像可以看作是一个一维数组,cv::Mat类提供的一个isContinuous函数以检测图像是否采用数据补齐,若没有采用数据补齐,则返回true.

void colorReduce(Mat&inputImage,Mat&outputImage,int div){

    outputImage = inputImage.clone();
    int rows = outputImage.rows;
    int cols = outputImage.cols * outputImage.channels();

    if(outputImage.isContinuous()){
        cols = cols * rows;
        rows = 1;//1维数组
        cout<<"没有进行数据补齐"<<endl;
    }

    for(int i = 0; i < rows; i++){
        uchar*data = inputImage.ptr<uchar>(i);
        uchar*dataout = outputImage.ptr<uchar>(i);
        for(int j = 0; j < cols; j++){
            dataout[j] = dataout[j] / div * div + div / 2;
        }
    }

}

        cv::Mat类data属性是一个指向数据区的无符号型指针,所以可以采用下面的方式实现指向数据区的操作:

  •  uchar*data = image.data;
  • 将指针移动至下一行的操作:
    • data += image.step;
  • 对于第j行第i列的像素,获取地址方式:
    • data = image.data + j * image.step + i *image.elemSize();
void colorReduce(Mat&inputImage,Mat&outputImage,int div){

    outputImage = inputImage.clone();
    int rows = outputImage.rows;
    int cols = outputImage.cols;

    uchar*dataout = outputImage.data;

    for(int i = 0; i < rows; i++){
        for(int j = 0; j < cols; j++){
            *dataout = *dataout / div * div + div / 2;
            dataout++;//指向下一个像素点的值
        }
    }

}

(4)整行整列像素值的赋值

对于整行或者整列像素值的赋值方式如下:

img.row(i).setTo(Scalar(255));//Scalar表示标量的意思

img.col(j).setTo(Scalar(255));

参考书籍:

《数字图像处理》

猜你喜欢

转载自blog.csdn.net/Keep_Trying_Go/article/details/130446844