このシリーズの 14 回目の記事では、等高線の幾何学的特徴でもある面積、周長、重心、凸包など、等高線の多くの基本的な特徴を紹介します。この記事では主に輪郭形状フィッティングについて紹介します。
輪郭形状のフィッティングとは、数学的モデルを通じて輪郭の形状を近似することを指します。輪郭形状フィッティングは、輪郭の表現を簡素化し、輪郭の幾何学的特徴を抽出するのに役立つため、次のように機能します。
単純化された輪郭: 単純な幾何学的形状を使用して複雑な輪郭を近似できるため、輪郭分析が簡素化されます。
形状特徴の抽出: 輪郭形状フィッティングを使用して、輪郭のアスペクト比、面積、周囲長などの形状特徴を抽出できます。
オブジェクト認識: 輪郭形状フィッティングを使用して、特定の形状を持つオブジェクトを識別できます。
Part11. 最小外接長方形
このシリーズの第 14 回では、輪郭の外接長方形と最小外接長方形が紹介されました。
境界四角形boundingRect()
最小の囲み長方形 minAreaRect()
特徴 | 外接長方形 | 最小の囲む長方形 |
---|---|---|
意味 | 形状のすべての点を囲むことができる最小の長方形 | 図形のすべての点を囲むことができ、アスペクト比が正の最小の長方形 |
回転させる | 回転を考慮しない | 回転を考慮する |
エリア | おそらく最小ではない | 最小でなければなりません |
使用 | グラフィックのサイズと位置を記述するために使用されます | グラフィックのサイズ、位置、形状を記述するために使用されます。 |
Part22. 最小外接円
輪郭の最小外接円は、輪郭のすべての点を囲む最小の円です。これは、円の中心が輪郭の重心に位置するように、輪郭の形状とサイズを記述するために使用できます。
OpenCV のminEnクロージングサークル() 関数を使用して、輪郭の最小外接円を計算します。
次の例では、図中の最大の輪郭の最小外接長方形と最小外接円を取得します。これらはそれぞれ青と赤で表されます。
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
bool ascendSort(vector<Point> a,vector<Point> b)
{
return contourArea(a) > contourArea(b);
}
int main(int argc, char **argv) {
Mat src = imread(".../fruit.jpg");
imshow("src", src);
Mat gray,thresh;
cvtColor(src, gray, cv::COLOR_BGR2GRAY);
threshold(gray,thresh,0,255,THRESH_BINARY | THRESH_OTSU);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
sort(contours.begin(), contours.end(), ascendSort);//ascending sort
RotatedRect rrt = minAreaRect(contours[0]);// 获取最大轮廓的最小外接矩形
Point2f pt[4];
rrt.points(pt);
line(src, pt[0], pt[1], Scalar(255, 0, 0), 8, 8);
line(src, pt[1], pt[2], Scalar(255, 0, 0), 8, 8);
line(src, pt[2], pt[3], Scalar(255, 0, 0), 8, 8);
line(src, pt[3], pt[0], Scalar(255, 0, 0), 8, 8);
Point2f center;
float radius;
minEnclosingCircle(contours[0],center,radius); // 获取最大轮廓的最小外接圆
circle(src, center, radius, Scalar(0, 0, 255),8,8);
circle(src, center, 8, Scalar(255, 0, 255),8,8);
imshow("result", src);
waitKey(0);
return 0;
}
Part33. 最大内接円
等高線の最大内接円とは、等高線内の最大の円を指します。OpenCV には輪郭の最大内接円を計算する既製の関数はありませんが、pointPolygonTest() 関数が提供されています。
double pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
この関数の機能は、ピクセル点から輪郭までの最小距離を計算することです。この関数のパラメーターを簡単に説明しましょう。
3 番目のパラメータmeasureDist: 計算された距離に方向記号があるかどうか。
true は、出力結果に方向性があることを示します。ピクセルが輪郭の内側にある場合、戻り値は正の値になり、ピクセルが輪郭の外側にある場合、戻り値は負の値になります。
false は、出力結果に方向性がないことを意味します。ピクセルと輪郭の位置関係のみを判断し、ピクセルが輪郭の内側にある場合は戻り値 -1、ピクセルが輪郭のエッジにある場合は戻り値 0、ピクセルが輪郭の外側にある場合は戻り値を返します。輪郭の場合、戻り値は -1 です。
pointPolygonTest() 関数は、輪郭の内側の点がスキャンされ、内部の点から輪郭までの距離が最大の点が見つかる限り、点から輪郭までの最短ピクセル距離を返すため、この距離が半径になります。最大の内接円の点であり、この点が円の中心になります。
以下の例では、五芒星を描画し、その最小の外接円と最大の内接円を描画しています。
#include "opencv2/imgproc.hpp"
#include <opencv2/opencv.hpp>
#include <cmath>
using namespace std;
using namespace cv;
int main(int argc, char **argv) {
Mat image(1600, 1600, CV_8UC3, cv::Scalar(0, 0, 0));
int centerX = image.cols / 2;
int centerY = image.rows / 2;
int radius = 600;
vector<Point> vertices;
for (int i = 0; i < 5; i++) {
double angle = i * 2 * M_PI / 5 + M_PI / 2;
int x = centerX + radius * cos(angle);
int y = centerY - radius * sin(angle);
vertices.push_back(Point(x, y));
}
for (int i = 0; i < 5; i++) {
line(image, vertices[i], vertices[(i + 2) % 5], cv::Scalar(255, 255, 255), 2);
}
imshow("src", image);
Mat gray;
cvtColor(image,gray,cv::COLOR_BGR2GRAY);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(gray, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
drawContours(image,contours,0,Scalar(255, 0, 255),2);// 绘制五角星的轮廓
imshow("contours", image);
Point2f center;
float r;
minEnclosingCircle(contours[0],center,r);
circle(image, center, radius, Scalar(0, 0, 255),8,8); // 绘制最小外接圆
circle(image, center, 2, Scalar(255, 0, 255),8,8);
imshow("minEnclosingCircle", image);
Mat raw_dist(image.size(), CV_32F);
for (int i = 0; i < image.rows; i++)
{
for (int j = 0; j < image.cols; j++)
{
raw_dist.at<float>(i, j) = (float)pointPolygonTest(contours[0], Point2f((float)j, (float)i), true);
}
}
// 获取最大内接圆半径
double minVal, maxVal;
Point maxDistPt; // inscribed circle center
minMaxLoc(raw_dist, &minVal, &maxVal, NULL, &maxDistPt);
minVal = abs(minVal);
maxVal = abs(maxVal);
circle(image, maxDistPt, maxVal, Scalar(0, 255, 255),8,8);
imshow("result", image);
waitKey(0);
return 0;
}
Part44. 輪郭近似
輪郭近似とは、より少ない点で輪郭の曲線を近似する方法を指します。これを使用して、等高線の表現を簡素化し、計算効率を向上させ、またはノイズの影響を軽減することができます。
輪郭近似に一般的に使用される方法:
Douglas-Peucker アルゴリズム: このアルゴリズムは、輪郭内の最も近い 2 つの点を 1 つずつ削除する貪欲アルゴリズムに基づいています。
Ramer-Douglas-Peucker アルゴリズム: このアルゴリズムは Douglas-Peucker アルゴリズムをベースにした改良版で、指定されたしきい値に基づいて輪郭近似の度合いを制御できます。
ベジェ曲線近似: このアルゴリズムは、より滑らかな輪郭を生成できるベジェ曲線近似法に基づいています。
applyPolyDP() 関数は、輪郭近似を実現するためにOpenCV で一般的に使用されます。この関数は Douglas-Peucker アルゴリズムを使用します。
void approxPolyDP( InputArray curve,
OutputArray approxCurve,
double epsilon, bool closed );
最初のパラメーターのカーブ: 入力カーブを表します。入力カーブは、Point 配列または Mat 行列にすることができます。
2 番目のパラメーターestimateCurve: は、出力ポリゴンを表します。これは、Point 配列または Mat 行列にすることができます。
3 番目のパラメーター epsilon: は、近似の精度と、元の曲線と近似曲線の間の最大距離を示します。値が小さいほど精度が高くなります。
4 番目のパラメーター close: 近似曲線が閉曲線であることを示します。
Douglas-Peucker アルゴリズムは、曲線の最初と最後の点から開始し、曲線に最も近い点を継続的に見つけて、その点を近似曲線に追加します。
アルゴリズムの手順は次のとおりです。
初期化: 曲線の最初と最後の点 A と B を設定します。
直線 AB からの最大距離にある曲線上の点 C を計算します。
C から AB までの距離を、指定されたしきい値イプシロンと比較します。
a. 距離がイプシロンより小さい場合、直線 AB が曲線の近似値として使用され、アルゴリズムは終了します。
b. 距離がイプシロンより大きい場合は、C を近似曲線に追加し、直線 AB を 2 つの線分 AB と AC に分割します。
近似曲線までのすべての点の距離がイプシロン未満になるまで、手順 2 と 3 を繰り返します。
以下の例では、それぞれ正方形、三角形、五角形、円を描いており、輪郭検索で輪郭を見つけた後、estimatePolyDP()関数を使って輪郭を多角形に近似しています。
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"
using namespace std;
using namespace cv;
bool ascendSort(vector<Point> a,vector<Point> b)
{
return contourArea(a) > contourArea(b);
}
int main(int argc, char **argv) {
Mat image(1600, 1600, CV_8UC3, Scalar(0, 0, 0));
// 画一个正方形
rectangle(image, Point(100, 100), Point(400, 400), Scalar(255, 255, 0), -1);
// 画一个三角形
Point trianglePoints[3] = {Point(500, 800), Point(250, 1300), Point(800, 1100)};
fillConvexPoly(image, trianglePoints, 3, Scalar(255, 255, 0));
// 画一个五边形
Point pentagonPoints[5];
for (int i = 0; i < 5; i++) {
pentagonPoints[i] = Point(600 + 200 * cos(2 * CV_PI * i / 5), 600 + 200 * sin(2 * CV_PI * i / 5));
}
fillConvexPoly(image, pentagonPoints, 5, Scalar(255, 255, 0));
// 画一个圆形
circle(image, Point(1200, 1200), 300, Scalar(255, 255, 0), -1);
Mat gray,thresh;
cvtColor(image, gray, cv::COLOR_BGR2GRAY);
threshold(gray,thresh,0,255,THRESH_BINARY | THRESH_OTSU);
imshow("thresh", thresh);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(thresh, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
sort(contours.begin(), contours.end(), ascendSort);//ascending sort
for (size_t i = 0; i< contours.size(); i++) {
double area = contourArea(contours[i]);
if (area < 1000) {
continue;
}
vector<Point> approx;
double perimeter = arcLength(contours[i], true);
double epsilon = perimeter * 0.01;
approxPolyDP(contours[i], approx, epsilon, true);
int approxSize = approx.size();
cout << "approxSize = " << approxSize << endl;
}
imshow("result", image);
waitKey(0);
return 0;
}
結果:
approxSize = 16
approxSize = 3
approxSize = 5
approxSize = 4
上記のコードは円を16角形に近似していますが、輪郭の長方形度から円かどうかを判断する方法を後ほど紹介します。
Part55.まとめ
輪郭は、画像内のオブジェクトの境界です。輪郭フィッティングは、輪郭を一連の点または多角形として近似することです。これは、オブジェクトの形状を抽出、記述、分析し、輪郭の堅牢性を向上させるのに役立ちます。輪郭の後処理を簡素化します。楕円フィッティングや直線フィッティングなども後ほど紹介します。
[ Java および Android テクノロジー スタック] パブリック アカウント
Java/Kotlin サーバー、デスクトップ、Android、機械学習、クライアント側インテリジェンスに注意を払う
さらにエキサイティングなコンテンツについては、次の点にご注意ください。