7. 图像操作(OpenCV 官方文档翻译)

官方文档链接:https://docs.opencv.org/4.2.0/d5/d98/tutorial_mat_operations.html


输入 / 输出 (Input / Output)

图像 (Images)

从文件中加载图像:

    cv::Mat img = cv::imread(filename);

如果读取的是一个 jpg 文件,则默认情况下会创建一个 3 通道图像。如果需要灰度图像,则使用:

    cv::Mat img = cv::imread(filename, cv::IMREAD_GRAYSCALE);

注意
文件的格式由其内容(前几个字节)决定。要将图像保存到文件中,请执行如下操作:

    cv::imwrite(filename, img);

注意
文件的格式由其扩展名决定。使用 cv::imdecodecv::imencode 从内存(而不是文件)读取和写入图像。


图像基本操作 (Basic operations with images)

访问像素值 (Accessing pixel intensity values)

为了获得像素强度值,必须知道图像的类型和通道数。以下是单通道灰度图像(8UC1 型) 和像素坐标 x 和 y 的示例:

    cv::Scalar intensity = img.at<uchar>(y, x);

C++ 版本:intensity.val[0] 存储着 0 到 255 的一个值。注意 x 和 y 的顺序。因为在 OpenCV 中,图像用和矩阵相同的结构表示,所以我们对这两种情况都使用相同的约定:首先是基于 0 的行索引(或 y 坐标),然后是基于 0 的列索引(或 x 坐标)。或者,也可以使用以下符号(仅 C++):

    cv::Scalar intensity = img.at<uchar>(cv::Point(x, y);

现在考虑一下具有 BGR 颜色顺序(cv::imread 返回的默认格式) 的 3 通道图像:

    cv::Vec3b intensity = img.at<cv::Vec3b>(y, x);
    uchar blue = intensity.val[0];
    uchar green = intensity.val[1];
    uchar red = intensity.val[2];

可以用同样的方法来处理浮点图像(例如,可以通过在 3 通道图像上运行 Sobel 来获得这样的图像)(基于 C++):

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

同样的方法可用于改变像素值:

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

例如

完整代码:

#include 

#include 
#include 
#include 
#include 

int main(int argc, char** argv)
{
	cv::Mat src = cv::imread(cv::samples::findFile("lena.jpg"), cv::IMREAD_COLOR);

	for (int i = 100; i <= 200; ++i)
		for (int j = 300; j <= 500; ++j)
		{
			src.at<cv::Vec3b>(i, j).val[0] = 250;
			src.at<cv::Vec3b>(i, j).val[1] = 250;
			src.at<cv::Vec3b>(i, j).val[2] = 250;
		}

	cv::imshow("Output", src);
	cv::waitKey(0);

	return 0;
}

输出图像:

7. 图像操作(OpenCV 官方文档翻译)_第1张图片

OpenCV 中有一些函数,特别是 calib3d 模块中的函数,比如 cv::projectPoints,它们以 Mat 的形式获取二维或三维点的数组。矩阵应正好包含一列,每行对应一个点,矩阵类型应相应为 32FC2 或 32FC3 。这样的矩阵可以很容易地从 std::vector 构造:

    std::vector<cv::Point2f> points;
    // ... fill the array
    cv::Mat pointsMat = cv::Mat(points);

可以使用相同的方法 cv::Mat::at 访问矩阵中的点:

    cv::Point2f point = pointsMat.at<cv::Point2f>(i, 0);

内存管理和引用计数 (Memory management and reference counting)

Mat 是一种保持矩阵/图像特征(行和列数、数据类型等)和指向数据的指针的结构。因此,可以同时拥有于相同数据对应的多个 Mat 实例。Mat 保存一个引用计数,该计数指明在销毁 Mat 的特定实例时是否必须释放数据。这里是一个创建两个矩阵而不复制数据的例子:

std::vector<cv::Point3f> points;
// ... fill the array
cv::Mat pointsMat = cv::Mat(points).reshape(1);

结果得到了一个 3 列的 32FC1 矩阵,而不是 1 列的 32FC3 矩阵。pointsMat 使用来自点的数据,并且在销毁时不会释放内存。但是,在这个特定的实例中,如果我们需要复制数据,必须确保 points 的生存期比 pointsMat 的生存期长。可以使用 cv::Mat::copyTo 或 cv::Mat::clone:

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

每个函数可提供一个空的输出 Mat。每个实现都为目标矩阵调用 cv::Mat::create。如果矩阵是空的,则此方法为其分配数据。如果它不是空的并且具有正确的大小和类型,则该方法不会执行任何操作。但是,如果大小或类型与输入参数不同,则会释放(并丢失)数据并重新分配数据。例如:

    cv::Mat img = cv::imread("image.jpg");
    cv::Mat sobelx;
    cv::Sobel(img, sobelx, CV_32F, 1, 0);

输出图像

7. 图像操作(OpenCV 官方文档翻译)_第2张图片


原始操作 (Primitive operations)

矩阵上定义了许多方便的算子。例如,

  • 下面是如何从现有的灰度图像 img 生成黑色图像的方法:
    img = cv::Scalar(0);

输出图像

7. 图像操作(OpenCV 官方文档翻译)_第3张图片

  • 选择感兴趣的区域 (ROI):
cv::Rect r(10, 10, 100, 100);
cv::Mat smallImg = img(r);

输出图像

7. 图像操作(OpenCV 官方文档翻译)_第4张图片

  • 将彩色图像转换为灰度图像:
cv::Mat img = cv::imread("image.jpg");      // loading a 8UC3 image
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

7. 图像操作(OpenCV 官方文档翻译)_第5张图片

  • 将图像类型从 8UC1 转换为 32FC1:
    img.convertTo(dst, CV_32F);

7. 图像操作(OpenCV 官方文档翻译)_第6张图片


图像可视化 (Visualizing images)

在开发过程中,查看算法的中间结果是非常有用的。OpenCV 提供了一种可视化图像的便捷方法。一幅 8U 的图像可以使用以下方式显示:

cv::Mat img = cv::imread("image.jpg");
cv::namedWindow("image", cv::WINDOW_AUTOSIZE);
cv::imshow("image", img);
cv::waitKey();

调用 cv::waitKey() 将启动一个消息传递周期,该周期将等待 “图像” 窗口中的键击。一幅 32F 图像需要转换为 8U 类型,例如:

cv::Mat img = cv::imread("image.jpg");
cv::Mat grey;
cv::cvtColor(img, grey, cv::COLOR_BGR2GRAY);
cv::Mat sobelx;
cv::Soble(grey, sobelx, CV_32F, 1, 0);

double minVal, maxVal;
cv::minMaxLoc(sobelx, &minVal, &maxVal);    // find minimum and maximum intensities

cv::Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));

cv::namedWindow("image", cv::WINDOW_AUTOSIZE);
cv::imshow("image", draw);
cv::waitKey();

输出图像

注意 (Note)

这里的 cv::namedWindow 不是必要的,因为后面紧跟着 cv::imshow。但是,它可以用于更改窗口属性,或者在使用 cv::createTrackbar 时。

你可能感兴趣的:(OpenCV)