キュウリの曲がり具合を画像処理でみてみる(2) 幅と長さを測る

20200401_果実

前回、キュウリの曲がり具合を測ってみました。

    Emotion Explorer - キュウリの曲がり具合を画像処理でみてみる。


下画像のキュウリの重なりを何とかならないものかと、ちょっとやってみたが
結局、通常の方法では厳しい。機械学習の守備範囲かな?

  20201122_head_disp.jpg

キュウリ画像の重なりを際立たせる撮像を行うか、
別のレーザーセンサーなどを平行で使うか、AIかというかんじでしょうか。
色が同じで、材質も同じ。これは通常ロジックでは厳しい。
出来る範囲としてはキュウリ画像の幅と長さくらいはわかるだろうと、
ちょっと今回はそれをやってみます。



Pythonサンプルプログラム(差分)

今回は差分で掲載します。

差分1
class WidthScanner:
def __init__(self, coef, mx, my):
self._imx = int(mx)
self._imy = int(my)
self.delta = -1 / coef[0]
self.b = my - mx * self.delta

def getX(self, y):
x = (y - self.b) / self.delta
return x

def getPoints(self, binimg):
h,w = binimg.shape
# 中央線より上をスキャン
x1 = 0
y1 = 0
for y in range(self._imy, 0, -1):
x = int(self.getX(y))
if binimg[y,x] == 0:
break
else:
x1 = x
y1 = y

# 中央線より下をスキャン
x2 = 0
y2 = 0
for y in range(self._imy, h, 1):
x = int(self.getX(y))
if binimg[y,x] == 0:
break
else:
x2 = x
y2 = y
dist = math.sqrt((x1-x2)**2+(y1-y2)**2)
return (x1,y1), (x2, y2), dist

キュウリの幅(直径)を測るクラスです。
3次回帰で求めた曲線の垂線で上下にスキャンします。
その上下両端の長さをもって幅とします。
このスキャンロジックなど、全般的にやっつけで作っていますので
実際に使おうとすると手がかかります。


差分2

cv2.rectangle(res,(tobjx,tobjy),(tobjx+tobjw,tobjy+tobjh),(0,255,255),1)

# for x in range(tobjx, tobjx+tobjw):
# v = expr(x)
# res[int(v),x,:] = (255,255,0)

# 幅を測る
offsetx = 10
incx = 16
cary = np.zeros((incx,), np.float32)
carx = np.zeros((incx,), np.float32)
lbno = 1
for sx in range(tobjx+incx, tobjx+tobjw-incx, incx):
cary = np.zeros((incx,), np.uint8)
for x in range(sx, sx+incx):
vy = expr(x)
carx[sx-x] = x
cary[sx-x] = vy
rv1 = np.polyfit(carx ,cary,1)
expr1 = np.poly1d(rv1)

mx = sx + incx // 2
my = expr(mx)

# 直行を求める
ex = WidthScanner(rv1, mx, my)
spt, ept, dist = ex.getPoints(bres)
cv2.line(res, (int(spt[0]), int(spt[1])), (int(ept[0]), int(ept[1])), (255,0,0), 1, lineType=cv2.LINE_AA)

cv2.putText(res, str(lbno), (int(spt[0]-6), int(spt[1])-8), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,255,255), 1,cv2.LINE_AA)
print('%2d.直径 %.2f' % (lbno, dist))
lbno += 1

# 長さを測る
total_len = 0
scan_flag = False
by = 0
for x in range(tobjx, tobjx+tobjw):
if scan_flag == False and bres[int(vy), x] == 255:
scan_flag = True
vy = expr(x)
bx = x
by = vy
elif scan_flag == True :
if bres[int(vy), x] == 255:
vy = expr(x)
sdist = math.sqrt((bx - x)** 2 + (by - vy) ** 2)
total_len += sdist
cv2.line(res, (int(x), int(vy)), (int(bx), int(by)), (255,0,255), 2, lineType=cv2.LINE_AA)
bx = x
by = vy
else:
break
print('トータル長(pixel)=%.2f' %(total_len))

main()に加筆する部分。
幅(直径)と長さを測定するループです。



動作環境

Windows Anaconda3
Python 3.9.0
OpenCV 4.4.0
Numpy 1.19.2



試行


入力画像(全体)


  20201121B_kyuri400.jpg

赤枠内のキュウリを処理します。

処理対象と計測結果


  20201122_res_disp.jpg

下の方が測定結果画像です。
青線が幅(直径)を測った位置を表しています。
黄色の数字位置の幅(直径)は、下のコンソール表示に表示しています。
単位はピクセル。


コンソール表示
$ python kyuri3.py
1.直径 38.08
2.直径 45.65
3.直径 50.99
4.直径 55.46
5.直径 57.78
6.直径 58.87
7.直径 59.14
8.直径 58.94
9.直径 57.71
10.直径 56.22
11.直径 53.08
12.直径 51.09
13.直径 49.25
14.直径 48.27
15.直径 47.43
16.直径 44.78
17.直径 44.29
18.直径 41.88
19.直径 42.80
20.直径 39.70
トータル長(pixel)=388.00

トータル長は3次回帰で求めた位置のピクセル長です。



感想

全体的に、やっつけで作っていますので、オールラウンドで使えません。
幅の測り方は、これは私なりのこの方法で、これで良いのかどうかについては
それぞれかと思います。
まあ、こんなところかな。後はどれだけ曲がっているかが計算できればOKかと思います。
いいかんじです。



修正・加筆

  • 2021/04/20 修正
    IntersectionObserver’s による画像のオフスクリーン遅延読み込み処理に変更。
    IE11未対応。



参考




写真引用



#画像処理
#画像検査
#スマート農業

関連記事
スポンサーサイト



コメント

非公開コメント