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 <iostream>

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/imgcodecs/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>

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;
}

輸出圖像:

在這裏插入圖片描述

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);

輸出圖像

在這裏插入圖片描述


原始操作 (Primitive operations)

矩陣上定義了許多方便的算子。例如,

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

輸出圖像

在這裏插入圖片描述

  • 選擇感興趣的區域 (ROI):
cv::Rect r(10, 10, 100, 100);
cv::Mat smallImg = img(r);

輸出圖像

在這裏插入圖片描述

  • 將彩色圖像轉換爲灰度圖像:
cv::Mat img = cv::imread("image.jpg");      // loading a 8UC3 image
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);

在這裏插入圖片描述

  • 將圖像類型從 8UC1 轉換爲 32FC1:
    img.convertTo(dst, CV_32F);

在這裏插入圖片描述


圖像可視化 (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 時。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章