文章目錄
- 1.配置開發環境
- 2.圖像讀取與顯示
- 3.圖像色彩空間轉換
- 4.圖像對象的創建與賦值
- 5.圖像像素的讀寫操作
- 6.圖像像素的算數操作
- 7.滾動條-調整圖像亮度
- 8.滾動條-調整對比度和亮度
- 9.鍵盤響應操作
- 10.圖像像素的邏輯操作
- 11.圖像的通道分離和合并
- 12.圖像色彩空間轉換
- 13.圖像的像素值統計
- 14.圖像幾何形狀繪制
- 15.隨機數與隨機顏色
- 16.多邊形填充與繪制
- 17.鼠標操作與響應
- 18.圖像像素類型轉換與歸一化
- 19.圖像縮放與插值
- 20.圖像翻轉
- 21.圖像旋轉
- 22.視頻文件攝像頭使用
- 23.視頻處理與保存
- 24.圖像直方圖
- 25.二維直方圖
- 26.直方圖均衡化
- 27.圖像卷積和高斯模糊
- 28.雙邊模糊
- 29.人臉實時檢測
1.配置開發環境
配置開發環境提前需要安裝好Visual Studio和opencv包,這里可以單獨觀看視頻學習安裝。
以下過程參考鏈接: B站opencv快速入門30講-賈志剛
-
配置包含目錄
這里需要注意上面的菜單欄選擇Release和x64,打開屬性管理器選擇Release|x64>VC++目錄>包含目錄。(可能由于Visual Studio版本的問題,這里沒有和視頻中相同的microsoft.cpp.x64.user文件(但是新建的相同文件名的文件會顯示已存在該文件,在項目中確實已經存在),因此直接對Release|x64屬性修改是一樣的)
具體步驟:視圖》其他窗口》屬性管理器》Release | x64》屬性》VC++目錄》包含目錄》編輯》將opencv安裝包中的兩個目錄地址復制進去,如下圖所示:這樣包含目錄就配置好了。
-
配置庫目錄
接下來配置庫目錄,點擊常規下面的庫目錄》編輯》如下圖所示將opencv安裝包的lib 目錄復制進去,點擊確定。這樣庫目錄就配置好了。
-
配置鏈接器
接下來配置鏈接器:如下圖所示:屬性》鏈接器》輸入》附加依賴項》編輯》,在下圖中有兩個.lib 文件opencv_world460.lib 和opencv_world460d.lib,分別對應Release和Debug,切記勿將兩個文件同時寫進去。這里選擇opencv_world460.lib。點擊確定。這樣鏈接器配置完畢。
-
配置環境變量并重啟VS2022
系統》高級系統設置》配置環境變量,將opencv安裝包里面的類似下圖的bin路徑添加進去,點擊確定。重啟VS。
配置環境變量并重啟VS2022
2.圖像讀取與顯示
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;
using namespace std;int main(int argc, char ** argv){cv::Mat src = cv::imread("C/code/workspace/lena.jpg", IMREAD_GRAYSCALE);nameWindow("輸入窗口“, WINDOW_FREERATIO);if (src.empty()) {printf("could not load image...");return -1;}cv::imshow("輸入窗口”, src);cv::watiKey(0);destoryAllWindows();return 0;
}
代碼解釋:
#<opencv2/opencv.hpp> OpenCV庫預處理指令,包含了OpenCV庫的所用頭文件。
# C++標準庫預處理指令。該頭文件包含了C++庫中的輸入輸出流函數。
using namespace cv 這是openCV庫的命名空間。它將所有OpenCV庫中的函數都放在cv命名空間中,這樣可以通過cv:: 函數來調用它們。
suing namespace std 這是C++標準庫的命名空間,它將C++庫中的函數都放在std命名空間中,這樣可以通過std:: 函數來調用它們。
int main(int argc, char** argv) 這是程序主函數接受兩個參數,argc和argv,argc是一個整數,表示命令行參數的個數,argv是一個字符淑珍數組,表示命令行參數的指針。
cv::Mat src = cv::imread(“C/code/workspace/lena.jpg”, IMREAD_GRAYSCALE); cv::imread()是OpenCV讀取圖像的函數,將圖像讀取為灰度圖像,并將圖像存儲在cv::Mat 的src變量中。
nameWindow(“輸入窗口”,WINDOW_FREERATIO)這是OpenCV庫中創建窗口的函數指令,將創建一個名為“輸入窗口”窗口,并使用WINDOW_FREERATIO自動調整窗口大小。
cv::imshow(“輸入窗口”, src) 這是OpenCV中顯示圖像的函數,它將變量src顯示在“輸入窗口”中。
cv::waitKey(0) 這是OpenCV中的waitKey函數等待用戶按鍵的指令,它將等待用戶按下任意鍵,然后繼續執行。
destroyAllWindows() 這是OpenCV關閉所有窗口的指令。
3.圖像色彩空間轉換
這節學習如何創建頭文件,如何定義頭文件中的函數,以及如何在程序中調用自定義的頭文件函數
- 頭文件的創建
創建頭文件:在項目文件下面的頭文件中創建一個新建項,名為xxxxx.h 的文件
代碼解釋:定義了一個名為QuickDemo 的類,public 是類的公共部分的開始, void colorSpace_Demo( )是QuickDemo類的成員函數,這個函數接受一個Mat類型的參數 image,用于存儲輸入的圖像。};是類的定義的結束。#include <opencv2/opencv.hpp> using namespace cv; class QuickDemo {public:void colorSpace_Demo(Mat& image); };
- 定義頭文件函數
頭文件中定義的類成員函數需要在源文件中創建一個名為 xxxxx.cpp 的文件
代碼解釋:QuickDemo::colorSpace_Demo(Mat& image)): 這是QuickDemo類的成員函數colorSpace_Demo的聲明。cvtColor() 是OpenCV庫進行顏色空間轉化的指令。imwrite()這是OpenCV的imwrite函數將圖像保存到文件的指令。#include "quickopencv.h" void QuickDemo::colorSpace_Demo(Mat& image) {Mat gray, hsv;cvtColor(image, hsv, COLOR_BGR2HSV);cvtColor(image, gray, COLOR_BGR2GRAY);imshow("HSV", hsv);imshow(“灰度”, gray);imwrite("C/code/workspace/hsv.jpg", hsv);imwrite("C/code/workspace/gray.jpg", gray); }
HSV(Hue, Saturation, Value)是一種顏色的表示方式,色相(Hue),飽和度(Saturation), 透明度(Value) - 主函數文件
主函數文件在源文件中 新建項為文件名 xxxxxx.cpp 的文件#include <opencv2/opencv.hpp> #inluce <iostream> #include "quickopencv" using namespace cv; using namespace std; int main(int argc, char ** argv){Mat src = cv::imread("C/code/workspace/lena.jpg");nameWindow("輸入窗口“, WINDOW_FREERATIO);if(src.empty()) {printf("could not load image...");return -1;}cv::imshow("輸入窗口”,src);QuickDemo qd;qd.colorSpace_Demo(src);cv::waitKey(0);destroyAllWindows();return 0; }
4.圖像對象的創建與賦值
這一節承接上一節的內容,需要在自定義的頭文件中添加類QuickDemo 的成員函數mat_creation_demo(), 同時需要在源文件夾下面的quickdemo.cpp 中定義函數Quick::mat_creation_demo() 的具體內容。本節中學習的關于OpenCV庫中的克隆、復制、賦值、創建空白圖像等都是在成員函數mat_creation_demo()中定義的。在執行程序時還需要再main.cpp 文件中調用mat_creation_demo()函數。
下面是關于成員函數的定義:
void QuickDemo::mat_creation_demo(Mat& image){Mat src = image;// 創建方法-克隆Mat m1 = src.clone();//復制Mat m2;src.copyTo(m2);//賦值法Mat m3 = srcl;//創建空白圖像Mat m4 = Mat::zeros(src.size(), src.type());Mat m5 = Mat::zeros(size(512, 512), CV_8UC3);imshow("窗口1", m1);imshow(“窗口2”, m2);imshow("窗口3", m3);// 除上面的圖像對象外,還可以創建一個空白對像,然后賦值標量Mat m6, m7;m6 = image.clone();image.copyTo(m7);//創建單通道的空白圖像Mat m8 = Mat::zeros(Size(512, 512), CV_8UC1);//創建三通道空白圖像Mat m9 = Mat::zeros(Size(512, 512), CV_8UC3);//給三通道空白圖像賦標量值m9 = Scalar(0, 128, 64);std::cout << "width:" << m9.cols << "height:" << m9.rows << "channels:" << m9.channels() << std::endl;std::cou t << m9 <<std::endl;
代碼解釋:上面的代碼中主要是最后兩行的輸出流需要注意,m9.cols:這是m9 對象的成員變量,表示圖像的寬度。m9.rows:這是m9 對象的成員變量,表示圖像的高度。m9.channels():這是m9 對象的成員變量,用于獲取圖像的通道數。std::cout:這是C++標準庫中的iostream流對象,用于輸出文本,<< :這是流對象的輸出操作符,用于將右邊的操作數(如變量m9.cols)輸出到流中。std::endl:這是iostream流對象的結束標記,用于輸出一個換行符,并刷新輸出緩沖區。
5.圖像像素的讀寫操作
第一種方法是通過數組坐標訪問每一像素,總體來看是使用了兩個for循環;同樣的,這里是在類成員是函數的詳細定義中,去定義這個像素的訪問。下面是第一種方法的代碼演示:
void QuickDemo::pixel_visit_demo(Mat& image) {int w = image.cols;int h = image.rows;int dims = image.channels();for (int row = 0; row < h; row++) {for (int col = 0; col < w; col++) {if (dims == 1) { //灰度圖像int pv = image.at<uchar>(row, col);image.at<uchar>(row, col) = 255 - pv;}if (dims == 3) { //彩色圖像Vec3b bgr = image.at<Vec3b>(row, col);image.at<Vec3b>(row, col)[0] = 255 - bgr[0];image.at<Vec3b>(row, col)[0] = 255 - bgr[1];image.at<Vec3b>(row, col)[0] = 255 - bgr[2];}}}imshow("像素讀寫演示", image);
第二種方法是通過指針的方式訪問每個像素,下面是代碼演示:
void QuickDemo::pixel_visit_demo(Mat& image) {int w = image.cols;int h = image.rows;int dims = image.channels();for (int row = 0; row < h; row++) {uchar* current_row = image.ptr<uchar>(row);for (int col = 0; col < w; col++) {if (dims == 1) { //灰度圖像int pv = *current_row;*current_row++ = 255 - pv;}if (dims == 3) { //彩色圖像*current_row++ = 255 - *current_row;*current_row++ = 255 - *current_row;*current_row++ = 255 - *current_row;}}}imshow("像素讀寫演示", image);
代碼解釋:這里主要是需要理解指針的運用,image.ptr(row):這是OpenCV中的Mat對象image的成員函數ptr,它返回抑制指向圖像第row行的數據的指針。表示我們想要獲取的像素數據類型是8位無符號整數(uchar).插入一句額外的話,C語言之所以效率高,就是因為C可以像匯編一樣去操控內存。整形(int)是占用4個字節,基于32位的有符號整形。
下面是關于指針的代碼圖解
6.圖像像素的算數操作
這節學習如何對圖像的像素進行加減乘除操作,下面通過代碼演示加深對圖像像素進行加減乘除的理解。
void QuickDemo::operators_demo(Mat &image){Mat dst = Mat::zeros(image.size(), image.type());Mat m = Mat::zeros(image.size(), image.type());//dst = image - Scalar(50, 50, 50);//dst = image + Scalar(50, 50 ,50);//mutiply(image, m, dst);// dst = image / Scalar(50, 50, 50);// imshow("圖像的算數操作", dst);int w = image.cols;int h = image.rows;int dims = image.channels();for (int row = 0; row < h; row++){for (int col = 0; col < w; col++){Vec3b p1 = image.at<Vec3b>(row, col);Vec3b p2 = m.at<Vec3b>(row, col);dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);}}imshow("加法操作", dst);
}
代碼解釋:at(row, col)表示獲取指定位置的像素,獲得的p1 是一個有三個元素的數組,saturate_cast是將相加后的像素值轉換為8位無符號(uchar)的整數,以適應dst圖像的像素范圍。
7.滾動條-調整圖像亮度
這節學習通過滾動條,用戶可以在窗口中自行調整圖像的亮度,下面是代碼演示:
Mat src, dst, m;
int lightness = 20;
static void on_track(int, void*) {m = Scalar(lightness, lightness, lightness);add(src, m, dst);//subtract(src, m, dst);imshow("亮度調整", dst);
}
void QuickDemo::tracking_bar_demo(Mat& image) {namedWindow("亮度調整", WINDOW_AUTOSIZE);dst = Mat::zeros(image.size(), image.type());m = Mat::zeros(image.size(), image.type());src = image;int max_value = 250;createTrackbar("Value Bar:", "亮度調整", &lightness, max_value, on_track);on_track(50, 0);
}
代碼解釋:這節的代碼和下一節一起進行解釋。
8.滾動條-調整對比度和亮度
這節學習在窗口中添加兩個滾動條,一個調整圖像的亮度,另一個調整圖像的對比度,下面是代碼演示:
static void on_lightness(int b, void* userdata){Mat image = *((Mat*)userdata);Mat dst = Mat::zeros(image.size(), image.type());Mat m = Mat::zeros(image.size(), image.type());addWeighted(image, 1.0, m, 0, b, dst);imshow("亮度與對比度調整", dst);
}
static void on_contrast(int b, void* userdata){Mat image = *((Mat*)userdata);Mat dst = Mat::zeros(image.size(), image.type());Mat m = Mat::zeros(image.size(), image.type());double contrast = b / 100.0;addWeighted(image, contrast, m, 0.0, 0, dst);imshow("亮度與對比度調整", dst);
}void QuickDemo::tracking_bar_demo(Mat &image){nameWindow("亮度與對比度調整", WINDOW_AUTOSIZE);int lightness = 50;int max_value = 250;int contrast_value = 100;createTrackbar("Value Bar:", "亮度與對比度調整“, &lightness,max_value, on_lightness, (void*)(&image));createTrackbar("Contrast Bar:", "亮度與對比度調整“, &contrast_value,200, on_contrast, (void*)(&image));on_lightness(50, &image);
}
代碼解釋:在回調函數on_lightness()中,static: 表示該函數是靜態的,只在當前文件中可見。除此之外,還有靜態變量,靜態函數,靜態類。定義了變量的周期,在靜態函數中,只在當前文件中可見,防止其他文件有重名的函數。void:表示函數的返回類型為空;on_lightness:是函數名;int b:表示亮度調整參數;void* userdata:一個指向用戶數據的指針,通常用于傳遞圖像數據;
((Mat)userdata): 將userdata指針轉換為Mat* 類型并解引用,獲取圖像數據。addWeighted(image, 1.0, m, 0, b, dst); addWeighted: OpenCV中的函數,用于圖像的加權疊加。該函數的公式為:dst = image * 1.0 + m * 0 + b,即 dst = image + b,實現了亮度的調整。
最后定義了一個名為tracking_bar_demo的函數,用于創建一個窗口并添加了兩個滑動條(Tackbar),分別用于調整圖像的亮度和對比度。createTrackbar: OpebCV中的函數,用于創建一個滑動條。Value Bar: :滑動條的標簽。&lightness: 指向亮度值的指針,滑動條的當前值。max_value: 滑動條的最大值。on_lightness: 回調函數,當滑動條值改變時調用。(void*)(&image): 傳遞給回調函數的用戶數據,即圖像數據
9.鍵盤響應操作
這節學習通過鍵盤按鍵對圖像進行操作并顯示操作后的結果
void QuickDemo::key_demo(Mat& image) {Mat dst = Mat::zeros(image.size(), image.type());;while (true) {int c = waitKey(100);if (c == 27) {//Escbreak;}if (c == 49) {//Key#1std::cout << "you enter Key#1" << std::endl;cvtColor(image, dst, COLOR_BGR2GRAY);}if (c == 50) {// Key#2std::cout << "you enter Key#2" << std::endl;cvtColor(image, dst, COLOR_BGR2HSV);}if (c == 51) {//Key#3std::cout << "you enter Key#3" << std::endl;dst = image + Scalar(0, 128, 0);}//std::cout << c << std::endl;imshow("鍵盤響應", dst);}
}
代碼解釋:需要注意的是函數waitKey()返回的數據類型,總體上代碼不難。
10.圖像像素的邏輯操作
這節學習創建兩個圖像,對這兩個圖像進行與、或、非 操作;下面是代碼演示:
void QuickDemo::bitwise_demo(Mat& image) {Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);imshow("m1", m1);imshow("m2", m2);Mat dst;//Mat dst = ~image;//bitwise_not(image, dst);//bitwise_or(m1, m2, dst);bitwise_xor(m1, m2, dst);imshow("像素位操作", dst);
}
代碼解釋:rectangle() 是OpenCV庫中的函數,在圖像上繪制一個填充的矩形。m1表示目標圖像,即在m1上進行繪制,Rect() 這是矩形的參數,用于確定矩形的大小和位置;Scalar()這是矩形的顏色,-1表示矩形的厚度,這里就表示對矩形進行填充,除此之外還有其他參數,例如0,1,2;LINE_8這是繪制矩形時使用的線條類型,LINE_8表示8連通線型。0:這是可選的坐標縮放因子。
11.圖像的通道分離和合并
這節學習將RGB圖像的三個通道分離,并以單獨的紅色,綠色和藍色進行顯示;這是通道分離,通道合并時,學習如何將兩個任意的通道進行合并并顯示出來。下面時代碼演示:
void QuickDemo::channels_split_demo(Mat& image) {std::vector<Mat> mv;split(image, mv);imshow("藍色", mv[0]);imshow("綠色", mv[1]);imshow("紅色", mv[2]);Mat dst;mv[1] = 0;mv[2] = 0;merge(mv, dst);imshow("藍色", dst);int from_to[] = { 0, 2, 1, 1, 2, 0 };mixChannels(&image, 1, &dst, 1, from_to, 3);imshow("通道混合", dst);
}
代碼解釋:split() 函數用于將圖像分離成三個通道并存儲在mv變量中,這樣根據數組的操作可以對單獨的每個通道進行顯示,merge() 函數用于將兩個圖像進行合并,合并前將其余的兩個通道的數值置零就可以以單獨的紅、藍、綠進行顯示;最后用到一個通道混合的函數mixChannels(),mixChannels是OpenCV庫中的一個函數,void mixChannels(const Mat* src, size_t nsrcs, Mat* dst, size_t ndsts, const int* fromTo, size_t npairs);
用于將輸入圖像的某些通道賦值到輸出圖像的某些通道中。const Mat* src是輸入圖像數組,size_t nsrcs是輸入圖像的數量,Mat* dst輸出圖像數組,可以是一個或多個圖像。 size_t ndsts是輸出圖像數量。const int* fromTo:一個數組,指定輸入和輸出通道的映射關系。 size_t npairs是數組中映射對的數量。
12.圖像色彩空間轉換
這節學習如何從純色的背景中扣出前景然后對背景顏色進行轉換。需要注意的是背景顏色必須是純色的。下面是代碼演示以及實驗結果圖。
void QuickDemo::inrange_demo(Mat& image){Mat hsv;cvtColor(image, hsv, COLOR_BGR2HSV);Mat mask;inRange(hsv, Scalar(36, 43, 46), Scalar(77, 255, 255), mask);imshow("mask", mask);Mat redback = Mat::zeros(image.size(), image.type());redback = Scalar(40, 40, 200);bitwise_not(mask, mask);imshow("mask", mask);image.copyTo(redback, mask);imshow("roi區域提取", redback);
}
代碼解釋:inRange()是OpenCV庫中的函數,用于在HSV顏色空間中進行顏色閾值處理。具體來說,它將圖像中的像素值與給定的范圍進行比較,并將符合條件的像素設置為白色(255), 不符合條件的像素設置為黑色(0)。HSV顏色空間將顏色分解為色調(Hue)、飽和度(Saturation)和亮度(Value)三個分量;在上述代碼中hsv是輸入的圖像,Scalar(36, 43, 46): 是下限閾值,這里的閾值根據HSV顏色空間能夠進行查詢獲得,這里的三個數是綠色的閾值下限。Scalar(77, 255, 255)是綠色的閾值上限。mask:輸出圖像,是一個二值化的圖像,在這個圖像中符合顏色范圍的像素值為255,不符合的像素值為0。后面將掩碼取反操作,即前景圖像的像素值從原來的0變為255,背景像素值從原來的255變為0;接下來使用image.copyTo(redback, mask);這里使用掩碼復制掩碼中像素值為255的redback像素值。這里更深入的理解需要理解copyTo()函數。
13.圖像的像素值統計
這節學習如何計算圖像各通道的均值和方差。下面是代碼演示:
void QuickDemo::pixel_statistic_demo(Mat& image) {double minv, maxv;Point minLoc, maxLoc;std::vector<Mat>mv;split(image, mv);for (int i = 0; i < mv.size(); i++) {minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());std::cout <<"No.channels: "<< i <<"min value : " << minv << "max value : " << maxv << std::endl;}Mat mean, stddev;meanStdDev(image, mean, stddev);std::cout << "means:" << mean << std::endl;std::cout<< "stddev:" << stddev << std::endl;
}
代碼解釋:首先定義了兩個Point類型的變量分別命名為minLoc, mavLoc;;其次定義了一個std::vector類型的變量mv,其元素類型為Mat ,這里的mv可以用于存儲多個圖像或矩陣數據。.Point是OpenCV庫中定義的一個類,通常用于表示圖像中的二維坐標點(x, y)。minMaxLoc()是OpenCV庫中的函數,用于在給定的圖像或矩陣中查找最小值和最大值及其位置。minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat()); ,mv[i]:是輸入的數據,&minv這是指向double類型的指針,用于存儲找到的最小值。&minLoc這是指向Point類型的指針,用于存儲最小值的位置,Mat(): 這是一個空的Mat對象,表示掩碼(mask).如果不需要使用掩碼,可以傳遞一個空的Mat對象。掩碼用于指定在哪些區域中進行最小值和最大值的查找。meanStdDev是OpenCV中的函數。用于計算給定圖像的均值和標準差。
14.圖像幾何形狀繪制
這節學習在圖像上繪制或者填充矩形、圓形、橢圓形、線條等,下面是代碼演示:
void QuickDemo::darwing_demo(Mat& image) {Rect rect;rect.x = 250;rect.y = 150;rect.width = 100;rect.height = 100;Mat bg = Mat::zeros(image.size(), image.type());rectangle(image, rect, Scalar(0, 0, 255), 2, 8, 0);circle(image, Point(350, 400), 50, Scalar(255, 0, 0), -1, 8, 0);line(image, Point(100, 100), Point(200, 200), Scalar(255, 255, 0));RotatedRect rrt;rrt.center = Point(200, 200);rrt.size = Size(100, 200);rrt.angle = 90;ellipse(image, rrt, Scalar(0, 0, 255), 2, 8);imshow("矩形", image);
}
代碼解釋:Rect是OpenCV庫中定義的一個類,用于表示矩形區域。Rect rect這行代碼聲明了一個Rect類型的變量。rectangle() 函數用于在圖像上繪制一個矩形。2: 表示矩形邊框的厚度,如果設置為負數矩形會被填充。8表示8連接線,0表示旋轉角度。circle() 函數用于在圖像上繪制一個圓形。Point(100, 100): 這是Point類型的對象,表示圓心的位置。50:表示圓的半徑。line() 函數用于在圖像上繪制一條直線。 RotatedRect是OpenCV庫中的函數用來繪制橢圓。
15.隨機數與隨機顏色
這節學習如何生成隨機數并且根據隨機種子在創建的圖像中畫隨機線條。下面是代碼演示:
void QuickDemo::random_drawing() {Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);int w = canvas.cols;int h = canvas.rows;RNG rng(12345);while (true) {int c = waitKey(100);if (c == 27) {break;}int x1 = rng.uniform(0, w);int y1 = rng.uniform(0, h);int x2 = rng.uniform(0, w);int y2 = rng.uniform(0, h);int b = rng.uniform(0, 255);int g = rng.uniform(0, 255);int r = rng.uniform(0, 255);//canvas = Scalar(0, 0, 0);line(canvas, Point(x1, y1), Point(x2, y2), Scalar(b, g, r), 1, LINE_AA, 0);imshow("隨機繪制演示", canvas);}
}
代碼解釋:RNG rng(12345):這行代碼創建了一個隨機數生成器對象rng,并使用12345作為隨機種子,隨機種子是一個初始值,用于初始化隨機數生成器的狀態,相同的種子會產生相同的隨機數序列。隨機種子的作用是確保每次運行程序時,如果使用相同的種子,生成的隨機數序列是相同的。可以確保每次運行程序時生成的隨機數序列是可重復的。
16.多邊形填充與繪制
這節學習如何繪制多邊形:下面是代碼是演示:
void QuickDemo::polyline_drawing_demo() {Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);Point p1(100, 100);Point p2(350, 100);Point p3(450, 280);Point p4(320, 450);Point p5(80, 400);std::vector<Point>pts;pts.push_back(p1);pts.push_back(p2);pts.push_back(p3);pts.push_back(p4);pts.push_back(p5);//polylines(canvas, pts, true, Scalar(0, 0, 255), 8, 8, 0);//fillPoly(canvas, pts, Scalar(255, 255, 0), LINE_AA, 0);std::vector<std::vector<Point>>contours;contours.push_back(pts);drawContours(canvas, contours, -1, Scalar(255, 0, 0), -1);imshow("多邊形繪制", canvas);
}
代碼解釋:pts.push_back()將五個點添加到向量pts中,polyline() 函數是OpenCV中用于繪制多邊形的函數,canvas:繪制畫布。pts:點向量;true:表示多邊形是閉合的。fillPoly函數用于填充多邊形。drawContours()函數是OpenCV庫中繪制輪廓的函數。
17.鼠標操作與響應
這節學習通過鼠標繪制矩形提取圖像中的感興趣區域(ROI),下面是代碼演示:
Point sp(-1,-1);
Point ep(-1,-1);
Mat temp;
static void on_draw(int event, int x, int y, int flags, void* userdata){Mat image = *((Mat*)userdata);if(event == EVENT_LBUTTONDOWN){sp.x = x;sp.y = y;std::cout<<"start point:"<<sp<<sts::endl;}else if(event == EVENT_LBUTTONUP){ep.x = x;ep.y = y;int dx = ep.x - ep.x;int dy = ep.y - ep.y;if(dx>0 && dy>0){Rect box(sp.x, sp.y, dx, dy);rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);imshow("鼠標繪制", image);imshow("ROI區域", image(box));}else if(event ==EVENT_MOUSEMOVE){if(sp.x>0 && sp.y>0){ep.x = x;ep.y = y;int dx = ep.x - ep.x;int dy = ep.y - ep.y;if(dx>0 && dy>0){Rect box(sp.x, sp.y, dx, dy);temp.copyTo(image);rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);inshow("鼠標繪制", image);}}}
}
void QuickDemo::mouse_drawing_demo(Mat& image){nameWindow("鼠標繪制", WINDOW_AUTOSIZE);setMouseCallback("鼠標繪制”, on_draw, (void*)(&image));imshow("鼠標繪制", image);temp = image.clone();
}
代碼解釋:static void on_draw(int event, int x, int y, int flags, void* userdata){ 這行代碼定義了一個名為 on_draw的靜態函數,該函數在繪制事件發生時被調用。函數的參數包括:event: 表示繪制事件類型。當用戶點擊鼠標左鍵時,事件類型為EVENT_LBUTTONDOWN。flags:表示事件標志,userdata: 表示用戶數據,在這個例子中是一個指向Mat類型的指針,表示要繪制圖像。void QuickDemo::mouse_drawing_demo(Mat& image){ 這行代碼定義了一個名為mouse_drawing_demo的函數,該函數用于實現鼠標繪制的功能,setMouseCallback()函數是OpenCV庫中的函數,為鼠繪制窗口設置鼠標回調函數。on_draw是回調函數的名稱,(void*)(&image)是將圖像地址傳遞給回調函數,以便在回調函數中使用。
18.圖像像素類型轉換與歸一化
這節學習將圖像通整數類型轉換為浮點數數類型并進行顯示
void QuickDemo::norm_demo(Mat& image) {Mat dst;std::cout << image.type() << std::endl;image.convertTo(image, CV_32F);std::cout << dst.type() << std::endl; // CV_8UC3, CV_32FC3normalize(image, dst, 1.0, 0, NORM_MINMAX);std::cout << dst.type() << std::endl;imshow("圖像數據歸一化", dst);
}
代碼解釋:這段代碼首先將圖像通過.convertTo()函數轉換為32位浮點數類型。然后通過 歸一化函數normlize()將圖像歸一化并存儲在dst中,歸一化的方式是NORM_MINMAX,最后顯示歸一化后的圖像數據dst。
19.圖像縮放與插值
這節學習圖像的縮放和插值操作,內容相對簡單。下面是代碼演示:
void QuickDemo::resize_demo(Mat& image) {Mat zoomin, zoomout;int h = image.rows;int w = image.cols;resize(image, zoomin, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);imshow("zoomin", zoomin);resize(image, zoomout, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);imshow("zoomout", zoomout);
}
代碼解釋:這里記住resize()函數就可以實現對圖像的縮放。
20.圖像翻轉
這節學習圖像的翻轉,只需要使用flip() 函數改變其參數就能實現圖像的水平翻轉、上下翻轉、對角線翻轉。代碼相對簡單。
void QuickDemo::flip_demo(Mat& image) {Mat dst;//flip(image, dst, 0);//上下翻轉//flip(image, dst, 1);//左右翻轉flip(image, dst, -1); //對角線翻轉,180°旋轉imshow("圖像翻轉", dst);
}
21.圖像旋轉
這節學習對圖像實現旋轉,圖像旋轉是通過仿射變換實現的,圖像旋轉后的高寬會發生變換,新的高寬通過下面圖示方式進行計算;
下面是代碼演示:
void QuickDemo::rotate_demo(Mat& image) {Mat dst, M;int w = image.cols;int h = image.rows;M = getRotationMatrix2D(Point2f(w / 2, h / 2), 45, 1.0);double cos = abs(M.at<double>(0, 0));double sin = abs(M.at<double>(0, 1));int nw = cos * w + sin * h;int nh = sin * w + cos * h;M.at<double>(0, 2) = M.at<double>(0, 2) + (nw / 2 - w / 2);M.at<double>(1, 2) = M.at<double>(1, 2) + (nh / 2 - h / 2);warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 255, 255));imshow("旋轉演示", dst);
}
代碼解釋:getRotationMatrix2D()是OpenCV庫中的函數,它的功能是生成一個二維的旋轉矩陣,Point2f類型的對象,表示旋轉的中心點,45表示旋轉的角度,1.0表示旋轉的縮放因子。M是旋轉矩陣,從旋轉矩陣中取出cos seta 和 sin seta,接下來重新計算高寬,然后將旋轉中心值重新賦值給M矩陣,組后通過warpAffine()函數進行旋轉放射變換。
22.視頻文件攝像頭使用
這節學習如何調用電腦攝像頭實時顯示攝像頭內容以及如何讀取視頻文件,對讀取到的視頻內容也可以使用圖像處理中的一些處理進行顯示,例如灰度變換、HSV、摳圖等。下面是代碼演示:
void QuickDemo::video_demo(Mat& image) {VideoCapture capture(0); // 讀出視頻文件capture(../video/xxx.mp4)Mat frame;while (true) {capture.read(frame);flip(frame, frame, 1);if (frame.empty()) {break;}imshow("視頻", frame);//TODO: do something...int c = waitKey(10);if (c == 27) {break;}}//releasecapture.release();
}
代碼解釋:VideoCapture是OpenCV中用于視頻捕獲的類,capture(0)表示從默認攝像頭捕獲視頻,Mat是OpenCV中用于存儲圖像數據的類。frame是一個Mat對象,用于存儲圖像數據的類。
23.視頻處理與保存
這節學習如何獲取讀取到的視頻屬性,例如高寬、視頻幀總數、每秒的幀數(FPS)等。最后學習如何保存視頻。下面是代碼演示:
void QuickDemo::video_demo(Mat& image) {VideoCapture capture("C:/code/workspace/data/video/dance.mp4"); // 讀出視頻文件capture(../video/xxx.mp4) 讀取攝像頭capture(0)int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);int count = capture.get(CAP_PROP_FRAME_COUNT);double fps = capture.get(CAP_PROP_FPS);std::cout << "fram width:" << frame_width << std::endl;std::cout << "fram height:" << frame_height << std::endl;std::cout << "Number of frames:" << count << std::endl;std::cout << "FPS:" <<fps<< std::endl;VideoWriter writer("C:/code/workspace/data/video/test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);Mat frame;while (true) {capture.read(frame);flip(frame, frame, 1);if (frame.empty()) {break;}namedWindow("視頻", WINDOW_NORMAL);resizeWindow("視頻", 800, 600);imshow("視頻", frame);//colorSpace_Demo(frame);writer.write(frame);//TODO: do something...int c = waitKey(10);if (c == 27) {break;}}//releasecapture.release();writer.release();
}
代碼解釋:獲取視頻的高寬等屬性用capture.get()函數,保存視頻文件時,VideoWriter是OpenCV庫中用于視頻寫入的類。writer是VideoWriter對象的名稱。capture是一個VideoCapture對象,用于視頻捕獲。write 是 VideoWriter 類的一個成員函數,用于將一幀圖像寫入到視頻文件中。
24.圖像直方圖
這節學習繪制rgb圖像三個通道的直方圖曲線,并將它們可視化在一張畫布上,下面是代碼演示:
void QuickDemo::Histogram_demo(Mat& image){//三通道分離std::vector<Mat> bgr_plane;split(image, bgr_plane);//定義參數變量const int channels[1] = {0};const int bins[1] = {256};float hranges[2] = {0, 255};const float* ranges[1] = {hranges};Mat b_hist;Mat g_hist;Mat r_hist;//計算Blue、Green、Red通道的直方圖calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);//顯示直方圖int hist_w = 512;int hist_h = 400;int bin_w = cvRound((double)hist_w / bins[0]); //每個bin的寬度Mat histImage = Mat::zeros(histImage.rows, NORM_MINMAX, -1, Mat());Mat histImage = Mat::zeros(histImage.rows, NORM_MINMAX, -1, Mat());Mat histImage = Mat::zeros(histImage.rows, NORM_MINMAX, -1, Mat());//繪制直方圖曲線for (int i = 1; i<bins[0];i++){line(histImage, Point(bin_w*(i-1), hist_h-cvRound(b_hist.at<float>(i-1))),Point(bin_w*(i), hist_h -cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);line(histImage, Point(bin_w*(i-1), hist_h-cvRound(g_hist.at<float>(i-1))),Point(bin_w*(i), hist_h -cvRound(g_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);line(histImage, Point(bin_w*(i-1), hist_h-cvRound(r_hist.at<float>(i-1))),Point(bin_w*(i), hist_h -cvRound(r_hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);//顯示直方圖nameWindow("Histogram Demo", WINDOW_AUTOSIZE);imshow("Histogram Demo", histImage);
代碼解釋:cvRound是OpenCV中的一個函數,用于將浮點數四舍五入為最接近的整數。這里關于線段的起始坐標還沒有搞清楚,
25.二維直方圖
代碼演示:
void QuickDemo::Histogram_2d_demo(Mat& image) {//2D直方圖Mat hsv, hs_hist;cvtColor(image, hsv, COLOR_BGR2HSV); // H取值范圍是(0,180),S和V的取值范圍是(0,255)int hbins = 30, sbins = 32;int hist_bins[] = { hbins, sbins };float h_range[] = { 0, 180 };float s_range[] = { 0, 256 };const float* hs_ranges[] = { h_range, s_range };int hs_channels[] = { 0,1 };calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false);double maxVal = 0;minMaxLoc(hs_hist, 0, &maxVal, 0, 0);int scale = 10;Mat hist2d_image = Mat::zeros(sbins * scale, hbins * scale, CV_8UC3);for (int h = 0; h < hbins; h++) {for (int s = 0; s < sbins; s++) {float binVal = hs_hist.at<float>(h, s);int intensity = cvRound(binVal * 255 / maxVal);rectangle(hist2d_image, Point(h * scale, s * scale),Point((h + 1) * scale - 1, (s + 1) * scale - 1), Scalar::all(intensity), -1);}}applyColorMap(hist2d_image, hist2d_image,COLORMAP_JET);imshow("H-S Histogram", hist2d_image);imwrite("C:/code/workplace/data/image/his2d.jpg", hist2d_image);
}
26.直方圖均衡化
原理:統計直方圖 》歸一化直方圖》累計直方圖》區間轉換
直方圖均衡化只能對單通道灰度圖像進行均衡化,如果對彩色圖像進行均衡化可以將彩色圖像轉換到HSV色彩空間然后對V通道的亮度進行直方圖均衡化。
void QuickDemo::histogram_eq_demo(Mat& image){Mat gray;cvtColor(image, gray, COLOR_BGR2GRAY);Mat dst;equalizeHist(gray, dst);imshow("直方圖均衡化", dst);
}
27.圖像卷積和高斯模糊
這節學習對圖像進行卷積操作和使用高斯核函數對圖像進行模糊處理,下面是代碼演示:
void QuickDemo::blur_demo(Mat& image) {Mat dst;blur(image, dst, Size(13, 13), Point(-1, -1));imshow("圖像模糊", dst);
}void QuickDemo::gaussian_blur_demo(Mat& image) {Mat dst;GaussianBlur(image, dst, Size(5, 5), 15);imshow("高斯模糊", dst);
}
代碼解釋:卷積操作使用blur() 函數,GaussianBlur() 是高斯模糊的函數。
28.雙邊模糊
雙邊模糊(Bilateral blur)是一種圖像處理技術,旨在模糊圖像中的細節,同時保留邊緣的清晰度,從而防止邊緣變得模糊或失真。與傳統的高斯模糊不同,雙邊模糊考慮了像素之間的空間距離和像素值之間的差異。具體來說,它利用了一個像素值相似性函數,這個函數在像素之間的距離較大時減小,從而保留了邊緣的銳利度。因此,雙邊模糊常用于需要在保留細節的同時進行圖像平滑處理的應用場景,如圖像去噪或者一些圖像特效的實現中。
void QuickDemo::bifilter_demo(Mat& image) {Mat dst;bilateralFilter(image, dst, 0, 100, 10);imshow("雙邊模糊", dst);
}
29.人臉實時檢測
這節通過加載訓練好的模型文件調用攝像頭 或者加載視頻文件實現人臉的實時檢測,在實現該項目時需要用到兩個文件,這兩個文件觀看b站視頻OpenCV與C++入門30講相應的視頻獲得下載地址,下面是代碼演示:
#include <opencv2/opencv.hpp>
#include <iostream>using namespace cv;int main(int argc, char** argv) {;std::string pf_file_path_path = "C:/code/workspace/model/opencv_face_detector_uint8.pb";std::string pbtxt_file_path = "C:/code/workspace/model/opencv_face_detector.pbtxt";cv::dnn::Net net = cv::dnn::readNetFromTensorflow(pf_file_path_path, pbtxt_file_path);VideoCapture cap(0);cv::Mat frame;while (true) {cap.read(frame);if (frame.empty()) {break;}cv::Mat blob = cv::dnn::blobFromImage(frame, 1.0, Size(300, 300), cv::Scalar(104, 177, 123), false, false);net.setInput(blob);cv::Mat probs = net.forward();// 1x1xNx7cv::Mat detectMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());for (int row = 0; row < detectMat.rows; row++) {float conf = detectMat.at<float>(row, 2);if (conf > 0.5) {float x1 = detectMat.at<float>(row, 3) * frame.cols;float y1 = detectMat.at<float>(row, 4) * frame.rows;float x2 = detectMat.at<float>(row, 5) * frame.cols;float y2 = detectMat.at<float>(row, 6) * frame.rows;cv::Rect box(x1, y1, x2 - x1, y2 - y1);cv::rectangle(frame, box, cv::Scalar(0, 0, 255), 2, 8);}}cv::imshow("OpenCV4.6DNN人臉檢測演示", frame);char c = waitKey(1);if (c == 27) {break;} }cv::waitKey(0);cv::destroyAllWindows();return 0;
}