###### tags: `東京威力 TEL` # I. 影像辨識 OpenCV ## <font color="orange"> 04. 控制面板</font> ### <font color="pink">4-1. 創視窗和控制條</font> 函式使用蠻簡單的,直接看著用就好了。 ```cpp= #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; void callback(int, void*){ } int main(){ namedWindow("TrackBar"); // 建立新視窗 resizeWindow("TrackBar", 640, 320); // 視窗大小 // 建立控制條 createTrackbar("Hue Min", "TrackBar", 0, 179, callback); createTrackbar("Hue Max", "TrackBar", 0, 179, callback); createTrackbar("Sat Min", "TrackBar", 0, 179, callback); createTrackbar("Sat Max", "TrackBar", 0, 179, callback); createTrackbar("Val Min", "TrackBar", 0, 179, callback); createTrackbar("Val Max", "TrackBar", 0, 179, callback); waitKey(0); return 0; } ``` ### <font color="pink">4-2. 控制條值的讀取與默認值</font> ><font color="magenza">getTrackbarPos(控制項目名稱, 視窗名稱)</font> 我們每過0.5秒印出一次控制條上的值,並且設定 `Hue Min` 的初始值為 55。 ```cpp= #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; void callback(int, void*){ } int main(){ namedWindow("TrackBar"); resizeWindow("TrackBar", 640, 320); createTrackbar("Hue Min", "TrackBar", 0, 179, callback); createTrackbar("Hue Max", "TrackBar", 0, 179, callback); createTrackbar("Sat Min", "TrackBar", 0, 179, callback); createTrackbar("Sat Max", "TrackBar", 0, 179, callback); createTrackbar("Val Min", "TrackBar", 0, 179, callback); createTrackbar("Val Max", "TrackBar", 0, 179, callback); // 控制條的默認值(沒設定就是0) setTrackbarPos("Hue Min", "TrackBar", 55); while(true){ // 讀取控制條的值 int hue_M = getTrackbarPos("Hue Min", "TrackBar"); int hue_m = getTrackbarPos("Hue Max", "TrackBar"); int sat_M = getTrackbarPos("Sat Max", "TrackBar"); int sat_m = getTrackbarPos("Sat Min", "TrackBar"); int val_M = getTrackbarPos("Val Max", "TrackBar"); int val_m = getTrackbarPos("Val Min", "TrackBar"); printf("Hue: %d ~ %d\n", hue_m, hue_M); printf("Sat: %d ~ %d\n", sat_m, sat_M); printf("Val: %d ~ %d\n===\n", val_m, val_M); waitKey(500); } waitKey(0); return 0; } ``` ### <font color="pink">4-3. 過濾顏色</font> 左為 HSV,右邊為原圖。 ![](https://i.imgur.com/1JmWpwU.jpg) 左為控制板,右為過濾後只留身體的圖。 ![](https://i.imgur.com/19B0ymo.png) 流程是先將圖片從 BGR 轉 HSV,再用 `TrackBar` 調整 HSV 三個參數,盡量濾出粉粉的肉體。 ><font color= "magenza"> void inRange(原圖,HSV下界,HSV上界,輸出圖)</font> 注意到上述 `HSV上下界` 是 `cv::Scalar`,宣告時傳入三個參數的建構子即可。(行35、行36) ```cpp=1 #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; int main(){ string path = "patrick.png"; Mat img = imread(path); Mat img_hsv; Mat mask; // 過濾圖片 resize(img, img, Size(img.cols/2, img.rows/2)); cvtColor(img, img_hsv, COLOR_BGR2HSV); // 要對 hsv 過濾 namedWindow("tracker"); resizeWindow("tracker", 500, 500); createTrackbar("Hue Min", "tracker", 0, 179); createTrackbar("Hue Max", "tracker", 0, 179); createTrackbar("Sat Min", "tracker", 0, 255); createTrackbar("Sat Max", "tracker", 0, 255); createTrackbar("Val Min", "tracker", 0, 255); createTrackbar("Val Max", "tracker", 0, 255); while(true){ int hue_m = getTrackbarPos("Hue Min", "tracker"); int hue_M = getTrackbarPos("Hue Max", "tracker"); int sat_m = getTrackbarPos("Sat Min", "tracker"); int sat_M = getTrackbarPos("Sat Max", "tracker"); int val_m = getTrackbarPos("Val Min", "tracker"); int val_M = getTrackbarPos("Val Max", "tracker"); // Scalar 實例 lower, upper Scalar lower(hue_m, sat_m, val_m); Scalar upper(hue_M, sat_M, val_M); // 將 img_hsv 過濾至 mask inRange(img_hsv, lower, upper, mask); imshow("image", img); imshow("img_hsv", img_hsv); imshow("mask", mask); if(waitKey(1) == 27) break; } return 0; } ``` 以下是寫好的一個函式,輸入原始圖片,輸出 mask 和 TrackBar。 ```cpp void tracker(Mat img){ Mat img_hsv, mask, result; cvtColor(img, img_hsv, COLOR_BGR2HSV); namedWindow("track_bar"); resizeWindow("track_bar", 500, 500); createTrackbar("Hue Min", "track_bar", 0, 179); createTrackbar("Hue Max", "track_bar", 0, 179); createTrackbar("Sat Min", "track_bar", 0, 255); createTrackbar("Sat Max", "track_bar", 0, 255); createTrackbar("Val Min", "track_bar", 0, 255); createTrackbar("Val Max", "track_bar", 0, 255); setTrackbarPos("Hue Max", "track_bar", 179); setTrackbarPos("Sat Max", "track_bar", 255); setTrackbarPos("Val Max", "track_bar", 255); while(true){ int hue_m = getTrackbarPos("Hue Min", "track_bar"); int hue_M = getTrackbarPos("Hue Max", "track_bar"); int sat_m = getTrackbarPos("Sat Min", "track_bar"); int sat_M = getTrackbarPos("Sat Max", "track_bar"); int val_m = getTrackbarPos("Val Min", "track_bar"); int val_M = getTrackbarPos("Val Max", "track_bar"); Scalar lower(hue_m, sat_m, val_m); Scalar upper(hue_M, sat_M, val_M); inRange(img_hsv, lower, upper, mask); // // usage: debug // printf("Hue: %d ~ %d\n", hue_m, hue_M); // printf("Sat: %d ~ %d\n", sat_m, sat_M); // printf("Val: %d ~ %d\n===\n", val_m, val_M); imshow("img", img); imshow("Mask", mask); if(waitKey(1) ==27) break; } } ``` ### <font color="pink">4-4. 遮罩原圖</font> 效果如下,原圖(左)、過濾(右): ![](https://i.imgur.com/6nf1jqC.png =45%x) ![](https://i.imgur.com/c5pyuuf.png =45%x) 原圖進行遮罩後: ![](https://i.imgur.com/gVuTQ7e.png =91%x) 上圖成功過濾出派大星的身體(和沙灘背景),會用到 `bitwise_and()`。 遮罩意思是將「原圖」和「參考圖」或「單通道過濾圖」做布林運算並輸出。 例如在上面的例子中,過濾圖中的白色部分會在原圖中保留,反之變為黑色。 > <font color="magenza">bitwise(輸入圖檔一, 輸入圖檔二, 輸出圖檔, 遮罩)</font> 特別注意到行45,在遮罩前要先「讓輸出圖檔的大小和原圖相同」,不然無法遮罩。 ```cpp= #include <iostream> #include <opencv2/opencv.hpp> using namespace std; using namespace cv; void tracker(Mat img){ Mat img_hsv, mask, result; cvtColor(img, img_hsv, COLOR_BGR2HSV); namedWindow("track_bar"); resizeWindow("track_bar", 500, 500); createTrackbar("Hue Min", "track_bar", 0, 179); createTrackbar("Hue Max", "track_bar", 0, 179); createTrackbar("Sat Min", "track_bar", 0, 255); createTrackbar("Sat Max", "track_bar", 0, 255); createTrackbar("Val Min", "track_bar", 0, 255); createTrackbar("Val Max", "track_bar", 0, 255); setTrackbarPos("Hue Max", "track_bar", 179); setTrackbarPos("Sat Max", "track_bar", 255); setTrackbarPos("Val Max", "track_bar", 255); while(true){ int hue_m = getTrackbarPos("Hue Min", "track_bar"); int hue_M = getTrackbarPos("Hue Max", "track_bar"); int sat_m = getTrackbarPos("Sat Min", "track_bar"); int sat_M = getTrackbarPos("Sat Max", "track_bar"); int val_m = getTrackbarPos("Val Min", "track_bar"); int val_M = getTrackbarPos("Val Max", "track_bar"); Scalar lower(hue_m, sat_m, val_m); Scalar upper(hue_M, sat_M, val_M); inRange(img_hsv, lower, upper, mask); // // usage: debug // printf("Hue: %d ~ %d\n", hue_m, hue_M); // printf("Sat: %d ~ %d\n", sat_m, sat_M); // printf("Val: %d ~ %d\n===\n", val_m, val_M); imshow("Img", img); imshow("Mask", mask); // 先讓 Mat result 和 Mat img 大小一樣 result = Mat::zeros(img.size(), CV_8UC3); // 才能使用 bitwise_and() bitwise_and(img, img, result, mask); imshow("Result", result); if(waitKey(1) ==27) break; } } int main(){ string path = "patrick.png"; Mat img = imread(path); resize(img, img, Size(img.cols/2, img.rows/2)); tracker(img); return 0; } ``` `bitwise_and()` 一個更常見的用法是只使用前三個參數,對兩個不同的輸入圖檔做布林運算。