********************** 操作图像 ********************** .. highlight:: cpp 输入/输出 ============ 图像 ------ 从文件中读入一副图像: :: Mat img = imread(filename) 如果你读入一个jpg文件,缺省情况下将创建一个3通道图像。如果你需要灰度(单通道)图像,使用如下语句: :: Mat img = imread(filename, 0); 将图像保存到一个文件: :: Mat img = imwrite(filename); XML/YAML -------- TBD 基本图像操作 ============================ 获取像素的亮度值 -------------------------------- 要获取像素的亮度值,你必须知道图像的类型和通道的数目。如下例子展示了获取单通道灰度图(类型 8UC1)的(x, y)位置处的像素值: :: Scalar intensity = img.at(x, y); ``intensity.val[0]`` 中保存从0到255的值。现在我们看一下3通道图像如何获取像素值,颜色顺序为 ``BGR`` ( ``imread`` 返回的缺省顺序): :: Vec3b intensity = img.at(x, y); uchar blue = intensity.val[0]; uchar green = intensity.val[1]; uchar red = intensity.val[2]; 你可以使用同样的方法处理浮点图像(例如通对一个3通道图像进行Sobel运算得到的浮点图像): :: Vec3f intensity = img.at(x, y); float blue = intensity.val[0]; float green = intensity.val[1]; float red = intensity.val[2]; 同样的方法也可用于像素值的修改: :: img.at(x, y) = 128; 一些OpenCV函数,例如calib3d模块中的 ``projectPoints`` 函数,需要以 ``Mat`` 的格式输入二维或者三维的点。这样的矩阵必须有且仅有一列,这样每行对应一个点,矩阵类型需要是32FC2或者32FC3。这样的矩阵可以很容易的从 ``std::vector`` 转换而来: :: vector points; //... fill the array Mat pointsMat = Mat(points); 您也可以通过 ``Mat::at`` 方法来读写矩阵中的一个元素: :: Point2f point = pointsMat.at(i, 0); 内存管理和参考计数(reference counting) ---------------------------------------- ``Mat`` 内存储了矩阵/图像的属性(行数,列数,数据类型等)以及一个指向数据的指针。因此几个 ``Mat`` 实例可以指向同一个数据。 ``Mat`` 中还记录了参考计数(reference count),这样在 ``Mat`` 被释放时就知道是否需要释放数据。这儿是一个不需复制数据就创建两个矩阵的例子: :: std::vector points; // .. fill the array Mat pointsMat = Mat(points).reshape(1); 这样我们得到了一个3列的32FC1矩阵,而不是1列的32FC3矩阵。 ``pointsMat`` 使用 ``points`` 的数据,且当它释放时不会是否数据。在这个例子中,开发者需要知道 ``points`` 的生命比 ``pointsMat`` 长。如果我们需要复制数据,那么请使用 ``Mat::copyTo`` 或 ``Mat::clone`` : :: Mat img = imread("image.jpg"); Mat img1 = img.clone(); 在C API中,开发者必须实现创建输出图像然后再调用函数。与之相比不同的是C++ API支持空的 ``Mat`` 类型的输出参数。C++ API会调用 ``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`` 转为 C API 数据类型: :: Mat img = imread("image.jpg"); IplImage img1 = img; CvMat m = img; 注意此处无数据复制操作。 将彩色图像转为灰度图像: :: Mat img = imread("image.jpg"); // loading a 8UC3 image Mat grey; cvtColor(img, grey, CV_BGR2GRAY); 将图像的类型从8UC1转为32FC1: :: src.convertTo(dst, CV_32F); 显示图像 ------------------ 在算法开发过程中,查看算法的中间结果是非常有用的。OpenCV提供了方便查看图像的方法。类型为 ``8U`` 的图像可以使用如下方法显示: :: Mat img = imread("image.jpg"); namedWindow("image", CV_WINDOW_AUTOSIZE); imshow("image", img); waitKey(); 调用 ``waitKey()`` 会进入一个消息循环,来等待 ``image`` 窗口上的按键动作。 类型为 ``32F`` 的图像需要转为 ``8U`` 类型。如下: :: Mat img = imread("image.jpg"); Mat grey; cvtColor(img, grey, CV_BGR2GREY); 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); namedWindow("image", CV_WINDOW_AUTOSIZE); imshow("image", draw); waitKey(); 翻译 ====================== 于仕琪 http://www.opencv.org.cn