【问题标题】:how can I detect a certain shape (plant stem) in a binary image?如何检测二进制图像中的某种形状(植物茎)?
【发布时间】:2021-07-14 01:07:55
【问题描述】:

我正在研究一个基于形状的叶子分类项目,当时我被困了好几个小时,试图弄清楚如何根据叶子的茎旋转图像。

这是输入图像的示例。

sample

我一直在尝试用 opencv 和 filter2D 用不同的内核甚至 HoughLines 应用形态学变换,这是我迄今为止得到的结果。

detected-steam

任何帮助将不胜感激,在此先感谢。


编辑

茎的位置很重要,因为我试图分类不同种类的叶子,所以我想要完成的是让叶子处于垂直位置,茎在底部。

我提供的图像是经过阈值处理的图像,我将原始图像留在这里。

我提供了第二个样本,因为在这种特殊情况下,我无法将茎放在底部,所以它最终位于顶部。

样品返回 2 度。

【问题讨论】:

  • 如果你知道叶子总是在一个主要方向上,那么你可以总结每一列、每一行中“内部”像素的数量,从那个列表中应该很清楚哪个方向有你的“细长”部分。
  • 发布您的原始图片,不要打勾和标签,以便我们使用它。您可以考虑使用图像矩中的等效椭圆方向来获取方向。

标签: python opencv object-detection


【解决方案1】:

这是一个想法。将椭圆拟合到阈值叶形(假设它长于宽)。

输入:

import cv2
import numpy as np

# read image
img = cv2.imread('leaf.png')

# threshold on color
lower=(84,1,68)
upper=(84,1,68)
thresh = cv2.inRange(img, lower, upper)

# get contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# fit ellipse to leaf contours
ellipse = cv2.fitEllipse(big_contour)
(xc,yc), (d1,d2), angle = ellipse
print(xc,yc,d1,d1,angle)

# draw ellipse on copy of input
result = img.copy() 
cv2.ellipse(result, ellipse, (0,0,255), 1)

# save results
cv2.imwrite('leaf_threshold.png',thresh)
cv2.imwrite('leaf_ellipse.png',result)

# show results
cv2.imshow("thresh", thresh)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

阈值图像:

椭圆图像:

\

椭圆信息:

126.44944763183594 101.98369598388672 112.40930938720703 112.40930938720703 89.33087158203125

So angle = 89.33087158203125 deg (cw from -y axis, i.e. from the top) or 
   angle = 0.66912841796875 deg (ccw from the x axis, i.e. from right side)

补充:

这里有一个更完整的解决方案。但它假设叶子的长度大于宽度,因此椭圆长轴沿步进方向对齐。

叶 1:

叶子 2:

import cv2
import numpy as np

# read image
#img = cv2.imread('leaf1.jpg')
img = cv2.imread('leaf2.jpg')

# threshold on color
lower=(0,0,0)
upper=(130,190,140)
thresh = cv2.inRange(img, lower, upper)

# get contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)

# fit ellipse to leaf contours
ellipse = cv2.fitEllipse(big_contour)
(xc,yc), (d1,d2), angle = ellipse
print(xc,yc,d1,d1,angle)

# draw ellipse on copy of input
graphic = img.copy() 
cv2.ellipse(graphic, ellipse, (0,0,255), 1)


# rotate image so step points downward   
if angle >= 135 and angle <=180:
    result = cv2.rotate(img, cv2.ROTATE_180)
elif angle >= 45 and angle <135:
    result = cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE)
elif angle >= 0 and angle <45:
    result = img.copy()
    

# save results
cv2.imwrite('leaf2_threshold.png',thresh)
cv2.imwrite('leaf2_ellipse.png',graphic)
cv2.imwrite('leaf2_result.png',result)

# show results
cv2.imshow("thresh", thresh)
cv2.imshow("graphic", graphic)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

叶 1 阈值:

叶 1 椭圆:

叶子 1 旋转:

叶子 2 阈值:

叶子 2 椭圆:

叶子 2 旋转:

【讨论】:

  • 嗨 fmw42,非常感谢您的快速回答,您的代码有效,但我遗漏了一些内容,我需要将茎垂直并放置在图像的底部。如何确定是否将角度转换为负数?提前致谢
  • 将此图像旋转角度 = 89.33087158203125 度或角度 = -89.33087158203125 度。无论哪一个为您提供所需的方向,请为所有其他人保留该标志。
  • 我不明白负角或正角的标准是什么,对描述中的两个图像使用相同的角度和正角,茎最终位于图像的顶部。我尝试过的其他一些图像甚至不旋转(当我期望 90 或 -90 时,我得到了 2 度)
  • 发布一个给出 2 度的示例。叶子有那么长吗?茎是短的还是长的?
  • 我刚刚发布了它,茎很长,但根据样品的不同,叶子可能会更宽或更长(查看我附上的图片)。谢谢
猜你喜欢
  • 2016-05-22
  • 2021-06-17
  • 2017-04-29
  • 1970-01-01
  • 2011-07-28
  • 2019-06-18
  • 2021-11-18
  • 2016-10-04
相关资源
最近更新 更多