【OpenCV:從零到一】04:圖像操作

前言
這是我《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 在……之前

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