简体   繁体   中英

how can I detect a certain shape (plant stem) in a binary image?

I'm working on a shape based leaf classification project when i got stuck for hours trying to figure out how to rotate the image based on the stem of the leaf.

Here is an example of an input image.

sample

I have been trying to apply morphological transformations with opencv and filter2D with different kernels and even HoughLines, this is the closes I have gotten so far.

detected-steam

Any help would be appreciated, thanks in advance.


EDIT

The position of the stem is important because of the different kind of leaves I'm trying to classify, so What I'm trying to acomplish is to get the leaf in a vertical position with the stem being at the bottom.

The images I provided are the thresholded images, I leave the original one down here.

在此处输入图像描述

I provide a second sample because I in this particular case i couldn't get the stem at the bottom so it ended up being at the top.

在此处输入图像描述

Sample returning 2 degrees.

在此处输入图像描述

Here is one idea. Fit an ellipse to thresholded leaf shape (assuming it will be longer than wide).

Input:

在此处输入图像描述

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()

Threshold image:

在此处输入图像描述

Ellipse image:

在此处输入图像描述 \

Ellipse Information:

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)

ADDITION:

Here is a more complete solution. But it assumes the leaf will be longer than wide so that the ellipse major axis aligns along the step direction.

Leaf 1:

在此处输入图像描述

Leaf 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()

Leaf 1 Threshold:

在此处输入图像描述

Leaf 1 Ellipse:

在此处输入图像描述

Leaf 1 Rotated:

在此处输入图像描述

Leaf 2 Threshold:

在此处输入图像描述

Leaf 2 Ellipse:

在此处输入图像描述

Leaf 2 Rotated:

在此处输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM