Reputation: 194
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.
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.
Any help would be appreciated, thanks in advance.
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.
Upvotes: 0
Views: 802
Reputation: 53081
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:
Upvotes: 1