ハフ変換を使用したOpenCV、100%
私の最初のアイデアは、ヤギの脚の垂直線を検出し、体と地平線に対する垂直位置を決定することでした。
結局のところ、すべての画像で地面は非常にノイズが多く、キャニーエッジ検出出力とそれに対応するハフ変換からの検出されたラインが多くなります。私の戦略は、水平線が画像の上半分にあるか下半分にあるかを判断することでした。これは問題を解決するのに十分でした。
# Most of this code is from OpenCV examples
import cv2
import numpy as np
def is_upgoat(path):
img = cv2.imread(path)
height, width, channels = img.shape
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 200, None, 0, 0, np.pi/2-0.5, np.pi/2+0.5)
rho_small = 0
for line in lines:
rho, theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 5000*(-b))
y1 = int(y0 + 5000*(a))
x2 = int(x0 - 5000*(-b))
y2 = int(y0 - 5000*(a))
if rho/height < 1/2: rho_small += 1
cv2.line(img,(x1,y1),(x2,y2),(0,0,255),1, cv2.LINE_AA)
output_dir = "output/"
img_name = path[:-4]
cv2.imwrite(output_dir + img_name + "img.jpg", img)
cv2.imwrite(output_dir + img_name + "edges.jpg", edges)
return rho_small / len(lines) < 1/2
for i in range(1, 10):
downgoat_path = "downgoat" + str(i) + ".jpg"
print(downgoat_path, is_upgoat(downgoat_path))
for i in range(1, 10):
upgoat_path = "upgoat" + str(i) + ".jpg"
print(upgoat_path, is_upgoat(upgoat_path))
以下は、画像を出力しない機能全体です。
def is_upgoat(path):
img = cv2.imread(path)
height, width, channels = img.shape
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi/180, 200, None, 0, 0, np.pi/2-0.5, np.pi/2+0.5)
rho_small = 0
for line in lines:
rho, theta = line[0]
if rho/height < 1/2: rho_small += 1
return rho_small / len(lines) < 1/2
Downgoat1エッジ:
Downgoat1行:
Upgoat2のエッジとライン:
この方法は、特にノイズの多い画像でもうまく機能しました。downgoat3のエッジとラインは次のとおりです。
補遺
ハフ変換がキャニーエッジ検出よりもはるかに良好に動作する前に、主にノイズの多い領域で中央値ぼけが良いため、中央値ぼけと適応ガウスしきい値処理が判明します。しかし、私の元のアプローチの問題はすぐに明確になります。顕著な背景線が検出され、いくつかの写真ではヤギの顔も検出されます。
def is_upgoat2(path):
img = cv2.imread(path)
#height, width, channels = img.shape
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 19)
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY_INV, 11, 2)
lines = cv2.HoughLinesP(thresh, 1, np.pi / 180, threshold=100,
minLineLength=50, maxLineGap=10)
vert_y = []
horiz_y = []
for line in lines:
x1, y1, x2, y2 = line[0]
# Vertical lines
if x1 == x2 or abs((y2-y1)/(x2-x1)) > 3:
vert_y.append((y1+y2)/2)
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
# Horizontal lines
if x1 != x2 and abs((y2-y1)/(x2-x1)) < 1/3:
horiz_y.append((y1+y2)/2)
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
print(np.median(vert_y), np.median(horiz_y))
downgoat8は次のとおりです。
輪郭(コードは表示されていません)は、ヤギ(背骨)の上端をかなりよく検出しますが、形状全体を取得できません。
さらなる研究: OpenCVにはHaar特徴ベースのオブジェクト検出機能があり、通常は車や顔などに使用されますが、特徴的な形状を考えるとヤギにも機能する可能性があります。
2D機能の認識は有望に見えますが(スケーリングと回転のためにテンプレートマッチングは機能しません)、OpenCV for C ++を理解するのは面倒です。