1. 程式人生 > 實用技巧 >OpenCV學習(8)

OpenCV學習(8)

OpenCV仿射變換

 1 int warpExample(void) {
 2     // OpenCV仿射變換 
 3     // T = M × X
 4     cv::Point2f srcTri[3];
 5     cv::Point2f dstTri[3];
 6     cv::Mat rotMat(2, 3, CV_32FC1);
 7     cv::Mat warpMat(2, 3, CV_32FC1);
 8     cv::Mat src, warpDst, warpRotateDst;
 9     src = cv::imread("dog.jpg", cv::IMREAD_COLOR);
10 11 if (src.empty()) 12 return -1; 13 14 warpDst = cv::Mat::zeros(src.rows, src.cols, src.type()); 15 srcTri[0] = cv::Point2f(0, 0); 16 srcTri[1] = cv::Point2f(src.cols - 1.f, 0); 17 srcTri[2] = cv::Point2f(0, src.cols - 1.f); 18 dstTri[0] = cv::Point2f(src.cols * 0.0f, src.rows * 0.33f
); 19 dstTri[1] = cv::Point2f(src.cols * 0.85f, src.rows * 0.25f); 20 dstTri[2] = cv::Point2f(src.cols * 0.15f, src.rows * 0.7f); 21 22 // 通過3點對應關係獲得反射變換矩陣 23 // https://blog.csdn.net/claroja/article/details/83624701 24 warpMat = cv::getAffineTransform(srcTri, dstTri); 25 // 仿射變換 26 // https://www.xuebuyuan.com/509229.html
27 cv::warpAffine(src, warpDst, warpMat, warpDst.size()); 28 29 cv::Point center = cv::Point(warpDst.cols / 2, warpDst.rows / 2); 30 double angle = -50.0; 31 double scale = 0.6; 32 33 // 獲得仿射變化矩陣 引數:中心點 旋轉角度 縮放比例 34 rotMat = cv::getRotationMatrix2D(center, angle, scale); 35 cv::warpAffine(warpDst, warpRotateDst, rotMat, warpDst.size()); 36 cv::namedWindow("Src", cv::WINDOW_AUTOSIZE); 37 cv::imshow("Src", src); 38 cv::namedWindow("Warp", cv::WINDOW_AUTOSIZE); 39 cv::imshow("Warp", warpDst); 40 cv::namedWindow("WarpRotate", cv::WINDOW_AUTOSIZE); 41 cv::imshow("WarpRotate", warpRotateDst); 42 // 展示了通過兩種不同的方式獲取仿射矩陣 在對影象作仿射變換 43 44 cv::waitKey(0); 45 return 0; 46 }

影象直方圖與其均衡化

int equalizeHistExample(void) {
    // 影象直方圖與其均衡化
    cv::Mat src = cv::imread("dog.jpg", cv::IMREAD_COLOR);
    if (src.empty())
        return -1;

    cv::Mat dst;
    cv::cvtColor(src, src, cv::COLOR_BGR2GRAY);
    // 影象直方圖均衡化
    cv::equalizeHist(src, dst);
    cv::namedWindow("Src", cv::WINDOW_AUTOSIZE);
    cv::namedWindow("Dst", cv::WINDOW_AUTOSIZE);
    // 可以看到影象對比度增強的了
    cv::imshow("Src", src);
    cv::imshow("Dst", dst);

    cv::waitKey(0);
    return 0;
}


int calcHistExample(void) {
    // 影象直方圖的計算 繪製出直方圖
    cv::Mat src, dst;
    src = cv::imread("dog.jpg", cv::IMREAD_COLOR);
    if (src.empty())
        return -1;

    vector<cv::Mat> bgrPlanes;
    // 影象通道的分離
    cv::split(src, bgrPlanes);
    int histSize = 256;
    float range[] = { 0, 256 };
    const float* histRange = { range };
    bool uniform = true;
    bool accumulate = false;
    cv::Mat bHist, gHist, rHist;
    // 直方圖的計算
    // https://blog.csdn.net/shuiyixin/article/details/80032167
    cv::calcHist(&bgrPlanes[0], 1, 0, cv::Mat(), bHist, 1, &histSize, &histRange, uniform, accumulate);
    cv::calcHist(&bgrPlanes[1], 1, 0, cv::Mat(), gHist, 1, &histSize, &histRange, uniform, accumulate);
    cv::calcHist(&bgrPlanes[2], 1, 0, cv::Mat(), rHist, 1, &histSize, &histRange, uniform, accumulate);

    // 繪製圖像直方圖
    int histW = 512, histH = 400;
    // cvRound返回跟引數最接近的整數值,即四捨五入 https://blog.csdn.net/sinat_36264666/article/details/78849125
    int binW = cvRound((double)histW / histSize);
    cv::Mat histImage(histH, histW, CV_8UC3, cv::Scalar(0, 0, 0));
    cv::normalize(bHist, bHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
    cv::normalize(gHist, gHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());
    cv::normalize(rHist, rHist, 0, histImage.rows, cv::NORM_MINMAX, -1, cv::Mat());

    // 繪製直方圖 一點沒有Python方便
    for (int i = 1; i < histSize; i++) {
        cv::line(histImage, cv::Point(binW * (i - 1), histH - cvRound(bHist.at<float>(i - 1))), cv::Point(binW * (i), histH - cvRound(bHist.at< float>(i))), cv::Scalar(255, 0, 0), 2, 8, 0);
        cv::line(histImage, cv::Point(binW * (i - 1), histH - cvRound(gHist.at<float>(i - 1))), cv::Point(binW * (i), histH - cvRound(gHist.at< float>(i))), cv::Scalar(0, 255, 0), 2, 8, 0);
        cv::line(histImage, cv::Point(binW * (i - 1), histH - cvRound(rHist.at<float>(i - 1))), cv::Point(binW * (i), histH - cvRound(rHist.at< float>(i))), cv::Scalar(0, 0, 255), 2, 8, 0);
    }

    cv::namedWindow("calcHist Demo", cv::WINDOW_AUTOSIZE);
    cv::imshow("calcHist Demo", histImage);


    cv::waitKey(0);
    return 0;
}
 1 int compareHistExample(void) {
 2     cv::Mat srcBase, hsvBase;
 3     cv::Mat srcTest1, hsvTest1;
 4     cv::Mat srcTest2, hsvTest2;
 5     cv::Mat hsvHalfDown;
 6 
 7     srcBase = cv::imread("sky1.jpg", cv::IMREAD_COLOR);
 8     srcTest1 = cv::imread("sky2.jpg", cv::IMREAD_COLOR);
 9     srcTest2 = cv::imread("sky3.jpg", cv::IMREAD_COLOR);
10 
11     // if ()
12     cv::cvtColor(srcBase, hsvBase, cv::COLOR_BGR2HSV);
13     cv::cvtColor(srcTest1, hsvTest1, cv::COLOR_BGR2HSV);
14     cv::cvtColor(srcTest2, hsvTest2, cv::COLOR_BGR2HSV);
15 
16     hsvHalfDown = hsvBase(cv::Range(hsvBase.rows / 2, hsvBase.rows - 1), cv::Range(0, hsvBase.cols - 1));
17     int hBins = 50, sBins = 60;
18     int histSize[] = { hBins, sBins };
19     float hRanges[] = { 0, 180 };
20     float sRanges[] = { 0, 256 };
21     const float* ranges[] = { hRanges, sRanges };
22 
23     int channels[] = { 0, 1 };
24     cv::MatND histBase, histHalfDown, histTest1, histTest2;
25 
26     cv::calcHist(&hsvBase, 1, channels, cv::Mat(), histBase, 2, histSize, ranges, true, false);
27     cv::normalize(histBase, histBase, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
28 
29     cv::calcHist(&hsvHalfDown, 1, channels, cv::Mat(), hsvHalfDown, 2, histSize, ranges, true, false);
30     cv::normalize(histHalfDown, histHalfDown, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
31 
32     cv::calcHist(&hsvTest1, 1, channels, cv::Mat(), hsvTest1, 2, histSize, ranges, true, false);
33     cv::normalize(histTest1, histTest1, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
34 
35     cv::calcHist(&hsvTest2, 1, channels, cv::Mat(), hsvTest2, 2, histSize, ranges, true, false);
36     cv::normalize(histTest2, histTest2, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
37 
38     for (int i = 0; i < 4; i++) {
39         int compareMethod = i;
40         double baseBase = cv::compareHist(histBase, histBase, compareMethod);
41         double baseHalf = cv::compareHist(histBase, histHalfDown, compareMethod);
42         double baseTest1 = cv::compareHist(histBase, histTest1, compareMethod);
43         double baseTest2 = cv::compareHist(histBase, histTest2, compareMethod);
44 
45         printf("Method [%d] Perfect, Base-Half, Base-Test1, Base-Test2 : %f, %f, %f, %f \n", i, baseBase, baseHalf, baseTest1, baseTest2);
46     }
47 
48     cv::waitKey(0);
49     return 0;
50 }

OpenCV反向投影

 1 cv::Mat src, hsv, hue;
 2 int bins = 25;
 3 
 4 void HistAndBackproj(int, void*) {
 5     cv::MatND hist;
 6     int histSize = MAX(bins, 2);
 7     float hueRange[] = { 0, 180 };
 8     const float* ranges = { hueRange };
 9     cv::calcHist(&hue, 1, 0, cv::Mat(), hist, 1, &histSize, &ranges, true, false);
10     cv::normalize(hist, hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat());
11     cv::MatND backproj;
12     // 建立反向投影
13     cv::calcBackProject(&hue, 1, 0, hist, backproj, &ranges, 1, true);
14     cv::imshow("BackProj", backproj);
15     int w = 400, h = 400;
16     int binW = cvRound((double)2 / histSize);
17     cv::Mat histImage = cv::Mat::zeros(w, h, CV_8UC3);
18     for (int i = 0; i < bins; i++) {
19         cv::rectangle(histImage, cv::Point(i * binW, h), cv::Point((i + 1) * binW, h - cvRound(hist.at<float>(i) * h / 255.0)), cv::Scalar(0, 0, 255), -1);
20     }
21     cv::imshow("Histogram", histImage);
22 }
23 
24 int main(void) {
25     // OpenCV反向投影
26     src = cv::imread("hand1.png", cv::IMREAD_COLOR);
27     if (src.empty())
28         return -1;
29 
30     cv::cvtColor(src, hsv, cv::COLOR_BGR2HSV);
31     hue.create(hsv.size(), hsv.depth());
32     int ch[] = { 0, 0 };
33     // 把輸入的矩陣(或矩陣陣列)的某些通道拆分複製給對應的輸出矩陣(或矩陣陣列)的某些通道中,其中的對應關係就由fromTo引數制定
34     cv::mixChannels(&hsv, 1, &hue, 1, ch, 1);
35     cv::createTrackbar("* Hue bins: ", "Source image", &bins, 180, HistAndBackproj);
36     HistAndBackproj(0, 0);
37     cv::imshow("Source image", src);
38 
39     cv::waitKey(0);
40     return 0;
41 }

模板匹配

 1 bool  useMask;
 2 cv::Mat img, templ, mask, result;
 3 const char* imageWindow = "Source Image";
 4 const char* resultWindow = "Result Image";
 5 int matchMethod;
 6 int maxTrackbar = 5;
 7 
 8 void MatchingMethod(int, void*) {
 9     cv::Mat imgDisplay;
10     img.copyTo(imgDisplay);
11     int resultCols = img.cols - templ.cols + 1;
12     int resultRows = img.rows - templ.rows + 1;
13     result.create(result.rows, result.cols, CV_32FC1);
14     bool methodAcceptsMask = (CV_TM_SQDIFF == matchMethod || matchMethod == CV_TM_CCOEFF_NORMED);
15     if (useMask && methodAcceptsMask) {
16         // 用於模板匹配
17         cv::matchTemplate(img, templ, result, matchMethod, mask);
18     }
19     else {
20         cv::matchTemplate(img, templ, result, matchMethod);
21     }
22 
23     cv::normalize(result, result, 0, 1, cv::NORM_MINMAX, -1, cv::Mat());
24     double minVal, maxVal;
25     cv::Point minLoc, maxLoc, matchLoc;
26     // minMaxLoc尋找矩陣(一維陣列當作向量,用Mat定義) 中最小值和最大值的位置.
27     cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, cv::Mat());
28     if (matchMethod == CV_TM_SQDIFF || matchMethod == CV_TM_SQDIFF_NORMED)
29         matchLoc = minLoc;
30     else
31         matchLoc = maxLoc;
32     cv::rectangle(imgDisplay, matchLoc, cv::Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), cv::Scalar::all(0), 2, 8, 0);
33     cv::rectangle(result, matchLoc, cv::Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), cv::Scalar::all(0), 2, 8, 0);
34     cv::imshow(imageWindow, imgDisplay);
35     cv::imshow(resultWindow, result);
36 }
37 
38 int main(void) {
39     // 模板匹配
40     // 查詢與模板影象匹配的影象區域
41     img = cv::imread("Lisa.png", cv::IMREAD_COLOR);
42     templ = cv::imread("LisaFace.png", cv::IMREAD_COLOR);
43 
44     if (img.empty() || templ.empty())
45         return -1;
46 
47     cv::createTrackbar("Method: \n 0; SQDIFF \n 1: SQDIFF NORMED \n 2: TM CCORR \n 3: TM CCORR NORMED \n 4: TM COEFF \n 5: TM COEFF NORMED", imageWindow, &matchMethod, maxTrackbar, MatchingMethod);
48     MatchingMethod(0, 0);
49 
50     cv::waitKey(0);
51     return 0;
52 }

影象中查詢輪廓

 1 cv::Mat src, srcGray;
 2 int thresh = 100;
 3 int maxThresh = 255;
 4 cv::RNG rng(12345);
 5 
 6 void threshCallback(int, void*) {
 7     cv::Mat cannyOutput;
 8     vector<vector<cv::Point>> contours;  // 輪廓
 9     vector<cv::Vec4i> hierarchy;
10     cv::Canny(srcGray, cannyOutput, thresh, thresh * 2, 3);  // 邊緣檢測
11     // https://blog.csdn.net/guduruyu/article/details/69220296
12     // 函式cv::findContour是從二值影象中來計算輪廓的,它可以使用cv::Canny()函式處理的影象,因為這樣的影象含有邊緣畫素;
13     // 也可以使用cv::threshold()或者cv::adaptiveThreshold()處理後的影象,其邊緣隱含在正負區域的交界處。
14     cv::findContours(cannyOutput, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
15     cv::Mat drawing = cv::Mat::zeros(cannyOutput.size(), CV_8UC3);
16 
17     for (size_t i = 0; i < contours.size(); i++) {
18         cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));  // 隨機選取顏色
19         // 畫出邊緣曲線
20         cv::drawContours(drawing, contours, (int)i, color, 2, 8, hierarchy, 0, cv::Point());
21     }
22 
23     cv::namedWindow("Contours", cv::WINDOW_AUTOSIZE);
24     cv::imshow("Contours", drawing);
25 }
26 
27 
28 int main(void) {
29     // 影象中查詢輪廓
30     src = cv::imread("bowl.jpg", cv::IMREAD_ANYCOLOR);
31     if (src.empty())
32         return -1;
33 
34     cv::cvtColor(src, srcGray, cv::COLOR_BGR2GRAY);
35     cv::blur(srcGray, srcGray, cv::Size(3, 3));
36     cv::namedWindow("Source", cv::WINDOW_AUTOSIZE);
37     cv::imshow("Source", src);
38     cv::createTrackbar("Canny thresh:", "Source", &thresh, maxThresh, threshCallback);
39     threshCallback(0, 0);
40 
41     cv::waitKey(0);
42     return 0;
43 }
 1 cv::Mat src, srcGray;
 2 int thresh = 100;
 3 int maxThresh = 255;
 4 cv::RNG rng(12345);
 5 
 6 void threshCallback(int, void*) {
 7     cv::Mat thresholdOutput;
 8     vector<vector<cv::Point>> contours;  // 輪廓
 9     vector<cv::Vec4i> hierarchy;
10     // 使用threshold進行邊緣檢測
11     cv::threshold(srcGray, thresholdOutput, thresh, 255, cv::THRESH_BINARY);
12     cv::findContours(thresholdOutput, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
13     vector<vector<cv::Point>> contoursPoly(contours.size());
14     vector<cv::Rect> boundRect(contours.size());
15     vector<cv::Point2f> center(contours.size());
16     vector<float> radius(contours.size());
17 
18     for (size_t i = 0; i < contours.size(); i++) {
19         // approxPolyDP 主要功能是把一個連續光滑曲線折線化,對影象輪廓點進行多邊形擬合。
20         // https://blog.csdn.net/kakiebu/article/details/79824856
21         cv::approxPolyDP(cv::Mat(contours[i]), contoursPoly[i], 3, true);
22         // 計算輪廓的垂直邊界最小矩形,矩形是與影象上下邊界平行的 引數是輪廓的點集
23         boundRect[i] = cv::boundingRect(cv::Mat(contoursPoly[i]));
24         // 計算最小包圍圓
25         cv::minEnclosingCircle(contoursPoly[i], center[i], radius[i]);
26     }
27 
28     cv::Mat drawing;
29     for (size_t i = 0; i < contours.size(); i++) {
30         cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
31         cv::drawContours(drawing, contoursPoly, (int)i, color, 1, 8, vector<cv::Vec4i>(), 0, cv::Point());
32         cv::rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);
33         cv::circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);
34     }
35 
36     cv::namedWindow("Contours", cv::WINDOW_AUTOSIZE);
37     cv::imshow("Contours", drawing);
38 }
39 
40 int main(void) {
41     // 為輪廓建立邊界框和圓
42     src = cv::imread("balloon.jpg", cv::IMREAD_ANYCOLOR);
43     if (src.empty())
44         return -1;
45 
46     cv::cvtColor(src, srcGray, cv::COLOR_BGR2GRAY);
47     cv::blur(srcGray, srcGray, cv::Size(3, 3));
48     cv::namedWindow("Source", cv::WINDOW_AUTOSIZE);
49     cv::imshow("Source", src);
50     cv::createTrackbar("Canny thresh:", "Source", &thresh, maxThresh, threshCallback);
51     threshCallback(0, 0);
52 
53     cv::waitKey(0);
54     return 0;
55 }