opencv 二值图转换_灰度图像


本节内容是OpenCV中的一些图像的操作,内容上与前面有重复。但是tutorials有这个文档,所以还是再梳理一遍吧。原文有C++、Java、Python三种代码示例,这里只说明C++部分。

  • 原文网址Operations with images
  • 本地目录D:opencvsourcesdoctutorialscore
  • 代码目录D:opencvsourcessamplescpptutorial_codecoremat_operations
  • GitHub 有相应文档和OpenCV源代码
  • 版本OpenCV4.1.2(版本兼容性见英文原文,部分文档适用于OpenCV2.0和3.0)
  • 环境Windows、C++、VS2019 Community

输入/输出

从文件中读取图像:


Mat


如果读取一个jpg图片,默认是三个通道。如果想读取为灰度图像,用下面方法:


Mat img = imread(filename, IMREAD_GRAYSCALE);


保存图像到文件:


imwrite(filename, img);


图像文件格式由扩展名决定。另外imdecode和imencode函数是读取和写入到内存,而不是文件。

图像的基本操作

获取像素的灰度值Accessing pixel intensity values

为了获取图像灰度值,需要知道图像的数据类型和通道数。

下面是单通道灰度图像(8UC1)和像素坐标(x,y):


Scalar intensity = img.at<uchar>(y, x);//注意是行、列坐标


其中,intensity.val[0]就是该点的灰度值,取值范围是0到255。需要注意x和y的顺序,是从0开始的列索引(column对应x坐标)还是行索引(row对应y坐标)。也可以用下面的:


Scalar intensity = img.at<uchar>(Point(x, y));//注意是点(x,y)


下面,考虑3通道的BGR图像


Vec3b intensity = img.at<Vec3b>(y, x);//注意是点的行、列位置
uchar blue = intensity.val[0];//蓝色分量
uchar green = intensity.val[1];//绿色分量
uchar red = intensity.val[2];//红色分量


浮点图像,像素的数据类型是float,不是前面的uchar。(例如,三个通道进行sobel运算得到的结果就是float型的图像)


Vec3f intensity = img.at<Vec3f>(y, x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];


上面的方法也可用于改变像素值,如:


Memory manimg.at<uchar>(y, x) = 128;


OpenCV的某些函数,尤其是calib3d模块中,如cv::projectPoints,会使用2D和3D点组成Mat数据。这些点存储为一列,每行只对应一个点。矩阵类型分别对应32FC2或32FC3。这样的矩阵可以用std::vector来构造。


vector<Point2f> points;
 //...对points数组进行赋值,如points.push(Point2f(x,y))等等
Mat pointsMat = Mat(points);//生成Mat类型数据,这里是2通道

Point2f point = pointsMat.at<Point2f>(i, 0);//访问Mat中第i个点


内存管理与参考计数Memory Management and reference counting

前面讲过Mat数据类型,Mat由包含矩阵或者图像的参数(行数、列数、数据类型等)和一个指向数据的指针构成。所以对应同一个数据,我们可以创建多个Mat对象。 Mat用引用计数的方法来表明当一个Mat对象销毁时,数据是否必须释放。下面是不用拷贝数据,创建两个矩阵的例子:


std::vector<Point3f> points;
        // .. 填充数组
        Mat pointsMat = Mat(points).reshape(1);//reshap将通道变为1
       //先将points转换为32FC3的Mat,再reshape为32FC1的pointsMat


最后,我们得到了一个32FC1的3列矩阵,取代了32FC3的1列矩阵。当pointsMat 使用来自points的数据,并且pointsMat销毁时不会释放内存。在这种特殊情况下,points的使用寿命必须必pointsMat长才行。如果需要拷贝数据,用下面的方法。cv::Mat::copyTo 或者cv::Mat::clone。


Mat img = imread("image.jpg");
        Mat img1 = img.clone();


提供给一个函数空的Mat对象作为输出,函数内部会调用Mat::create,为该矩阵分配数据内存。如果非空,Mat对象大小和数据类型正确,则什么也不做。如果大小或数据类型不同于输入参数,矩阵数据会释放并且分配新的数据。如下:


Mat img = imread("image.jpg");
        Mat sobelx;
        Sobel(img, sobelx, CV_32F, 1, 0);//函数执行,并为sobelx分配内存


一些基本操作 Primitive operations

有一些很方便的定义矩阵的方式。例如,下面是由一副灰度图像img创建一副黑色图像


img = Scalar(0);//所有元素赋值为0


选择感兴趣区域:


Rect r(10, 10, 100, 100);
            Mat smallImg = img(r);


彩色转换为灰度:


Mat img = imread("image.jpg"); // 加载8UC3图像
Mat grey;  
cvtColor(img, grey, COLOR_BGR2GRAY);


图像类型从8UC1到32FC1:


src.convertTo(dst, CV_32F);


显示图像 Visualizing images

在调试程序过程中可以显示图像。显示8U图像:


Mat img = imread("image.jpg");
        namedWindow("image", WINDOW_AUTOSIZE);//如果不改变窗口属性,可以不写该行
        imshow("image", img);
        waitKey();


32F图像需要转换为8U类型才能显示:


Mat img = imread("image.jpg");
        Mat grey;
        cvtColor(img, grey, COLOR_BGR2GRAY);
        Mat sobelx;
        Sobel(grey, sobelx, CV_32F, 1, 0);//Soble结果为32F图像
        double minVal, maxVal;
        minMaxLoc(sobelx, &minVal, &maxVal); //找到最小和最大值
        Mat draw;
        //sobelx图像灰度拉伸,并转换为8U显示
        sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
        namedWindow("image", WINDOW_AUTOSIZE);
        imshow("image", draw);
        waitKey();