输入/输出
从文件加载一个图像:
Mat img = imread(filename);
如果你读取一个jpg文件,默认情况下会创建一个3通道的图像。如果你需要一个灰度图像,使用:
Mat img = imread(filename, IMREAD_GRAYSCALE);
文件的格式由它的内容(前几个字节)决定。保存一个图像到一个文件:
imwrite(filename, img);
文件的格式由其扩展名决定。
使用cv::imdecode
和cv::imencode
读取/写入 图像 从/到 内存,而不是一个文件。
图像的基本操作
为了得到像素强度值,你必须知道图像的类型和通道的数量。下面是一个单通道灰度图像(类型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返回的默认格式):
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++ only):
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的形式取一个二维或三维点数组,矩阵只包含一列,每一行对应一个点,对应的矩阵类型为32FC2或32FC3。这样的矩阵可以很容易地由std::vector
(c++ only)构造:
vector<Point2f> points;
//... fill the array
Mat pointsMat = Mat(points);
一个可以访问这个矩阵中的一个点使用相同的方法Mat::at
(c++ only):
Point2f point = pointsMat.at<Point2f>(i, 0);
内存管理和引用计数
Mat是一种保持矩阵/图像特征(行和列数,数据类型等)和数据指针的结构。因此,没有什么能阻止我们拥有几个Mat实例来对应相同的数据。Mat保留一个引用计数,它告诉我们当特定的Mat实例被销毁时,是否需要释放数据。下面是一个创建两个矩阵而不复制数据的例子(仅c++):
std::vector<Point3f> points;
// .. fill the array
Mat pointsMat = Mat(points).reshape(1);
结果,我们得到了一个3列的32FC1矩阵,而不是一个1列的32FC3矩阵。
pointsMat使用来自点的数据,并且在被销毁时不会释放内存。然而,在这个特定的实例中,开发人员必须确保点的生命周期长于pointsMat。如果我们需要复制数据,可以使用例如cv::Mat::copyTo
或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);
基本操作
在一个矩阵上定义了许多方便的算子。例如,下面是我们如何从现有的灰度图像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);
可视化图像
在开发过程中查看算法的中间结果非常有用。OpenCV提供了一种方便的图像可视化方法。一个8U图像可以显示使用:
Mat img = imread("image.jpg");
namedWindow("image", WINDOW_AUTOSIZE);
imshow("image", img);
waitKey();
调用waitKey()将启动一个消息传递周期,等待在“image”窗口中按下按键。一个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
时。