###### 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,右邊為原圖。

左為控制板,右為過濾後只留身體的圖。

流程是先將圖片從 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>
效果如下,原圖(左)、過濾(右):
 
原圖進行遮罩後:

上圖成功過濾出派大星的身體(和沙灘背景),會用到 `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()` 一個更常見的用法是只使用前三個參數,對兩個不同的輸入圖檔做布林運算。