上一個教程: 矩陣的掩膜操作
下一個教程: 使用 OpenCV 新增(混合)兩張影像
輸入/輸出
影像
從檔案載入影像
C++
Java
Mat img = Imgcodecs.imread(filename);
Python
如果讀取 JPG 檔案,預設會建立一個三通道影像。如果需要灰度影像,請使用
C++
Mat img =
imread(filename, IMREAD_GRAYSCALE);
Java
Mat img = Imgcodecs.imread(filename, Imgcodecs.IMREAD_GRAYSCALE);
Python
img =
cv.imread(filename, cv.IMREAD_GRAYSCALE)
- 注意
- 檔案的格式由其內容(前幾個位元組)決定。要將影像儲存到檔案,請使用
C++
Java
Imgcodecs.imwrite(filename, img);
Python
- 注意
- 檔案的格式由其副檔名決定。
- 使用 cv::imdecode 和 cv::imencode 從/向記憶體而非檔案讀寫影像。
影像基本操作
訪問畫素強度值
為了獲取畫素強度值,您需要知道影像的型別和通道數。以下是一個單通道灰度影像(型別 8UC1)以及畫素座標 x 和 y 的示例:
C++
Java
byte[] imgData = new byte[(int) (img.total() * img.channels())];
img.get(0, 0, imgData);
byte intensity = imgData[y * img.cols() + x];
Python
僅限 C++ 版本:`intensity.val[0]` 包含一個 0 到 255 之間的值。請注意 x 和 y 的順序。由於在 OpenCV 中影像與矩陣由相同的結構表示,我們對這兩種情況使用相同的約定——0-based 行索引(或 y 座標)在前,0-based 列索引(或 x 座標)在後。此外,您也可以使用以下表示法(僅限 C++):
現在讓我們考慮一個具有 BGR 顏色順序的三通道影像(imread 返回的預設格式):
C++ 程式碼
Python
_blue = img[y,x,0]
_green = img[y,x,1]
_red = img[y,x,2]
對於浮點影像(例如,透過對三通道影像執行 Sobel 演算法可以得到此類影像),您可以使用相同的方法(僅限 C++):
float blue = intensity.
val[0];
float green = intensity.
val[1];
float red = intensity.
val[2];
同樣的方法也可以用來改變畫素強度:
C++
img.at<
uchar>(y, x) = 128;
Java
byte[] imgData = new byte[(int) (img.total() * img.channels())];
imgData[y * img.cols() + x] = (byte) 128;
img.put(0, 0, imgData);
Python
OpenCV 中有一些函式,特別是 `calib3d` 模組中的函式,例如 `cv::projectPoints`,它們接受 `Mat` 形式的 2D 或 3D 點陣列。矩陣應只包含一列,每行對應一個點,矩陣型別應分別為 `32FC2` 或 `32FC3`。這樣的矩陣可以很容易地從 std::vector 構造(僅限 C++):
可以使用相同的 Mat::at 方法訪問此矩陣中的一個點(僅限 C++):
記憶體管理與引用計數
`Mat` 是一個結構體,它儲存矩陣/影像的特性(行數、列數、資料型別等)以及指向資料的指標。因此,擁有多個 `Mat` 例項對應同一資料是可行的。一個 `Mat` 維護一個引用計數,該計數指示當 `Mat` 的特定例項被銷燬時資料是否需要被釋放。以下是建立兩個不復制資料的矩陣的示例(僅限 C++):
std::vector<Point3f> points;
結果,我們得到一個三列的 `32FC1` 矩陣,而不是一列的 `32FC3` 矩陣。pointsMat 使用來自 `points` 的資料,並且在銷燬時不會釋放記憶體。然而,在這種特定情況下,開發人員必須確保 `points` 的生命週期長於 `pointsMat`。如果我們需要複製資料,可以透過例如 `cv::Mat::copyTo` 或 `cv::Mat::clone` 來完成。
C++
Java
Mat img = Imgcodecs.imread("image.jpg");
Mat img1 = img.clone();
Python
可以為每個函式提供一個空的輸出 `Mat`。每個實現都會為目標矩陣呼叫 `Mat::create`。如果矩陣為空,此方法會為其分配資料。如果它不為空且具有正確的大小和型別,則此方法不執行任何操作。但是,如果大小或型別與輸入引數不同,則資料將被釋放(並丟失),並分配新的資料。例如:
C++
Java
Mat img = Imgcodecs.imread("image.jpg");
Mat sobelx = new Mat();
Imgproc.Sobel(img, sobelx, CvType.CV_32F, 1, 0);
Python
_sobelx =
cv.Sobel(img, cv.CV_32F, 1, 0)
基本操作
矩陣上定義了許多方便的運算子。例如,下面是如何從現有的灰度影像 img 建立一個黑色影像:
C++
Java
byte[] imgData = new byte[(int) (img.total() * img.channels())];
Arrays.fill(imgData, (byte) 0);
img.put(0, 0, imgData);
Python
選擇感興趣區域
C++
Rect r(10, 10, 100, 100);
Java
Mat smallImg = img.submat(r);
Python
_smallImg = img[10:110,10:110]
從彩色到灰度的轉換
C++
Java
Mat img = Imgcodecs.imread("image.jpg");
Mat grey = new Mat();
Imgproc.cvtColor(img, grey, Imgproc.COLOR_BGR2GRAY);
Python
將影像型別從 8UC1 更改為 32FC1
C++
Java
src.convertTo(dst, CvType.CV_32F);
Python
_dst = src.astype(np.float32)
影像視覺化
在開發過程中檢視演算法的中間結果非常有用。OpenCV 提供了一種方便的影像視覺化方法。可以使用以下方法顯示 8U 影像:
C++
Java
Mat img = Imgcodecs.imread("image.jpg");
HighGui.namedWindow("image", HighGui.WINDOW_AUTOSIZE);
HighGui.imshow("image", img);
HighGui.waitKey();
Python
呼叫 `waitKey()` 會啟動一個訊息傳遞迴圈,等待“image”視窗中的按鍵。32F 影像需要轉換為 8U 型別。例如:
C++
double minVal, maxVal;
sobelx.
convertTo(draw,
CV_8U, 255.0/(maxVal - minVal), -minVal * 255.0/(maxVal - minVal));
Java
Mat img = Imgcodecs.imread("image.jpg");
Mat grey = new Mat();
Imgproc.cvtColor(img, grey, Imgproc.COLOR_BGR2GRAY);
Mat sobelx = new Mat();
Imgproc.Sobel(grey, sobelx, CvType.CV_32F, 1, 0);
MinMaxLocResult res = Core.minMaxLoc(sobelx);
Mat draw = new Mat();
double maxVal = res.maxVal, minVal = res.minVal;
sobelx.convertTo(draw, CvType.CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
HighGui.namedWindow("image", HighGui.WINDOW_AUTOSIZE);
HighGui.imshow("image", draw);
HighGui.waitKey();
Python
_sobelx =
cv.Sobel(grey, cv.CV_32F, 1, 0)
minVal = np.amin(sobelx)
maxVal = np.amax(sobelx)
draw =
cv.convertScaleAbs(sobelx, alpha=255.0/(maxVal - minVal), beta=-minVal * 255.0/(maxVal - minVal))
- 注意
- 這裡不需要 `cv::namedWindow`,因為它緊接著 `cv::imshow`。然而,它可以用於更改視窗屬性或在使用 `cv::createTrackbar` 時。