OpenCV 楕円抽出
画像から直線とか円をハフ変換で検出できたから、もしかしたら楕円もあるか?
と思ったが、さすがにそれは無かった。当たり前だろう膨大な計算量だ。
ただ輪郭から楕円を求める関数が画像処理ライブラリOpenCVに存在したので
それを試してみたいと思います。
楕円を描く式はあるから、回帰計算で求められるけれど、いやーあるものは
使わねば。それをやっている手間で別のことが出来るし。
作って頂いた方、ありがとうございます。
中心位置と番号も表示するように作りました。
ただ、三角形はうまく動作しませんでしたので、三角形を
少し変形させました。

二値画像で黒の部分は見にくいので、グレイにしています。
三角形がうまくいかない問題について、何らかの入力値の問題だと思うのだけれど、
そこまで調べきれませんでした。
まあ、それ以外はうまく動作し、楕円フィッティングしています。
最小二乗法で計算しているのでしょうね。実際に計算するのは大変。
楕円の縦と横の長さが同じか近ければ、円として判断しても良いのかなと
思っています。
#画像処理
#近似
と思ったが、さすがにそれは無かった。当たり前だろう膨大な計算量だ。
ただ輪郭から楕円を求める関数が画像処理ライブラリOpenCVに存在したので
それを試してみたいと思います。
楕円を描く式はあるから、回帰計算で求められるけれど、いやーあるものは
使わねば。それをやっている手間で別のことが出来るし。
作って頂いた方、ありがとうございます。
楕円のフィッティング
関数cv2.fitEllipse()使って輪郭から楕円フィッティングを行います。
関数
retval = cv.fitEllipse(points)
2Dポイントのセットの周りに楕円をフィットします。
この関数は、(最小二乗の意味で)2Dポイントのセットに
最もよく適合する楕円を計算します。
楕円が内接する回転した長方形を返します。
返却値 ellipse / rotatedRectデータに負のインデックスが
含まれている可能性があります。
アルゴリズム
Andrew W Fitzgibbon and Robert B Fisher. A buyer's guide to conic fitting.
In Proceedings of the 6th British conference on Machine vision (Vol. 2),
pages 513–522. BMVA Press, 1995.
参考サンプルコード
関数
retval = cv.fitEllipse(points)
- points - 2Dポイントセットを入力します
- retval - 返却値 ellipse / rotatedRectデータ
返却値データは下記のタプル形式をとります。((cx, cy), (h, w), deg) - cx - 中心X
- cy - 中心Y
- h - 楕円縦方向の長さ
- w - 楕円横方向の長さ
- deg - 傾き角度
この関数を実行する前処理として輪郭を求める必要があります。
ちなみに外接矩形の角4点を求める場合、cv2.boxPoints()を使います。 - cx - 中心X
2Dポイントのセットの周りに楕円をフィットします。
この関数は、(最小二乗の意味で)2Dポイントのセットに
最もよく適合する楕円を計算します。
楕円が内接する回転した長方形を返します。
返却値 ellipse / rotatedRectデータに負のインデックスが
含まれている可能性があります。
アルゴリズム
Andrew W Fitzgibbon and Robert B Fisher. A buyer's guide to conic fitting.
In Proceedings of the 6th British conference on Machine vision (Vol. 2),
pages 513–522. BMVA Press, 1995.
参考サンプルコード
contours,hierarchy = cv2.findContours(binimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for i, cnt in enumerate(contours):
ellipse = cv2.fitEllipse(cnt)
resimg = cv2.ellipse(resimg,ellipse,(255,0,0),2)
#外接矩形4点を求める
box = cv2.boxPoints(ellipse)
box = np.int0(box)
im = cv2.drawContours(im,[box],0,(0,0,255),2)
C/C++の場合のfitEllipse()返却値について
C/C++コードではリストではなくRotatedRectクラスインスタンスが返却されます。
ただ、Python版OpenCVにはRotatedRectがありません。
C/C++コードで外接矩形4点を求める場合、RotatedRect::points()メソッドを使います。
ただ、Python版OpenCVにはRotatedRectがありません。
C/C++コードで外接矩形4点を求める場合、RotatedRect::points()メソッドを使います。
入力画像
二値化について
入力画像の左上三角形がシアンで、通常のグレイスケール化しても
うまく三角形が二値化できなかった。
あんまりこんなことは書かないのですが、まあ必要な方もいると思いますので
書いておきます。
うまく三角形が二値化できなかった。
あんまりこんなことは書かないのですが、まあ必要な方もいると思いますので
書いておきます。
- 一般的なグレイスケール・二値化
三角形が二値化できなかった例をしめします。
一般的なグレイスケール化サンプルgray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
_, binimg = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
二値化結果
手動でしきい値を設定するのも有なんですけれど、それもなんかやだな。 - カスタムなグレイスケール・二値化
RGB3プレーンをすべてAND合成してグレイスケールを作ります。
カスタムなグレイスケール化gray1 = cv2.bitwise_and(img[:,:,0], img[:,:,1])
gray1 = cv2.bitwise_and(gray1, img[:,:,2])
_, binimg = cv2.threshold(gray1 , 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
二値化結果

出来ていますね。
Pythonプログラム
import cv2
import math
import numpy as np
def main():
img = cv2.imread('ellipse2.png', cv2.IMREAD_COLOR)
# グレイスケール化
gray1 = cv2.bitwise_and(img[:,:,0], img[:,:,1])
gray1 = cv2.bitwise_and(gray1, img[:,:,2])
# 二値化
_, binimg = cv2.threshold(gray1, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
binimg = cv2.bitwise_not(binimg)
# 結果画像の黒の部分を灰色にする。
bimg = binimg // 4 + 255 * 3 //4
resimg = cv2.merge((bimg,bimg,bimg))
# 輪郭取得
contours,hierarchy = cv2.findContours(binimg,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for i, cnt in enumerate(contours):
# 楕円フィッティング
ellipse = cv2.fitEllipse(cnt)
print(ellipse)
cx = int(ellipse[0][0])
cy = int(ellipse[0][1])
# 楕円描画
resimg = cv2.ellipse(resimg,ellipse,(255,0,0),2)
cv2.drawMarker(resimg, (cx,cy), (0,0,255), markerType=cv2.MARKER_CROSS, markerSize=10, thickness=1)
cv2.putText(resimg, str(i+1), (cx+3,cy+3), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,80,255), 1,cv2.LINE_AA)
cv2.imshow('resimg',resimg)
cv2.waitKey()
if __name__ == '__main__':
main()
中心位置と番号も表示するように作りました。
ただ、三角形はうまく動作しませんでしたので、三角形を
少し変形させました。
動作環境
結果画像

二値画像で黒の部分は見にくいので、グレイにしています。
三角形がうまくいかない問題について、何らかの入力値の問題だと思うのだけれど、
そこまで調べきれませんでした。
まあ、それ以外はうまく動作し、楕円フィッティングしています。
最小二乗法で計算しているのでしょうね。実際に計算するのは大変。
楕円の縦と横の長さが同じか近ければ、円として判断しても良いのかなと
思っています。
修正・加筆
- 2020/3/13
OpenCV4でfindContours()の返却値が変わりましたので修正しました。contours, hierarchy = cv.findContours(...)
ソースコードも修正しておきます。
また、動作環境も更新しました。 - 2020/10/27
cv2.fitEllipse()の返却値とC++の場合について、少し加筆しました。 - 2021/3/14
cv2.fitEllipse()の関数の説明を加筆。 - 2021/3/23
cv2.fitEllipse()の関数の説明をさらに加筆。 - 2021/05/02 修正
IntersectionObserver's による画像のオフスクリーン遅延読み込み処理に変更。
IE11未対応。
関連
弊ブログ OpenCV関連記事一覧 2021春版
Emotion Explorer - OpenCV fitLineで直線近似
Emotion Explorer - OpenCV 円検出の考察
Emotion Explorer - OpenCV 楕円抽出(2)
Emotion Explorer - OpenCV fitLineで直線近似
Emotion Explorer - OpenCV 円検出の考察
Emotion Explorer - OpenCV 楕円抽出(2)
参考
Emotion Explorer - ハフ変換で円の検出
Emotion Explorer - 直線の検出
Emotion Explorer - 楕円のプロット
Emotion Explorer - OpenCVでトレンドエッジのようなもの
Emotion Explorer - OpenCVでトレンドエッジのようなもの(2)
Emotion Explorer - 地図画像を調べる
opencv 2.2 documentation - 特徴検出
OpenCV-Python Tutorials 1 documentation - 領域(輪郭)の特徴
docs.opencv.org 4.0.1 - findContours
docs.opencv.org - Structural Analysis and Shape Descriptors - fitEllipse
Emotion Explorer - 直線の検出
Emotion Explorer - 楕円のプロット
Emotion Explorer - OpenCVでトレンドエッジのようなもの
Emotion Explorer - OpenCVでトレンドエッジのようなもの(2)
Emotion Explorer - 地図画像を調べる
opencv 2.2 documentation - 特徴検出
OpenCV-Python Tutorials 1 documentation - 領域(輪郭)の特徴
docs.opencv.org 4.0.1 - findContours
docs.opencv.org - Structural Analysis and Shape Descriptors - fitEllipse
#画像処理
#近似
- 関連記事
スポンサーサイト

コメント