opencv チュートリアルチャレンジ9 OpenCVにおける輪郭(領域)
輪郭に関するチュートリアル
OpenCVにおける輪郭(領域) — OpenCV-Python Tutorials 1 documentation
輪郭抽出
OpenCVの輪郭検出は,黒い背景から白い物体の輪郭を検出すると仮定しています.物体は白(明るい色),背景は黒(暗い色)と覚えておいてください.
とある。
cv2.findContours
輪郭抽出と、その輪郭に対する処理を人多りやてみる。
void findContours(const Mat& image, vector<vector<Point> >& contours, int mode, int method, Point offset=Point()) 2値画像中の輪郭を検出します. パラメタ: image – 入力画像,8ビット,シングルチャンネル.0以外のピクセルは 1として,0のピクセルは0のまま扱われます.つまり,入力画像は 2値画像 として扱われます.グレースケールやカラー画像から2値画像を得るには, compare() , inRange() , threshold() , adaptiveThreshold() , Canny() などの関数を利用します.また,この関数は,輪郭抽出処理中に入力画像 image の中身を書き換えます. contours – 検出された輪郭.各輪郭は,点のベクトルとして格納されます. hiararchy – オプション.画像のトポロジーに関する情報を含む出力ベクトル.これは,輪郭数と同じ数の要素を持ちます.各輪郭 contours[i] に対して,要素 hierarchy[i][0] , hiearchy[i][1] , hiearchy[i][2] , hiearchy[i][3] にはそれぞれ,同じ階層レベルに存在する前後の輪郭,最初の子輪郭,および親輪郭の contours インデックス(0 基準)がセットされます.また,輪郭 i において,前後,親,子の輪郭が存在しない場合,それに対応する hierarchy[i] の要素は,負の値になります. mode – 輪郭抽出モード CV_RETR_EXTERNAL 最も外側の輪郭のみを抽出します.すべての輪郭に対して hierarchy[i][2]=hierarchy[i][3]=-1 がセットされます. CV_RETR_LIST すべての輪郭を抽出しますが,一切の階層構造を保持しません. CV_RETR_CCOMP すべての輪郭を抽出し,それらを2階層構造として保存します:上のレベルには,連結成分の外側の境界線が,下のレベルには,連結成分の内側に存在する穴の境界線が属します.ある連結成分の穴の内側に別の輪郭が存在する場合,その穴は上のレベルに属します. CV_RETR_TREE すべての輪郭を抽出し,入れ子構造になった輪郭を完全に表現する階層構造を構成します.この完全な階層構造は,OpenCVの contours.c デモで見ることができます. method – 輪郭の近似手法: CV_CHAIN_APPROX_NONE すべての輪郭点を完全に格納します.つまり,この手法により格納された任意の隣り合う2点は,互いに8近傍に存在します. CV_CHAIN_APPROX_SIMPLE 水平・垂直・斜めの線分を圧縮し,それらの端点のみを残します.例えば,まっすぐな矩形の輪郭線は,4つの点にエンコードされます. CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS Teh-Chinチェーン近似アルゴリズムの1つを適用します. TehChin89 を参照してください. offset – オプションのオフセット.各輪郭点はこの値の分だけシフトします.これは,ROIの中で抽出された輪郭を,画像全体に対して位置づけて解析する場合に役立ちます.
通常、グレースケール化して、輪郭を抽出して、見やすいように色を載せると思う。
面倒なので、カラー化したものを変換して乗せてしまう。
元の画像は、これ。
#!/usr/bin/env python # -*- coding: utf-8 -* import sys import cv2 import numpy as np import math img = cv2.imread('170519-174830.jpg') img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) img = cv2.GaussianBlur(img, (11, 11), 0) #cv2.imshow('GaussianBlur', img) #ret, img = cv2.threshold(img, 60, 255, cv2.THRESH_BINARY) #cv2.imshow('threshold1', img) #cv2.imwrite('threshold1.png', img) #ret, img = cv2.threshold(img, 150, 255, cv2.THRESH_BINARY) #cv2.imshow('threshold2', img) #cv2.imwrite('threshold2.png', img) img = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 15, 5) cv2.imshow('adaptiveThreshold', img) #cv2.imwrite('adaptiveThreshold.png', img) #img, contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) img, contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) print("{} contours.".format(len(contours))) # カラー化 img = cv2.cvtColor(img, cv2.COLOR_GRAY2RGB) #img = cv2.drawContours(img, contours, -1, (0,0,255), 1) #cv2.imshow('drawContours', img) #cv2.imwrite('drawContours.png', img) if len(contours) > 0: for i, contour in enumerate(contours): #print("{}, {}".format(i, len(contour))) # area:面積 area = cv2.contourArea(contour) if area < 100: continue # moments:重心 M = cv2.moments(contour) if M["m00"] != 0: cx = int(M["m10"] / M["m00"]) cy = int(M["m01"] / M["m00"]) else: cx, cy = 0, 0 # 重心を中心にした円 cv2.circle(img, (cx, cy), int(math.sqrt(area)), (0,255,0), 1, 8) # 最小外接円 (x, y), radius = cv2.minEnclosingCircle(contour) center = (int(x),int(y)) radius = int(radius) img = cv2.circle(img,center,radius,(255,255,0),2) # 輪郭の近似 epsilon = 0.05*cv2.arcLength(contour,True) approx = cv2.approxPolyDP(contour,epsilon,True) print(len(approx)) if len(approx) == 4: cv2.drawContours(img, [approx], -1, (255, 0, 0), 3) # 凸包 hull = cv2.convexHull(contour) img = cv2.drawContours(img,[hull],0,(255,255,0),2) # 外接矩形 rect = cv2.boundingRect(contour) cv2.rectangle(img, (rect[0], rect[1]), (rect[0]+rect[2], rect[1]+rect[3]), (0,0,255), 1) # 回転を考慮した外接矩形 rect = cv2.minAreaRect(contour) box = cv2.boxPoints(rect) box = np.int0(box) img = cv2.drawContours(img,[box],0,(0,255,255),1) # 楕円のフィッティング ellipse = cv2.fitEllipse(contour) img = cv2.ellipse(img,ellipse,(0,255,0),2) #直線のフィッティング rows,cols = img.shape[:2] [vx,vy,x,y] = cv2.fitLine(contour, cv2.DIST_L2,0,0.01,0.01) lefty = int((-x*vy/vx) + y) righty = int(((cols-x)*vy/vx)+y) img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2) cv2.imshow('findContours', img) cv2.imwrite('findContours.png', img) cv2.waitKey(0) cv2.destroyAllWindows()
こうなった
キーワード
Extent
Solidity
等価直径
傾き
マスクと画素点
最大値,最小値とその位置
平均色と平均値
端点
凸性の欠陥
こんどやる
輪郭の階層情報
いつかやる