I.はじめに
ハフ変換は、画像解析、コンピュータ ビジョン、デジタル画像処理で広く使用されている特徴抽出です。西暦 1972 年にリチャード デューダとピーター ハートによって発明され、一般化ハフ変換 (一般化ハフ変換) と呼ばれる一般化ハフ変換は、以前の 1962 年のポール ハフの特許に関連しています。古典的なハフ変換は画像内の直線を検出するものですが、その後、ハフ変換は直線だけでなく、円や楕円などのあらゆる形状を認識することができます。1981 年、DanaH.Ballard によるジャーナル論文「任意の形状を検出するためのハフ変換の一般化」のおかげで、ハフ変換はコンピュータ ビジョン コミュニティで人気になりました。ハフ変換は、線などのオブジェクト内の特徴を識別するために使用されます。彼のアルゴリズム フローは大まかに次のとおりです。オブジェクトと識別される形状のタイプが与えられると、アルゴリズムはパラメーター空間で投票を実行してオブジェクトの形状を決定します。これはアキュムレーター空間の部分によって決定されます。決定する最大値(極大値)。
2、ハフ変換
直線は 2 つの点で構成できますA = ( x 1 , y 1 ) A=(x_1,y_1)あ=( ×1、y1)和 B = ( x 2 , y 2 ) B=(x_2,y_2) B=( ×2、y2)決定 (デカルト座標)
一方、y = kx + by=kx+by=k ×+b は( k , q ) (k,q)について書くこともできます。(k 、q )関数式 (ハフ空間):
{ q = − kx 1 + y 1 q = − kx 2 + y 2 \left\{ \begin{aligned} q=-kx_1+y_1 \\ q=-kx_2 +y_2 \ \ \end{整列} \右。{
q=− k x1+y1q=− k x2+y2
空間変換の過程は下図のようになります。
変換された空間はハフ空間になります。つまり、デカルト座標系の直線はハフ空間の点に対応します。
逆もまた真です (ハフ空間の直線はデカルト座標系の 1 つの点に対応します)。デカルト座標系の
2 つの点はハフ空間の 2 つの線に対応します。
デカルト座標系の 3 つの点が共線的、対応する ハフ空間の 3 つの直線が 1 点で交差する
ハフ変換の後処理の基本的な方法: できるだけ多くの直線が収束する点を選択します。
しかし、デカルト座標系によれば、画像空間上の点の直線が x 軸に垂直な場合、傾きは無限大となり、ハフ空間では交点が見つからないという状況が生じます。したがって、人々は最終的に極座標の表記法を導入しました。
極座標でのハフ ライン検出の原理は直交座標でのハフ ライン検出の原理とまったく同じです。再導出する必要があるのは、ハフ空間の極座標パラメータ関数 y = ( − cos θ sin θ ) x + だけです。
rsin θ y=\left (-\frac{cos\theta}{sin\theta}\right)x+\frac{r}{sin\theta}y=( −θのscosθ _)バツ+θのsr
定義:
r = xcos θ + ysin θ r=xcos\theta+ysin\thetar=× cosθ _+
与えられた点( x 0 , y 0 ) (x_0,y_0)の場合、 θのys( ×0、y0)は、 ( r , θ ) (r,\theta)の各ペアを意味します。( r ,θ )点( x θ , y θ ) を通る経路を表します (x_\theta,y_\theta)( ×私、y私)直線。極座標面上を通過するすべての直線を極半径と極角まで描画すると、正弦曲線が得られます。たとえば、与えられた点 ( x 0 = 8 および y 0 = 6 ) に対して (x_0 = 8 および y_0 = 6)( ×0=8和y0=6 )次の図を(平面上に)描くことができます:
極座標( r , θ ) (r, θ)( r ,デカルト座標系( x , y ) (x,y)のθ )( x ,y ) :
{ x = rcos θ y = rsin θ \left\{ \begin{aligned} x=rcos\theta \\ y=rsin\theta \\ end{aligned} \right.{
バツ=rcosθ _y=θのrs
デカルト座標 (x,y) から( x , y )を変換します( x ,y )から極座標( r , θ ) (r,θ)( r ,θ ) :
r = x 2 + y 2 θ = arctan ( y / x ) r=\sqrt{x^2+y^2} \\ \theta=arctan(y/x)r=バツ2+y2私=
極座標系のa rc t an ( y / x )は実際には同じです。極座標の点 → ハフ空間の直線ですが、ハフ空間は [ k , q ] [k,q][ k 、q ]、ただし( r , θ ) (r,\theta)( r ,私)。
3. ライン検出
上記の導入から、 x − y xyを描くことがわかります。バツ−y座標空間の点はしかし、パラメータ空間内に複数の曲線の交点がある場合、最も可能性の高い解をどのように選択するかという問題がまだ残っています。
具体的な計算では、パラメータ空間はいわゆる累積単位A ( θ , ρ ) A(\theta,\rho)A (私、ρ )。下図に示すように、x − y xyバツ−y平面の背景以外の各点( xk , yk ) (x_k,y_k)( ×k、yk),令 θ \theta θ は、θ = − xk θ + yk \theta=-x_k\theta+y_kに従って、可能な各細分値に等しくなります。私=− ×k私+yk対応するρ \rhoを計算します。ρ値、A のグループの各計算( θ , ρ ) A(\theta,\rho)A (私、ρ )、A ( θ , ρ ) = A ( θ , ρ ) + 1 A(\theta,\rho)=A(\theta,\rho)+1A (私、r )=A (私、r )+1 . すべての結果を計算した後、A ( θ , ρ ) A(\theta,\rho)A (私、ρ )はθ \thetaのピーク値に対応しますθとρ \rhoρ、直線を検出できます。( θ min , θ max ) (\theta_{min},\theta_{max})(私は分、私マックス_)和( ρ min , ρ max ) (\rho_{min},\rho_{max})( r分、rマックス_)は必要なパラメータ範囲です:0 ο ≤ θ ≤ 18 0 ο 0^\omicron \leq\theta \leq180^\omicron0の≤私≤18 0ο和− D ≤ θ ≤ D -D \leq\theta \leq D−D _≤私≤D、DDDは画像の対角線の長さです。θ \シータθとρ \rhoρの細分数によって、検出結果の精度が決まります。
投票プロセス中、下の GIF を見ることができます。
左側のシアンの点は画像上のピクセルを表し、黄色の点はさまざまな角度からの各ポイントの検索を表します。右半分は投票プレートで、色が薄いほど投票数が多くなります。
4. コードの実装
1. ハフ検知
def lines_detector_hough(img,ThetaDim=None, DistStep=None, threshold=None, halfThetaWindowSize=2,
halfDistWindowSize=None):
'''
:param img: 经过边缘检测得到的二值图
:param ThetaDim: hough空间中theta轴的刻度数量(将[0,pi)均分为多少份),反应theta轴的粒度,越大粒度越细
:param DistStep: hough空间中dist轴的划分粒度,即dist轴的最小单位长度
:param threshold: 投票表决认定存在直线的起始阈值
:return: 返回检测出的所有直线的参数(theta,dist)和对应的索引值,
'''
row,col= edge.shape
if ThetaDim == None:
ThetaDim = 90
if DistStep == None:
DistStep = 1
# 计算距离分段数量
MaxDist = np.sqrt(row ** 2 + col ** 2)
DistDim = int(np.ceil(MaxDist / DistStep))
if halfDistWindowSize == None:
halfDistWindowSize = int(DistDim /50)
# 建立投票
accumulator = np.zeros((ThetaDim, DistDim)) # theta的范围是[0,pi). 在这里将[0,pi)进行了线性映射.类似的,也对Dist轴进行了线性映射
#
sinTheta = [np.sin(t * np.pi / ThetaDim) for t in range(ThetaDim)]
cosTheta = [np.cos(t * np.pi / ThetaDim) for t in range(ThetaDim)]
#计算距离(rho)
for i in range(row):
for j in range(col):
if not edge[i, j] == 0:
for k in range(ThetaDim):
accumulator[k][int(round((i * cosTheta[k] + j * sinTheta[k]) * DistDim / MaxDist))] += 1
M = accumulator.max()
#---------------------------------------
#非极大抑制
if threshold == None:
threshold = int(M * 1.369/ 10)
result = np.array(np.where(accumulator > threshold)) # 阈值化
#获得对应的索引值
temp = [[], []]
for i in range(result.shape[1]):
eight_neiborhood = accumulator[
max(0, result[0, i] - halfThetaWindowSize + 1):min(result[0, i] + halfThetaWindowSize,
accumulator.shape[0]),
max(0, result[1, i] - halfDistWindowSize + 1):min(result[1, i] + halfDistWindowSize,
accumulator.shape[1])]
if (accumulator[result[0, i], result[1, i]] >= eight_neiborhood).all():
temp[0].append(result[0, i])
temp[1].append(result[1, i])
#记录原图所检测的坐标点(x,y)
result_temp= np.array(temp)
#-------------------------------------------------------------
result = result_temp.astype(np.float64)
result[0] = result[0] * np.pi / ThetaDim
result[1] = result[1] * MaxDist / DistDim
return result,result_temp
2. 直線コードを引く
def drawLines(lines, edge, color=(255, 0, 0), err=3):
'''
:param lines: 检测后的直线参数
:param edge: 原图
:param color: 直线的颜色
:param err:检测的可接受的误差值
:return: 无
'''
if len(edge.shape) == 2:
result = np.dstack((edge, edge, edge))
else:
result = edge
Cos = np.cos(lines[0])
Sin = np.sin(lines[0])
for i in range(edge.shape[0]):
for j in range(edge.shape[1]):
e = np.abs(lines[1] - i * Cos - j * Sin)
if (e < err).any():
result[i, j] = color
plt.imshow(result, cmap='gray')
plt.axis('off')
plt.show()
3.ハフスペースコードを描画する
def data_img(data):
'''
:param data: 直线上含有的点(x,y)
:return: 输出hough空间图像
'''
fig = plt.figure() # 新建画布
ax = axisartist.Subplot(fig, 111) # 使用axisartist.Subplot方法创建一个绘图区对象ax
fig.add_axes(ax)
ax.axis[:].set_visible(False) # 隐藏原来的实线矩形
ax.axis["x"] = ax.new_floating_axis(0, 0, axis_direction="bottom") # 添加x轴
ax.axis["y"] = ax.new_floating_axis(1, 0, axis_direction="bottom") # 添加y轴
ax.axis["x"].set_axisline_style("->", size=1.0) # 给x坐标轴加箭头
ax.axis["y"].set_axisline_style("->", size=1.0) # 给y坐标轴加箭头
t = np.arange(-np.pi / 2, np.pi / 2, 0.1)
ax.annotate(text='x', xy=(2 * math.pi, 0), xytext=(2 * math.pi, 0.1)) # 标注x轴
ax.annotate(text='y', xy=(0, 1.0), xytext=(-0.5, 1.0)) # 标注y轴
for i in range(data.shape[1]):
rho = data[0][i] * np.cos(t) + data[1][i] * np.sin(t)
plt.plot(t, rho)
plt.show()
4. 試験結果
検出画像をダウンロードするには、ここをクリックしてください。すべてのコードは、私のGitHub ウェアハウス
にあります。コードが役立つ場合は、スター ハフをクリックしてください。検出の前に、賢明なオペレーターは基本エッジを検出する必要があります。ここをクリックして、関連コンテンツを表示します。賢いアルゴリズム
この記事が役に立った場合は、フォローしていいね!!!!!