web-dev-qa-db-ja.com

オープンCVトリビアルサークル検出-輪郭の代わりに最小二乗を取得する方法?

私の目標は、顕微鏡から穴の直径を正確に測定することです。ワークフローは次のとおりです。画像を取得し、フィッティングのプロセス、フィット、半径をピクセル単位でmmに変換し、csvに書き込みます。

skewed hough circle

これは、穴の直径を測定するために使用した私の画像処理スクリプトの出力です。私の円のフィッティングが最小二乗法のようなものではなく、輪郭の一致を優先しているように見える問題があります。

あるいは、次のようなもので多くの適合を平均化しました:

many fits to avg

ここでの問題は、円を適切に合わせるためにすばやくスキャンすることです。トレードオフは、フィットが多いほど、フィットが現実的であり、数が少ないほど、数が正しいことを確認しやすくなります。私のサークルはいつもこれほどきれいで円形ではないので、それは私にとって重要です。

これが私のスクリプトフィッティングサークルの一部であり、これを見て、最小2乗法を5つのサークルのオーダーでより多く実行する方法を教えていただければと思います。流体がこの穴を通って流れているので、最小円検出を使用したくないので、水力直径のようにしたいのですが、ありがとう!

(thresh, blackAndWhiteImage0) = cv2.threshold(img0, 100, 255, cv2.THRESH_BINARY) #make black + white 
median0 = cv2.medianBlur(blackAndWhiteImage0, 151) #get rid of noise 
circles0 = cv2.HoughCircles(median0,cv2.HOUGH_GRADIENT,1,minDist=5,param1= 25, param2=10, minRadius=min_radius_small,maxRadius=max_radius_small) #fit circles to image
6
user11886521

私が持っている提案の1つは、 cv2.fitEllipse() を見ることです。

OpenCV fitEllipse example on ballons

OpenCV fitEllipse result

うまくいけば、楕円の幅/高さのアスペクト比を使用して、奇妙なものを区別できます。

1
George Profenza

アプローチは ガウスぼかし 次に 大津のしきい値 バイナリイメージを取得するためのイメージです。ここから、 楕円形のカーネル形態学的開始 を実行します。このステップは、小さなノイズ粒子を効果的に除去します。円のニース推定を取得するには、等高線を見つけて cv2.minEnclosingCircle() を使用します。これにより、中心点と半径も得られます。ここに視覚化があります:

入力画像(スクリーンショット)

バイナリイメージ

モーフオープン

結果->半径付きの結果

Radius: 122.11396026611328

ここから、キャリブレーションスケールに基づいて、半径をピクセル単位でmmに変換できます

コード

import cv2
import numpy as np

# Load image, convert to grayscale, Gaussian blur, then Otsu's threshold
image = cv2.imread('1.png')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]

# Morph open with a elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=2)

# Find contours and draw minimum enclosing circle
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    ((x, y), r) = cv2.minEnclosingCircle(c)
    cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
    print('Radius: {}'.format(r))

cv2.imshow('thresh', thresh)
cv2.imshow('opening', opening)
cv2.imshow('image', image)
cv2.waitKey()
1
nathancy