前言
這是我《OpenCV:從零到一》專欄的第四篇博客,想看跟多請戳這。
本文概要
Sobel算子
minMaxLoc函數
Mat對象的成員函數 at
vec類及其常用的類
加強上一篇提到的API的使用
案例代碼
大概內容:sobel邊緣檢測,阻塞圖片的藍色和綠色通道。
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
using namespace cv;
using std::endl;
using std::cout;
int main(int argc, char** args) {
//讀取圖像並灰度化
Mat src = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_COLOR);
if (src.empty()) {
cout << "could not find the image resource..." << endl;
return -1;
}
Mat grayImg;
cvtColor(src, grayImg, COLOR_BGR2GRAY);
namedWindow("grayImg");
imshow("grayImg", grayImg);
//使用sobel濾波器,x方向一階導數
Mat sobelx;
Sobel(grayImg, sobelx, CV_32F, 1, 0);
imshow("sobelx", sobelx);
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //找最大和最小的強度(返回指針)
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
imshow("draw", draw);
//通過遍歷數組可以達到把某個通道置0,從而達到只讓某個通道顯示,或者阻止某個通道顯示
Mat image = src.clone();
int height = image.rows;
int width = image.cols;
int channels = image.channels();
printf("height=%d width=%d channels=%d", height, width, channels);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (channels == 3) {//檢測圖片的通道數,防止出現野指針(訪問越界),提升程序的魯棒性
image.at<Vec3b>(row, col)[0] = 0; // blue
image.at<Vec3b>(row, col)[1] = 0; // green
//image.at<Vec3b>(row, col)[2] = 0; // red
}//只讓紅色通道顯示
}
}
imshow("image", image);
waitKey(0);
return 0;
}
運行效果:
解析及注意事項
- 當前版本(opencv4)的namedWindow的參數CV_WINDOW_AUTOSIZE需要加#include<opencv2/highgui/highgui_c.h>,因爲namedWindow第二個參數本身就是默認CV_WINDOW_AUTOSIZE的,所以這樣就顯得有些麻煩了,乾脆不寫第二個參數,或者更懶一點的話直接連namedWindow都不寫了。
- cvtColor用於轉換色彩空間,convertTo用於將矩陣的值轉化,通常用於歸一化(同一色彩空間下的值的轉換),同時後者是Mat的成員函數
- (多行註釋快捷鍵ctrl+k+c 取消ctrl+k+u)
- Sobel是一個離散性差分算子,用來運算圖像亮度函數的梯度之近似值,主要用於獲得數字圖像的一階梯度。索貝爾算子不但產生較好的檢測效果,而且對噪聲具有平滑抑制作用,但是得到的邊緣較粗,且可能出現僞邊緣。(離散的差分對應的是連續的微分)
- Sobel算子是一個線性濾波器(算子 包括 濾波器 包括 卷積核),即通常要進行卷積操作。所以別看它的參數那麼多,大多數掩膜用的算子的前兩個參數是固定的分別是
src(原圖像)
dst(目標圖像/結果圖像)
而後面四個也是固定的,並且通常是有默認值的他們分別是
ksize = 3(卷積核的大小)、
scale = 1(縮放比例)、
delta = 0(整體增加的值)(有時候叫shift)、
borderType = BORDER_DEFAULT(邊界處理類型)
連計算公式都是一樣的dst(i)=src(i) x scale + shift scale和delta在其他處理圖像的值的函數中也很常見,所以這樣看下來那些看似很長的參數列表其實也不過是紙老虎罷了。 - minMaxLoc用於尋找一個矩陣的最大最小值,並且返回它的指針,需要用參數去接收,需要地址的話也可以增加參數去接受,然而這個函數只能夠接受單通道的圖片,多通道的圖片需要先轉換。
- at和上一篇講到的ptr都是用於訪問圖片像素的方法之一,都是成員函數,相比之下,ptr在debug模式下比at快,但是在release模式下差異不明顯,是因爲at在release模式下不進行索引範圍檢查以此來提高效率。除此之外這兩個還有一個不同的就是at返回引用而ptr返回的是指針。除此之外,還可以使用迭代器(Mat_::iterator配合Mat.begin()和Mat.end())來訪問圖片,熟悉stl的讀者一定不陌生,這裏暫時不展開寫。
- vec是一個向量類,通常配合at來訪問通道(把一個像素當成一個向量),需要傳兩個參數,一個是元素類型另一個是元素數量,值得注意的是他還有一些填好長度的宏,比較常用的有如下幾個
typedef Vec<uchar,3> Vec3b;
typedef Vec<int,2> Vec2i;
typedef Vec<float,4> Vec4f;
typedef Vec<double,3> Vec3d;
全註釋代碼
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
//#include<opencv2/highgui/highgui_c.h>//CV_WINDOW_AUTOSIZE
#include <iostream>
using namespace cv;
using std::endl;
using std::cout;
int main(int argc, char** args) {
Mat src = imread("D:\\86186\\Documents\\opencv\\lena.jpg", IMREAD_COLOR);
if (src.empty()) {
cout << "could not find the image resource..." << endl;
return -1;
}
Mat grayImg;
cvtColor(src, grayImg, COLOR_BGR2GRAY);
//將彩色圖像轉換爲灰度圖像,2(two)和to同音,完整是 BGR to GRAY 在opencv裏通道順序是BRG
namedWindow("grayImg");//namedWindow("grayImg", CV_WINDOW_AUTOSIZE);//要加#include<opencv2/highgui/highgui_c.h>
//namedWindow第二個參數默認的就是CV_WINDOW_AUTOSIZE,不寫也可以,因此也不用加#include<opencv2/highgui/highgui_c.h>
imshow("grayImg", grayImg);
Mat sobelx;
Sobel(grayImg, sobelx, CV_32F, 1, 0);//使用sobel濾波器,x方向一階導數
/*
參數
InputArray src,
OutputArray dst,
int ddepth,//output image depth 輸出圖像的深度
int dx, //order of the derivative x. x的導數的階數
int dy, //order of the derivative y. y的導數的階數
int ksize = 3,//size of the extended Sobel kernel; it must be 1, 3, 5, or 7.
double scale = 1,//optional scale factor for the computed derivative values
double delta = 0,//optional delta value that is added to the results prior to storing them in dst.
int borderType = BORDER_DEFAULT //這個老生常談了,絕大多數情況下都不用理(卷積的時候就會有這個參數)
*/
imshow("sobelx", sobelx);//甚至爲了省事連namedWindow都可以不寫,自動生成
//多行註釋快捷鍵ctrl+k+c 取消ctrl+k+u
double minVal, maxVal;
minMaxLoc(sobelx, &minVal, &maxVal); //找最大和最小的強度(返回指針)
/*
The extremums are searched across the whole array or, if mask is not an empty array, in the specified array region.
The function do not work with multi-channel arrays. 只能處理單通道的圖像
參數
InputArray src, //input single-channel array.
double * minVal, //pointer to the returned minimum value; NULL is used if not required.
double * maxVal = 0, //pointer to the returned maximum value; NULL is used if not required.
Point * minLoc = 0, //pointer to the returned minimum location (in 2D case); NULL is used if not required.
Point * maxLoc = 0, //pointer to the returned maximum location (in 2D case); NULL is used if not required.
InputArray mask = noArray() //optional mask used to select a sub-array.(尋找src數組中的局部的極值)
*/
Mat draw;
sobelx.convertTo(draw, CV_8U, 255.0 / (maxVal - minVal), -minVal * 255.0 / (maxVal - minVal));
//convertTo(dst,rtype,scale,shift) dst(i)=src(i)*scale+shift eg: 255歸一化則scale=1/255.0
//這裏是將原本不是255的圖擴大到255並減去最小值(變白)
imshow("draw", draw);
//通過遍歷數組可以達到把某個通道置0,從而達到只讓某個通道顯示,或者阻止某個通道顯示
Mat image = src.clone();//這個克隆是連矩陣一起復制的
int height = image.rows;
int width = image.cols;
int channels = image.channels();
printf("height=%d width=%d channels=%d", height, width, channels);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
if (channels == 3) {//檢測圖片的通道數,防止出現野指針(訪問越界),提升程序的魯棒性
image.at<Vec3b>(row, col)[0] = 0; // blue
image.at<Vec3b>(row, col)[1] = 0; // green
//image.at<Vec3b>(row, col)[2] = 0; // red
}//只讓紅色通道顯示
/*
The template methods return a reference to the specified array element.
at是一個 返回特定數組元素的引用 的模版(ptr是返回地址/指針的模版)有12個重載
For the sake of higher performance, the index range checks are only performed in the Debug configuration.
爲了獲得更高的性能,僅在調試配置中執行索引範圍檢查。
If matrix is of type CV_8U then use Mat.at<uchar>(y,x).
If matrix is of type CV_8S then use Mat.at<schar>(y,x).
If matrix is of type CV_16U then use Mat.at<ushort>(y,x).
If matrix is of type CV_16S then use Mat.at<short>(y,x).
If matrix is of type CV_32S then use Mat.at<int>(y,x).
If matrix is of type CV_32F then use Mat.at<float>(y,x).
If matrix is of type CV_64F then use Mat.at<double>(y,x).
參數一般爲行和列,也可以傳一個指針
*/
/*
vec是一個向量類,通常配合at來訪問通道(把一個像素當成一個向量)
template<typename _Tp, int cn>
_Tp element type 類型
cn the number of elements 元素數量(長度)
還有幾個宏,一目瞭然,望文生義
typedef Vec<uchar,3> Vec3b;
typedef Vec<int,2> Vec2i;
typedef Vec<float,4> Vec4f;
typedef Vec<double,3> Vec3d;
*/
}
}
imshow("image", image);
waitKey(0);
return 0;
}
翻譯筆記
derivative n. 派生物;導數
For the sake of higher performance, the index range checks are only performed in the Debug configuration.
爲了獲得更高的性能,僅在debug模式中執行索引範圍檢查。
sake n. 目的;利益;理由
extremums n. [數] 極值,極端值
optional delta value that is added to the results prior to storing them in dst
可選的增量值,在將結果存儲到dst之前添加到結果中
prior to 在……之前