私は実際にOpenCVとPythonを使用してマシンビジョンプロジェクトに取り組んでいます。
Objective:プロジェクトの目的は、コンポーネントの寸法を高精度で測定することです。
メインハードウェア:
Basler 5MPカメラ(aca-2500-14gm)
赤いバックライト(100 mm x 100 mm)(コンポーネントのサイズは約60mm)
実験
私は非常に厳しい許容限界を見ているので、最初に精密な研究をしました。私はコンポーネントをバックライトソースに置いたまま、部品を動かさずに100枚の画像を撮りました(100フレームのビデオのように想像してください)。全100画像の外径(OD)を測定しました。私のmm /ピクセル比は0.042です。測定の標準偏差を測定して精度を確認しましたが、精度はおよそ0.03 mmであり、不良です。コンポーネントや設定には触れないため、0.005 mmの精度を期待していました。しかし、私は桁違いです。 OpenCVのハフ円を使用して、コンポーネントのODを計算しています。
コード:
import sys
import pickle
import cv2
import matplotlib.pyplot as plt
import glob
import os
import numpy as np
import pandas as pd
def find_circles(image,dp=1.7,minDist=100,param1=50,param2=50,minRadius=0,maxRadius=0):
""" finds the center of circular objects in image using hough circle transform
Keyword arguments
image -- uint8: numpy ndarray of a single image (no default).
dp -- Inverse ratio of the accumulator resolution to the image resolution (default 1.7).
minDist -- Minimum distance in pixel distance between the centers of the detected circles (default 100).
param1 -- First method-specific parameter (default = 50).
param2 -- Second method-specific parameter (default = 50).
minRadius -- Minimum circle radius in pixel distance (default = 0).
maxRadius -- Maximum circle radius in pixel distance (default = 0).
Output
center -- Tuple: (x,y).
radius -- int : radius.
ERROR if circle is not detected. returns(-1) in this case
"""
circles=cv2.HoughCircles(image,
cv2.HOUGH_GRADIENT,
dp = dp,
minDist = minDist,
param1=param1,
param2=param2,
minRadius=minRadius,
maxRadius=maxRadius)
if circles is not None:
circles = circles.reshape(circles.shape[1],circles.shape[2])
return(circles)
else:
raise ValueError("ERROR!!!!!! circle not detected try tweaking the parameters or the min and max radius")
def find_od(image_path_list):
image_path_list.sort()
print(len(image_path_list))
result_df = pd.DataFrame(columns=["component_name","measured_dia_pixels","center_in_pixels"])
for i,name in enumerate(image_path_list):
img = cv2.imread(name,0) # read the image in grayscale
ret,thresh_img = cv2.threshold(img, 50, 255, cv2.THRESH_BINARY_INV)
thresh_img = cv2.bilateralFilter(thresh_img,5,91,91) #smoothing
edges = cv2.Canny(thresh_img,100,200)
circles = find_circles(edges,dp=1.7,minDist=100,param1=50,param2=30,minRadius=685,maxRadius=700)
circles = np.squeeze(circles)
result_df.loc[i] = os.path.basename(name),circles[2]*2,(circles[0],circles[1])
result_df.sort_values("component_name",inplace=True)
result_df.reset_index(drop=True,inplace=True)
return(result_df)
df = find_od(glob.glob("./images/*"))
mean_d = df.measured_dia_pixels.mean()
std_deviation = np.sqrt(np.mean(np.square([abs(x-mean_d) for x in df.measured_dia_pixels])))
mm_per_pixel = 0.042
print(std_deviation * mm_per_pixel)
出力:0.024
コンポーネントの画像:
画像はセットアップに影響を与えずに取得されているため、測定の再現性は約0.005 mm(5ミクロン)であると予想しています(100画像の場合)。ハフサークルの問題ですか?または私はここで何が欠けていますか
ハフは検出用に設計されており、定量化用に設計されていません。正確な測定が必要な場合は、そのために設計されたライブラリを使用する必要があります。 OpenCVは定量化を目的としたものではないため、その機能は不十分です。
ずっと前に、ラドン変換を使用したサイズのより正確な見積もりについて論文を書きました(ハフ変換はラドン変換を離散化する1つの方法です。場合によっては高速ですが、正確ではありません)。
しかし、セットアップは非常によく制御されているため、正確な測定値を取得するためにそれらすべてが実際に必要になるわけではありません。これらは、これらの穴を定量化する非常に単純なPythonスクリプトです。
import PyDIP as dip
import math
# Load image and set pixel size
img = dip.ImageReadTIFF('/home/cris/tmp/machined_piece.tif')
img.SetPixelSize(dip.PixelSize(0.042 * dip.Units("mm")))
# Extract object
obj = ~dip.Threshold(dip.Gauss(img))[0]
obj = dip.EdgeObjectsRemove(obj)
# Remove noise
obj = dip.Opening(dip.Closing(obj,9),9)
# Measure object area
lab = dip.Label(obj)
msr = dip.MeasurementTool.Measure(lab,img,['Size'])
objectArea = msr[1]['Size'][0]
# Measure holes
obj = dip.EdgeObjectsRemove(~obj)
lab = dip.Label(obj)
msr = dip.MeasurementTool.Measure(lab,img,['Size'])
sz = msr['Size']
holeAreas = []
for ii in sz.Objects():
holeAreas.append(sz[ii][0])
# Add hole areas to main object area
objectArea += sum(holeAreas)
print('Object diameter = %f mm' % (2 * math.sqrt(objectArea / math.pi)))
for a in holeAreas:
print('Hole diameter = %f mm' % (2 * math.sqrt(a / math.pi)))
これは私に出力を与えます:
Object diameter = 57.947768 mm
Hole diameter = 6.540086 mm
Hole diameter = 6.695357 mm
Hole diameter = 15.961935 mm
Hole diameter = 6.511002 mm
Hole diameter = 6.623011 mm
上記のコードには多くの仮定があることに注意してください。カメラがオブジェクトの真上に配置されていないという問題もあります。穴の右側が光を反射しているのがわかります。これは確かにこれらの測定に不正確さを追加します。ただし、オブジェクトを測定するときにオブジェクトが丸いという知識を使用しなかったことにも注意してください(面積を直径に変換するときのみ)。真円度の基準を使用して、イメージングの欠陥の一部を克服することが可能である場合があります。
上記のコードは、PyDIPを使用しています。これは、非常に大まかなPython C++ライブラリへのインターフェース、 DIPlib です。Python構文は直接ですC++構文の変換、およびPython person(実際にはC++で使用する方が簡単だと思います!)には、まだかなり厄介な点があります。)しかし、これは特に数量化を目的としているため、アプリケーションで試してみることをお勧めします。