只用來記錄學習筆記
Douglas-Peucker算法:
- 在曲線首尾兩點間虛連一條直線,求出其餘各點到該直線的距離,如右圖(1)。
- 選其最大者與閾值相比較,若大於閾值,則離該直線距離最大的點保留,否則將直線兩端點間各點全部捨去,如右圖(2),第4點保留。
- 依據所保留的點,將已知曲線分成兩部分處理,重複第1、2步操作,迭代操作,即仍選距離最大者與閾值比較,依次取捨,直到無點可捨去,最後得到滿足給定精度限差的曲線點座標,如圖(3)、(4)依次保留第6點、第7點,捨去其他點,即完成線的化簡。
approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
代碼:(函數的解釋全部寫在註釋中了)
定義:
Mat src, gray_src, temp, dst;
const char* input_title = "input";
const char* output_title = "output";
const char* trackbar_title = "output";
int threshold_value = 170;
int threshold_max = 255;
RNG rng(12345);
void Contours_Callback(int, void*);
主函數:
int main(int argc, char** argv) {
src = imread("C:/Users/Administrator/Pictures/p234.jpg");
if (src.empty()) {
cout << "no image" << endl;
return -1;
}
namedWindow(input_title, CV_WINDOW_AUTOSIZE);
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
imshow(input_title, src);
cvtColor(src, gray_src, CV_BGR2GRAY); //轉灰度圖像
blur(gray_src, gray_src, Size(3, 3), Point(-1, -1)); //模糊
createTrackbar(trackbar_title, output_title, &threshold_value, 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_value, threshold_max, THRESH_BINARY);//閾值操作
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()); //RotatedRect:包含中心點座標,以及矩形的長度和寬度還有矩形的偏轉角度
vector<RotatedRect> myellipse(contours.size()); //RotatedRect:包含中心點座標,以及矩形的長度和寬度還有矩形的偏轉角度
for (size_t i = 0; i < contours.size(); i++) { //循環所有輪廓
approxPolyDP(Mat(contours[i]), contours_ploy[i], 3, true); //多邊擬合函數 ,目的是減少
//Mat(contours[i]):輸入曲線,數據類型可以爲vector<Point>
//contours_ploy[i]:輸出折線,數據類型可以爲vector<Point>
//3:判斷點到相對應的line segment 的距離的閾值。(距離大於此閾值則保留,小於此閾值則捨棄,epsilon越小,折線的形狀越“接近”曲線。)
//true:曲線是否閉合的標誌位
ploy_rects[i]=boundingRect(contours_ploy[i]);//計算輪廓的垂直邊界最小矩形,矩形是與圖像上下邊界平行的
minEnclosingCircle(contours_ploy[i], ccs[i], radius[i]); //尋找最小包圍圓形
//contours_ploy[i]:輸入折線
//ccs[i]:圓心
//radius[i]:半徑
if (contours_ploy[i].size() > 5) { //只有大於等於6條線的才能擬合成橢圓
myellipse[i] = fitEllipse(contours_ploy[i]); //二維點集的橢圓擬合,用橢圓將二維點包含起來
minRects[i] = minAreaRect(contours_ploy[i]);//最小區域邊界斜矩形
}
}
//繪製
src.copyTo(dst);
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(dst, ploy_rects[t], color, 1, 8); //繪製正矩形框
//circle(dst, ccs[t], radius[t], color, 1, 8);//繪製最小包圍圓
ellipse(dst, myellipse[t], color, 1, 8); //繪製橢圓
minRects[t].points(pts);
for (int r = 0; r < 4; r++) {//繪製斜矩形
line(dst, pts[r], pts[(r + 1) % 4], color, 1, 8);
}
}
imshow(output_title, dst);
}
效果圖:(繪製正矩形和最小包圍圓的代碼已經註釋掉了)