【OpenCV入門】形状・輪郭検出


私の個人的なブログ: Mouren·Blog
WeChat パブリック アカウント: Mouren の袋
CSDN: Cao Mouren



検出前の前処理 - エッジ検出バイナリ イメージ

いわゆる形状・輪郭検出とは、検出対象画像中のエッジ輪郭からなる図形を認識し、輪郭点を検出して判断するものである。したがって、輪郭の検出を容易にするために、検出前にいくつかの前処理を実行する必要があります:
グレースケール -> ガウス フィルター -> キャニー エッジ検出アルゴリズム -> 画像拡張
コード:

/// <summary>
/// 形状检测前的预处理(灰度->高斯滤波->Canny边缘算法->膨胀)
/// </summary>
/// <param name="imgIn">Mat 类,输入图像</param>
/// <returns>Mat类,预处理后的图像</returns>
Mat preProcessing(Mat imgIn) {
    
    
	Mat imgGray, imgBlur, imgCanny, imgDila;
	//先定义一个内核
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	cvtColor(imgIn, imgGray, COLOR_BGR2GRAY, 0);//灰度变换
	GaussianBlur(imgGray, imgBlur, Size(65, 65), 1, 1);//高斯滤波去噪点
	Canny(imgBlur, imgCanny, 40, 120);//Canny边缘检测
	dilate(imgCanny, imgDila, kernel);//图像膨胀
	return imgDila;//返回处理完后的图像
}

findContours 関数 ---- 検出輪郭

関数の役割: バイナリ イメージから輪郭を検出する
関数の定義:

void findContours( 
	InputArray image, 
	OutputArrayOfArrays contours,
    OutputArray hierarchy, 
    int mode,
    int method, 
    Point offset = Point()
    );

パラメータの説明:

  • image: 8 ビットの単一チャネル画像。ゼロ以外のピクセルは 1 と見なされ、ゼロ ピクセルは 0 と見なされます。ここで、一般的な入力は、Canny エッジ検出、inRange 選択、Laplace などによって 01 イメージに変換されます。
  • 輪郭: 検出された輪郭の変数。各輪郭はポイント ベクトルとして格納されます。プロファイルのタイプは次のように定義されます。
vector<vector<Point>>

これは double ベクトルです. 外側のベクトルには、検出される画像内のすべての形状の輪郭が格納され、内側のベクトルには、特定の輪郭の点の集合 (連続する Points で構成される点の集合のベクトル) が格納されます。

  • 階層: パラメータ定義のタイプは次のとおりです。
vector<Vec4i> hierarchy

Vec4i の定義:

typedef Vec<int, 4> Vec4i

ベクトルの各要素には 4 つの int 変数が含まれているため、定義により、階層はベクトルであり、ベクトルの各要素は 4 つの int を含む配列です。
ベクトル階層内の要素と輪郭ベクトル輪郭内の要素の間には 1 対 1 の対応があり、ベクトルの容量は同じです。階層内の各要素の 4 つの int 変数は、hierarchy[i][0] ~ hierarchy[i][3] であり、それぞれ次の輪郭、前の輪郭、親の輪郭、および現在の埋め込み輪郭の番号を表します。輪郭 i インデックス。現在の輪郭が 4 つのいずれにも対応しない場合、対応する階層 [i][*] は -1 に設定されます。

  • mode: 輪郭の検出モード。その値は次のように定義されます。
enum RetrievalModes {
    
    
    /** retrieves only the extreme outer contours. It sets `hierarchy[i][2]=hierarchy[i][3]=-1` for
    all the contours. */
    RETR_EXTERNAL  = 0,
    /** retrieves all of the contours without establishing any hierarchical relationships. */
    RETR_LIST      = 1,
    /** retrieves all of the contours and organizes them into a two-level hierarchy. At the top
    level, there are external boundaries of the components. At the second level, there are
    boundaries of the holes. If there is another contour inside a hole of a connected component, it
    is still put at the top level. */
    RETR_CCOMP     = 2,
    /** retrieves all of the contours and reconstructs a full hierarchy of nested contours.*/
    RETR_TREE      = 3,
    RETR_FLOODFILL = 4 //!<
};

翻訳は次のとおりです:
RETR_EXTERNAL: 最も外側の輪郭のみを検出し、外側の輪郭に含まれる内側の輪郭は無視されます;
RETR_LIST: 内側と外側の輪郭を含むすべての輪郭を検出しますが、検出された輪郭は階層関係を確立しません。互いに独立しており、階層はありません。これは、この取得モードでは親の輪郭または埋め込まれた輪郭がないことを意味するため、階層ベクトルのすべての要素の 3 番目と 4 番目のコンポーネントは -1 に設定されます; RETR_CCOMP: すべての輪郭を検出しますが、すべての輪郭は確立されるだけです 2 つの
階層関係、外側は最上位レイヤー、外周の内側輪郭に他の輪郭情報が含まれる場合、内周のすべての輪郭は最上位レイヤーに属します; RETR_TREE: すべての輪郭を検出し、すべての輪郭の階層ツリー構造を構築します
外側の輪郭は内側の輪郭を含み、内側の輪郭は引き続き内側の輪郭を含むことができます。
RETR_FLOODFILL: 正式な紹介がないので今のところわかりません

参考:https://www.cnblogs.com/GaloisY/p/11062065.html

  • method: 等高線近似法。値は次のように定義されます。
enum ContourApproximationModes {
    
    
    /** stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and
    (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors, that is,
    max(abs(x1-x2),abs(y2-y1))==1. */
    CHAIN_APPROX_NONE      = 1,
    /** compresses horizontal, vertical, and diagonal segments and leaves only their end points.
    For example, an up-right rectangular contour is encoded with 4 points. */
    CHAIN_APPROX_SIMPLE    = 2,
    /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */
    CHAIN_APPROX_TC89_L1   = 3,
    /** applies one of the flavors of the Teh-Chin chain approximation algorithm @cite TehChin89 */
    CHAIN_APPROX_TC89_KCOS = 4
};

CHAIN_APPROX_NONE
: 各輪郭の各ピクセルを取得し、隣接する 2 点間のピクセル位置の差が 1 を超えない
CHAIN_APPROX_SIMPLE: 水平方向、垂直方向、および斜め方向の要素を圧縮し、値は保持されますこの方向のフォーカス 座標 (長方形の輪郭が輪郭情報を保存するために 4 つの点しか必要としない場合);
CHAIN_APPROX_TC89_L1 および CHAIN_APPROX_TC89_KCOS: Teh-Chinl チェーン近似アルゴリズムのいずれかを使用します。

参考:https://blog.csdn.net/qq_42887760/article/details/86565601

  • オフセット: 元の画像に対する輪郭のオフセット。例: ここで Size(-10,-2​​0) と記述した場合、最終的に描画されるアウトラインは、元のイメージの実際のアウトラインに対して、左に 10 ピクセル、上に 20 ピクセルシフトされます。Size(10,20) の場合は、右に 10 ピクセル、下に 20 ピクセルシフトすることを意味します。

findContours 関数の別のオーバーロードとこの定義の唯一の違いは、hierarchy パラメーターがないことです。他のパラメーターと意味は同じであるため、ここでは繰り返しません。

輪郭面積、弧長関数----面積、周長

等高線エリア機能----等高線エリア

関数の役割: 画像の輪郭の面積を計算する
関数の定義:

double contourArea( InputArray contour, bool oriented = false );

パラメータの説明:

  • 輪郭: 入力輪郭の点セット
  • directional: 等高線の特定の方向 (時計回りまたは反時計回り) の面積値を示します。通常はデフォルトの false を選択します。

arcLength 関数----輪郭の長さ

関数の役割: 画像の輪郭の周長を計算する
関数の定義:

double arcLength( InputArray curve, bool closed );

パラメータの説明:

  • 曲線: 入力輪郭の点セット
  • closed: 曲線が閉じているかどうかを示します

approxPolyDP 関数 ---- 曲線ポリライン

機能: より少ない頂点を割り引いて曲線または多角形を近似する、つまり、連続した滑らかな曲線を曲線に変換する. 曲線変換の目的は、
破線の頂点の数を取得し、次にどのような形状を決定することです.関数 輪郭は
. 定義:

void approxPolyDP( 
	InputArray curve,
    OutputArray approxCurve,
    double epsilon, 
    bool closed 
    );

パラメータの説明:

  • 曲線: Mat クラスまたはベクトル クラス、2D ポイント セット、通常は等高線ポイントで構成されるポイント セット
  • approxCurve: 出力ポリライン ポイント セット
  • epsilon: 元の曲線と近似曲線の間の最大距離である指定された精度
  • closed: true の場合、フィッティング曲線が閉じていることを意味します; そうでない場合、false の場合、閉じた曲線ではありません

drawContours 関数 ---- 輪郭を描く

関数の役割: 見つかった画像の輪郭を描く
関数の定義:

void drawContours( 
	InputOutputArray image, 
	InputArrayOfArrays contours,
    int contourIdx, 
    const Scalar& color,
    int thickness = 1, 
    int lineType = LINE_8,
    InputArray hierarchy = noArray(),
    int maxLevel = INT_MAX, 
    Point offset = Point() 
    );

パラメータの説明:

  • image: 輪郭を描く画像
  • 等高線: 等高線のポイント コレクション
  • ContourIdx: 描画する等高線の番号を指定します。負の数の場合、すべての等高線が描画されます
  • color: 等高線の色。通常は Scalar (青、緑、赤) を使用
  • 太さ: 等高線の幅。-1 の場合 (cv2.FILLED)、塗りつぶしモードです。
  • lineType: 線種、値の定義:
enum LineTypes {
    
    
    FILLED  = -1,
    LINE_4  = 4, //!< 4-connected line--4连通线型
    LINE_8  = 8, //!< 8-connected line--8连通线型
    LINE_AA = 16 //!< antialiased line--抗锯齿线型
};
  • hierarchy: アウトラインの一部を描画するときにのみ使用される、階層に関するオプションのパラメーター
  • maxLevel: 等高線を描画するための最高レベル。このパラメーターは、階層が有効な場合にのみ有効です
    maxLevel=0-----入力等高線と同じレベルに属するすべての等高線を描画します。つまり、入力等高線とそれに隣接するものです。輪郭
    maxLevel=1--- --入力輪郭とその子ノードと同じレベルのすべての輪郭を描画します
    maxLevel=2-----入力輪郭と同じレベルのすべての輪郭、それらの子ノードとその子を描画しますノード
  • オフセット: 見つかった輪郭位置に対する描画された輪郭のオフセット。例: ここに Size(-10,-2​​0) と記述した場合、最終的に描画される輪郭は、見つかった輪郭の位置に対して左に 10 ピクセル、上に 20 ピクセルオフセットされます。Size(10,20) の場合は、右に 10 ピクセル、下に 20 ピクセルシフトすることを意味します。

ソースコード:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
#ifdef _DEBUG
#pragma comment(lib,"opencv_world453d.lib")
#else
#pragma comment(lib,"opencv_world453.lib")
#endif // _DEBUG


/// <summary>
/// 对预处理后的图像检测轮廓并标出已定义的简单形状
/// </summary>
/// <param name="imgDil">与处理后的图像</param>
/// <param name="img">待检测轮廓的原图</param>
void GetContour(Mat imgDil, Mat img)
{
    
    
	vector<vector<Point>> contour;
	vector<Vec4i> hierarchy;
	findContours(imgDil, contour, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_NONE);
	vector<vector<Point>> conPoly(contour.size());//定义用于存放轮廓折线化后的变量
	vector<Rect> boundRect(contour.size());
	//contour.size()是在该图像中检测到的轮廓个数
	for (int i = 0; i < contour.size(); i++)//所有轮廓遍历
	{
    
    
		string objectType;//形状名称
		float peri = arcLength(contour[i], true);//轮廓周长
		//0.015* peri表示拟合的精度
		approxPolyDP(contour[i], conPoly[i], 0.015 * peri, true);//把一个连续光滑曲线折线化
		boundRect[i] = boundingRect(conPoly[i]);//获得包覆该轮廓最小矩形
		int objCor = (int)conPoly[i].size();//获取折线化后轮廓的顶点个数
#pragma region 定义形状
		if (objCor == 3) //三角形
			objectType = "Tri";
		else if (objCor == 4) {
    
    //矩形
			float aspRatio = (float)boundRect[i].width / boundRect[i].height;//长宽比来区别正方形与矩形
			if (aspRatio > 0.95 && aspRatio < 1.05) 
				objectType = "Square";
			else 
				objectType = "Rect";
		}
		else if (objCor > 4) //圆形
			objectType = "Circle";
#pragma endregion
		drawContours(img, conPoly, i, Scalar(187, 109, 68), 3, LINE_AA);//画出轮廓线
		rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(72,255,104), 4);//用矩形框住选出的图形
		//标出已定义出的图形名称
		putText(img, objectType, {
    
     boundRect[i].x,boundRect[i].y - 5 } , 1, FONT_HERSHEY_PLAIN, Scalar(0, 69, 255), 1);
	}
}

/// <summary>
/// 形状检测前的预处理(灰度->高斯滤波->Canny边缘算法->膨胀)
/// </summary>
/// <param name="imgIn">Mat 类,输入图像</param>
/// <returns>Mat类,预处理后的图像</returns>
Mat PreProcessing(Mat imgIn) {
    
    
	Mat imgGray, imgBlur, imgCanny, imgDila;
	//先定义一个内核
	Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
	cvtColor(imgIn, imgGray, COLOR_BGR2GRAY, 0);//灰度变换
	GaussianBlur(imgGray, imgBlur, Size(65, 65), 1, 1);//高斯滤波去噪点
	Canny(imgBlur, imgCanny, 40, 120);//Canny边缘检测
	dilate(imgCanny, imgDila, kernel);//图像膨胀
	return imgDila;//返回处理完后的图像
}

void main() {
    
    
	string path = "D:\\My Bags\\图片\\shapes.png";//原图路径
	Mat img = imread(path);//读取图片
	Mat imgProcessed = PreProcessing(img);//图像预处理
	GetContour(imgProcessed , img);//检测轮廓并标出简单图形名称
	imshow("Image", img);//显示出来
	waitKey(0);
}

操作結果:

おすすめ

転載: blog.csdn.net/ZBC010/article/details/120690367