鄭州市熱點新聞優(yōu)化游戲卡頓的軟件
文章目錄
- 一、輪廓
- findContours發(fā)現(xiàn)輪廓
- drawContours繪制輪廓
- 代碼
- 二.幾何及特性概括——凸包(Convex Hull)
- 凸包概念
- 凸包掃描算法介紹——Graham掃描算法
- 相關API介紹
- 程序示例
- 輪廓集合及特性性概括——輪廓周圍繪制矩形框和圓形
- 相關理論介紹
- 輪廓周圍繪制矩形 -API
- 繪制步驟
- 程序?qū)嵗?/li>
- 四.圖像矩(Image Moments)
- 1、相關理論
- 2、API介紹
- 計算輪廓面積cv::contourArea
- .計算輪廓長度cv::arcLength
- 例程
- 五、多邊形測試
- 1.相關理論
- 2.相關API介紹
- 程序示例
一、輪廓
輪廓發(fā)現(xiàn)是基于圖像邊緣提取的基礎尋找對象輪廓的方法。 所以邊緣提取的閾值選定會影響最終輪廓發(fā)現(xiàn)結果
輪廓查找步驟:
- 輸入圖像轉(zhuǎn)為灰度圖像cvtColor
- 使用Canny進行邊緣提取或者threshold閾值操作,得到二值圖像
- 使用findContours尋找輪廓
- 使用drawContours繪制輪廓
findContours發(fā)現(xiàn)輪廓
在二值圖像上發(fā)現(xiàn)輪廓使用
cv::findContours(
InputOutputArray binImg, 輸入圖像,非0的像素被看成1,0的像素值保持不變,8-bit
OutputArrayOfArrays contours, 全部發(fā)現(xiàn)的輪廓對象
OutputArray, hierachy 圖該的拓撲結構 std::vector<cv::Vec4i>,可選,該輪廓發(fā)現(xiàn)算法正是基于圖像拓撲結構實現(xiàn)。它的元素與輪廓的數(shù)量一樣多。對于每個第 i 個輪廓輪廓[i],元素hierarchy[i][0]、hierarchy[i][1]
int mode, 輪廓返回的模式
int method, 發(fā)現(xiàn)方法
Point offset=Point() 輪廓像素的位移,默認(0, 0)沒有位移
)
drawContours繪制輪廓
在二值圖像上發(fā)現(xiàn)輪廓cv::findContours之后對發(fā)現(xiàn)的輪廓數(shù)據(jù)進行繪制顯示
drawContours(
InputOutputArray binImg, 輸出圖像
OutputArrayOfArrays contours, 全部發(fā)現(xiàn)的輪廓對象
Int contourIdx 輪廓索引號
const Scalar & color, 繪制顏色
int thickness,/ 繪制線寬
int lineType , 線的類型LINE_8
InputArray hierarchy, 拓撲結構圖
int maxlevel, 最大層數(shù), 0只繪制當前的,1表示繪制繪制當前及其內(nèi)嵌的輪廓
Point offset=Point() 輪廓位移,可選
代碼
//輪廓發(fā)現(xiàn):通過cv::fingContoursAPI查找輪廓,通過cv::drawContours繪制輪廓
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>using namespace cv;
using namespace std;int threshold_value = 100;
int threshold_max = 255;
RNG rng;
const char* output_win = "Demo_Contour";
void Demo_Contours(int, void*);
Mat src,dst;
int main(int argc, char** argv) {src = imread("D:/photos/45.png");if (src.empty()) {printf("could not load image...\n");return -1;}namedWindow("input image", CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);imshow("input image", src);cvtColor(src, src, CV_BGR2GRAY);//灰度化圖像,為Canny邊緣檢測做準備const char* trackbar_title = "threshold_value";createTrackbar(trackbar_title, output_win, &threshold_value, threshold_max, Demo_Contours);//動態(tài)調(diào)整Canny邊緣檢測的閾值Demo_Contours(0, 0);//使程序剛開始就有結果,與createTrackbar無關waitKey(0);return 0;
}void Demo_Contours(int, void*) {Mat canny_output;vector<vector<Point>> contours;vector<Vec4i> hierachy;Canny(src, canny_output, threshold_value, threshold_value * 2, 3, false);//Canny邊緣檢測,3代表算子尺寸imshow("canny image", canny_output);findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));//contours儲存輪廓的點集,輪廓提取方式為RETR_TREE,輪廓表達為:CHAIN_APPROX_SIMPLEdst = Mat::zeros(src.size(), CV_8UC3);RNG rng(12345);for (size_t i = 0; i < contours.size(); i++) {//逐條繪制輪廓Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));}imshow(output_win, dst);}
二.幾何及特性概括——凸包(Convex Hull)
凸包概念
什么是凸包(Convex Hull),在一個多變形邊緣或者內(nèi)部任意兩個點的連線都包含在多邊形邊界或者內(nèi)部。
**正式定義:**包含點集合S中所有點的最小凸多邊形稱為凸包
凸包掃描算法介紹——Graham掃描算法
- 首先選擇Y方向最低的點作為起始點p0。
- 從p0開始極坐標掃描,依次添加p1….pn(排序順序是根據(jù)極坐標的角度大小,逆時針方向)。
- 對每個點pi來說,如果添加pi點到凸包中導致一個左轉(zhuǎn)向(逆時針方法)則添加該點到凸包,
反之如果導致一個右轉(zhuǎn)向(順時針方向)刪除該點從凸包中。
相關API介紹
convexHull(
InputArray points,// 輸入候選點,來自findContours
OutputArray hull,// 凸包
bool clockwise,// default true, 順時針方向
bool returnPoints)// true 表示返回點個數(shù),如果第二個參數(shù)是 vector<Point>則自動忽略
}
凸包逼近實現(xiàn)步驟:
-
首先把圖像從RGB轉(zhuǎn)為灰度。
-
然后再轉(zhuǎn)為二值圖像。
-
在通過發(fā)現(xiàn)輪廓得到候選點。
-
凸包API調(diào)用。
-
繪制顯示。
程序示例
#include <opencv2/opencv.hpp>
#include <opencv2/dnn.hpp>
#include <iostream>using namespace cv;
using namespace std;int threshold_value = 100;
int threshold_max = 255;
RNG rng(12345);
const char* output_win = "Demo_convex hull";
void threshold_callback(int, void*);
Mat src, dst,dst2,gray_src;
int main(int argc, char** argv) {src = imread("D:/photos/45.png");if (src.empty()) {printf("could not load image...\n");return -1;}namedWindow("input image", CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);const char* trackbar_label = "threshold:";imshow("input image", src);cvtColor(src, gray_src, CV_BGR2GRAY);blur(gray_src, gray_src, Size(3, 3), Point(-1, -1), BORDER_DEFAULT);//均值模糊進行降噪處理imshow("src_gray", gray_src);createTrackbar(trackbar_label, output_win, &threshold_value, threshold_max, threshold_callback);threshold_callback(0, 0);waitKey(0);return 0;
}
void threshold_callback(int, void*) {Mat bin_output;vector<vector<Point>> contours;vector<Vec4i> hierachy;threshold(gray_src, bin_output, threshold_value, threshold_max, THRESH_BINARY);findContours(bin_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));vector<vector<Point>> convexs(contours.size());dst = Mat::zeros(src.size(), CV_8UC3);dst2 = Mat::zeros(src.size(), CV_8UC3);for (size_t i = 0; i < contours.size(); i++) {Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));convexHull(contours[i], convexs[i], false, true);//drawContours(dst, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));}vector<Vec4i> empty(0);for (size_t k = 0; k < contours.size(); k++) {Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));drawContours(dst2, contours, k, color, 2, LINE_8, hierachy,1, Point(0, 0));drawContours(dst, convexs, k, color, 2, LINE_8, empty, 0, Point(0, 0));//注意此時hieracgy選項填Mat()}imshow(output_win, dst);imshow("contours_Demo", dst2);return;
}
輪廓集合及特性性概括——輪廓周圍繪制矩形框和圓形
相關理論介紹
輪廓周圍繪制矩形 -API
approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
基于RDP算法實現(xiàn),目的是減少多邊形輪廓點數(shù)。
cv::minEnclosingCircle(InputArray points, //得到最小區(qū)域圓形
Point2f& center, // 圓心位置
float& radius)// 圓的半徑
cv::fitEllipse(InputArray points)得到最小橢圓
繪制步驟
首先將圖像變?yōu)槎祱D像。
發(fā)現(xiàn)輪廓,找到圖像輪廓。
通過相關API在輪廓點上找到最小包含矩形和圓,旋轉(zhuǎn)矩形與橢圓。
繪制它們。
程序?qū)嵗?/h2>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;
Mat src, gray_src, drawImg;
int threshold_v = 170;
int threshold_max = 255;
const char* output_win = "rectangle-demo";
RNG rng(12345);
void Contours_Callback(int, void*);
int main(int argc, char** argv) {src = imread("D:/photos/45.png");if (!src.data) {printf("could not load image...\n");return -1;}cvtColor(src, gray_src, CV_BGR2GRAY);blur(gray_src, gray_src, Size(3, 3), Point(-1, -1));const char* source_win = "input image";namedWindow(source_win, CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);imshow(source_win, src);createTrackbar("Threshold Value:", output_win, &threshold_v, threshold_max, Contours_Callback);Contours_Callback(0, 0);waitKey(0);return 0;
}void Contours_Callback(int, void*) {Mat binary_output;vector<vector<Point>> contours;vector<Vec4i> hierachy;threshold(gray_src, binary_output, threshold_v, threshold_max, THRESH_BINARY);//imshow("binary image", binary_output);findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));vector<vector<Point>> contours_ploy(contours.size());vector<Rect> ploy_rects(contours.size());vector<Point2f> ccs(contours.size());vector<float> radius(contours.size());vector<RotatedRect> minRects(contours.size());vector<RotatedRect> myellipse(contours.size());for (size_t i = 0; i < contours.size(); i++) {approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true);ploy_rects[i] = boundingRect(contours_ploy[i]);minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]);if (contours_ploy[i].size() > 5) {myellipse[i] = fitEllipse(contours_ploy[i]);minRects[i] = minAreaRect(contours_ploy[i]);}}// draw itdrawImg = Mat::zeros(src.size(), src.type());Point2f pts[4];for (size_t t = 0; t < contours.size(); t++) {Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//rectangle(drawImg, ploy_rects[t], color, 2, 8);//circle(drawImg, ccs[t], radius[t], color, 2, 8);if (contours_ploy[t].size() > 5) {ellipse(drawImg, myellipse[t], color, 1, 8);minRects[t].points(pts);for (int r = 0; r < 4; r++) {line(drawImg, pts[r], pts[(r + 1) % 4], color, 1, 8);}}}imshow(output_win, drawImg);return;
}
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;
Mat src, gray_src, drawImg;
int threshold_v = 170;
int threshold_max = 255;
const char* output_win = "rectangle-demo";
RNG rng(12345);
void Contours_Callback(int, void*);
int main(int argc, char** argv) {src = imread("D:/photos/45.png");if (!src.data) {printf("could not load image...\n");return -1;}cvtColor(src, gray_src, CV_BGR2GRAY);blur(gray_src, gray_src, Size(3, 3), Point(-1, -1));const char* source_win = "input image";namedWindow(source_win, CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);imshow(source_win, src);createTrackbar("Threshold Value:", output_win, &threshold_v, threshold_max, Contours_Callback);Contours_Callback(0, 0);waitKey(0);return 0;
}void Contours_Callback(int, void*) {Mat binary_output;vector<vector<Point>> contours;vector<Vec4i> hierachy;threshold(gray_src, binary_output, threshold_v, threshold_max, THRESH_BINARY);//imshow("binary image", binary_output);findContours(binary_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(-1, -1));vector<vector<Point>> contours_ploy(contours.size());vector<Rect> ploy_rects(contours.size());vector<Point2f> ccs(contours.size());vector<float> radius(contours.size());vector<RotatedRect> minRects(contours.size());vector<RotatedRect> myellipse(contours.size());for (size_t i = 0; i < contours.size(); i++) {approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true);ploy_rects[i] = boundingRect(contours_ploy[i]);minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]);if (contours_ploy[i].size() > 5) {myellipse[i] = fitEllipse(contours_ploy[i]);minRects[i] = minAreaRect(contours_ploy[i]);}}// draw itdrawImg = Mat::zeros(src.size(), src.type());Point2f pts[4];for (size_t t = 0; t < contours.size(); t++) {Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//rectangle(drawImg, ploy_rects[t], color, 2, 8);//circle(drawImg, ccs[t], radius[t], color, 2, 8);if (contours_ploy[t].size() > 5) {ellipse(drawImg, myellipse[t], color, 1, 8);minRects[t].points(pts);for (int r = 0; r < 4; r++) {line(drawImg, pts[r], pts[(r + 1) % 4], color, 1, 8);}}}imshow(output_win, drawImg);return;
}
運行效果:
四.圖像矩(Image Moments)
1、相關理論
2、API介紹
1.計算矩cv::moments
moments(
InputArray array,//輸入數(shù)據(jù)
bool binaryImage=false // 是否為二值圖像
)
API介紹與使用 – cv::moments 計算生成數(shù)據(jù)
計算輪廓面積cv::contourArea
contourArea(
InputArray contour,//輸入輪廓數(shù)據(jù)
bool oriented// 默認false、返回絕對值)
}
.計算輪廓長度cv::arcLength
arcLength(
InputArray curve,//輸入曲線數(shù)據(jù)
bool closed// 是否是封閉曲線)
}
實現(xiàn)步驟:
提取圖像邊緣。
發(fā)現(xiàn)輪廓。
計算每個輪廓對象的矩。
計算每個對象的中心、弧長、面積
例程
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;Mat src, gray_src;
int threshold_value = 80;
int threshold_max = 255;
const char* output_win = "image moents demo";
RNG rng(12345);
void Demo_Moments(int, void*);
int main(int argc, char** argv) {src = imread("D:/photos/45.png");if (!src.data) {printf("could not load image...\n");return -1;}cvtColor(src, gray_src, CV_BGR2GRAY);GaussianBlur(gray_src, gray_src, Size(3, 3), 0, 0);char input_win[] = "input image";namedWindow(input_win, CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);imshow(input_win, src);createTrackbar("Threshold Value : ", output_win, &threshold_value, threshold_max, Demo_Moments);Demo_Moments(0, 0);waitKey(0);return 0;
}void Demo_Moments(int, void*) {Mat canny_output;vector<vector<Point>> contours;vector<Vec4i> hierachy;Canny(gray_src, canny_output, threshold_value, threshold_value * 2, 3, false);findContours(canny_output, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));vector<Moments> contours_moments(contours.size());vector<Point2f> ccs(contours.size());for (size_t i = 0; i < contours.size(); i++) {contours_moments[i] = moments(contours[i]);ccs[i] = Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00), static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));}Mat drawImg;// = Mat::zeros(src.size(), CV_8UC3);src.copyTo(drawImg);for (size_t i = 0; i < contours.size(); i++) {if (contours[i].size() < 100) {continue;}Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));printf("center point x : %.2f y : %.2f\n", ccs[i].x, ccs[i].y);printf("contours %d area : %.2f arc length : %.2f\n", i, contourArea(contours[i]), arcLength(contours[i], true));drawContours(drawImg, contours, i, color, 2, 8, hierachy, 0, Point(0, 0));circle(drawImg, ccs[i], 2, color,2, 8);}imshow(output_win, drawImg);return;
}
五、多邊形測試
1.相關理論
點多邊形測試 : 測試一個點是否在給定的多邊形內(nèi)部,邊緣或者外部。
2.相關API介紹
cv::pointPolygonTest
pointPolygonTest(
InputArray contour,// 輸入的輪廓
Point2f pt, // 測試點
bool measureDist // 是否返回距離值,如果是false,1表示在內(nèi)面,0表示在邊界上,-1表示在外部,true返回實際距離
)
返回數(shù)據(jù)是double類型
程序示例
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>using namespace std;
using namespace cv;
int main(int argc, char** argv) {const int r = 100;Mat src = Mat::zeros(r * 4, r * 4, CV_8UC1);vector<Point2f> vert(6);vert[0] = Point(3 * r / 2, static_cast<int>(1.34*r)); vert[1] = Point(1 * r, 2 * r);vert[2] = Point(3 * r / 2, static_cast<int>(2.866*r)); vert[3] = Point(5 * r / 2, static_cast<int>(2.866*r));vert[4] = Point(3 * r, 2 * r); vert[5] = Point(5 * r / 2, static_cast<int>(1.34*r));for (int i = 0; i < 6; i++) {line(src, vert[i], vert[(i + 1) % 6], Scalar(255), 3, 8, 0);}vector<vector<Point>> contours;vector<Vec4i> hierachy;Mat csrc;src.copyTo(csrc);findContours(csrc, contours, hierachy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));Mat raw_dist = Mat::zeros(csrc.size(), CV_32FC1);for (int row = 0; row < raw_dist.rows; row++) {for (int col = 0; col < raw_dist.cols; col++) {double dist = pointPolygonTest(contours[0], Point2f(static_cast<float>(col), static_cast<float>(row)), true);raw_dist.at<float>(row, col) = static_cast<float>(dist);}}double minValue, maxValue;minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, Mat());Mat drawImg = Mat::zeros(src.size(), CV_8UC3);for (int row = 0; row < drawImg.rows; row++) {for (int col = 0; col < drawImg.cols; col++) {float dist = raw_dist.at<float>(row, col);if (dist > 0) {drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255);}else if (dist < 0) {drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255);} else {drawImg.at<Vec3b>(row, col)[0] = (uchar)(abs(255 - dist));drawImg.at<Vec3b>(row, col)[1] = (uchar)(abs(255 - dist));drawImg.at<Vec3b>(row, col)[2] = (uchar)(abs(255 - dist));}}}const char* output_win = "point polygon test demo";char input_win[] = "input image";namedWindow(input_win, CV_WINDOW_AUTOSIZE);namedWindow(output_win, CV_WINDOW_AUTOSIZE);imshow(input_win, src);imshow(output_win, drawImg);waitKey(0);return 0;
}