1. 输入输出
1.1 图片
从文件加载图像:
Mat img = imread(filename);
如果读取 jpg 文件,则默认创建 3 通道图像。如果需要灰度图像,则使用:
Mat img = imread(filename, IMREAD_GRAYSCALE);
-
提示
文件的格式由其内容(前几个字节)决定。要将图像保存到文件:
imwrite(filename, img);
-
提示
- 文件的格式由其扩展名决定。
- 使用cv::imdecode和cv::imencode从向内存而不是文件读取和写入图像。
2. 图像的基本操作
2.1 访问像素值
为了获得像素值,必须知道图像的类型和通道数。以下是单通道灰度图像(8UC1 型)和像素坐标 x 和 y 的示例:
Scalar intensity = img.at<uchar>(y, x);
仅限 C++ 版本:intensity.val[0] 包含从 0 到 255 的值。注意 x 和 y 的顺序。由于在 OpenCV 中图像与矩阵用相同的结构表示,因此对两种情况都使用相同的约定——从 0 开始的行索引(y坐标)在前边,从0开始的列索引(x坐标)在后边。或者,也可以使用下边的表示法(仅限 C++ ):
Scalar intensity = img.at<uchar>(Point(x, y));
现在考虑一个具有 BGR 颜色排序的 3 通道图像(imread 返回的默认格式):
C++ 代码
Vec3b intensity = img.at<Vec3b>(y, x);
uchar blue = intensity.val[0];
uchar green = intensity.val[1];
uchar red = intensity.val[2];
可以对浮点图像使用相同的方法(例如,可以通过在 3 通道图像上运行 Sobel 来获得这样的图像)(仅限 C++ ):
Vec3f intensity = img.at<Vec3f>(y, x);
float blue = intensity.val[0];
float green = intensity.val[1];
float red = intensity.val[2];
可以使用相同的方法来改变像素强度:
img.at<uchar>(y, x) = 128;
OpenCV 中有一些函数,尤其是来自 calib3d 模块的函数,例如cv::projectPoints,它们采用 Mat 形式的 2D 或 3D 点数组。矩阵只包含一列,每一行对应一个点,类型应该是 32FC2 或 32FC3 对应。这样的矩阵可以很容易地从std::vector(仅限 C++ )构造:
vector<Point2f> points;
//... fill the array
Mat pointsMat = Mat(points);
可以使用相同的方法Mat::at(仅限 C++ )访问此矩阵中的一个点:
Point2f point = pointsMat.at<Point2f>(i, 0);
2.2 内存管理和引用计数
Mat 是一种结构,该结构保存矩阵/图像特征(行数和列数、数据类型等)和指向数据的指针。因此,没有什么能阻止让多个 Mat 实例对应相同的数据。Mat 保留一个引用计数,该计数告诉当 Mat 的特定实例被销毁时是否必须释放数据。以下是创建两个矩阵而不复制数据的示例(仅限 C++ ):
std::vector<Point3f> points;
// .. fill the array
Mat pointsMat = Mat(points).reshape(1);
结果,得到了一个 32FC1 的 3 列矩阵,而不是 32FC3 的 1 列矩阵。pointsMat使用来自点的数据,并且在销毁时不会释放内存。然而,在这种特殊情况下,开发人员必须确保points的生命周期比:pointsMat长。如果需要拷贝数据,可以使用 cv::Mat::copyTo or cv::Mat::clone, 如下所示:
Mat img = imread("image.jpg");
Mat img1 = img.clone();
可以为每个函数输出提供一个空的 Mat。每个方法的实现都为目标矩阵调用了 Mat::create。如果矩阵为空,则此方法为矩阵分配数据。如果它不为空并且具有正确的大小和类型,则该方法不执行任何操作。但是,如果大小或类型与输入参数不同,则数据将被释放(并丢失)并分配新数据。例如:
Mat img = imread("image.jpg");
Mat sobelx;
Sobel(img, sobelx, CV_32F, 1, 0);
2.3 基础操作
在矩阵上定义了许多方便的运算符。例如,这里是如何从现有的灰度图像制作黑色图像img
img = Scalar(0);
选择感兴趣的区域:
Rect r(10, 10, 100, 100);
Mat smallImg = img(r);
从彩色到灰度的转换:
Mat img = imread("image.jpg"); // loading a 8UC3 image
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
将图像类型从 8UC1 更改为 32FC1:
src.convertTo(dst, CV_32F);
2.4 可视化图像
在开发过程中查看算法的中间结果非常有用。OpenCV 提供了一种可视化图像的便捷方式。可以使用以下方式显示 8U 图像:
Mat img = imread("image.jpg");
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey();
对waitKey()的调用启动了一个消息传递循环,该循环等待“图像”窗口中的击键。一张 32F 的图像需要转换成 8U 类型。例如:
Mat img = imread("image.jpg");
Mat grey;
cvtColor(img, grey, COLOR_BGR2GRAY);
Mat sobelx;
Sobel(grey, sobelx, CV_32F, 1, 0);
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //find minimum and maximum intensities
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", draw);
waitKey();
-
提示
这里cv::namedWindow不是必需的,因为它紧随其后的是cv::imshow。不过,它可用于更改窗口属性或使用cv::createTrackbar