Seedlings in-field orientation detection via elementary PCA
In the aim of counting young seedlings, it can be interesting to find the path followed by the tractor first. One can think that with that information known, it’ll be easier to find the rows from where we can eliminate inter-rows vegetation considered as weeds.
Here we explore a naive solution based on PCA. After all, PCA gives the main directions of a points cloud! Actually, even not perfect this solution still succeeds a bit (and allows one to practice PCA with 2 lines of Python’ code):
Loading an orthorectified RGB image
from IPython.display import Image
from IPython.core.display import HTML as Center
Center(""" <style>
.output_png {
display: table-cell;indf
text-align: center;
vertical-align: middle;
height:1600;
width:600;
}
</style> """)
import cv2
import imutils
import numpy as np
import matplotlib.pyplot as plt
Image_path='/home/dac/Bureau/COMPTAGE/Images/tilled.tif'
img=cv2.imread(Image_path,-1)
Nrow=img.shape[0]
Ncol=img.shape[1]
Ncol
4066
###########CallBack Function to get MouseClick pixel values##########################
def mouseRGB(event,x,y,flags,param):
if event == cv2.EVENT_LBUTTONDOWN:
colors = image[y,x]
print("BRG Format: ",colors)
print("Coordinates of pixel: X: ",x,"Y: ",y)
Calculating the Green Index: 2G - R - B
###########GREEN INDEX############################
Red,Green,Blue,_=cv2.split(img)
Green_Idx=2*Green-Blue-Red
print(Green_Idx.shape[0])
Window_name='Green Index'
cv2.namedWindow(Window_name,cv2.WINDOW_NORMAL)
cv2.imshow(Window_name,Green_Idx)
image=Green_Idx
cv2.setMouseCallback(Window_name,mouseRGB)
k = cv2.waitKey(0)
cv2.destroyAllWindows()
3192
Cleaning the image
#############################MASK###################################
mask=cv2.threshold(Green_Idx,100,255,cv2.THRESH_BINARY)[1]
#####################ERODATE FOR OUTLIERS SUPPRESSION####################################
kernel= np.ones((2,2),np.uint8)
erosion=255-cv2.dilate(mask,kernel,iterations=3)
# The best is still to revert the mask
Finding the contours
################FIND CONTOURS#####################################################
Window_name="Contours"
# contours are not anymore in [row, col] but [col, row] format (geometrical points format)
contours, hierarchy = cv2.findContours(erosion, cv2.RETR_LIST, cv2.CHAIN_APPROX_TC89_L1)
cv2.drawContours(erosion, contours, -1, (255,0,0), 3)
cv2.namedWindow(Window_name,cv2.WINDOW_NORMAL)
cv2.imshow(Window_name,erosion)
k = cv2.waitKey(0)
cv2.destroyAllWindows()
Calculating the contours’ centroids
def f_Centroids(contours,axis=0):
Centroids=np.zeros((len(contours),2))
for i,points in enumerate(contours):
Centroids[i]=np.average(points,axis)
return Centroids
##############Contours Centroids#####################################
Centroids=f_Centroids(contours)
##########PLOTS##################################
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB),zorder=1)
plt.scatter(Centroids[:,0],Centroids[:,1],c='b',s=3,zorder=2)
plt.show()
Towards finding the seedlings’ lines
First, we clip the image into Ndisks disks to avoid principal components to be influenced by the shape of the sampling window.
The helpers functions here:
def Disk_clip(points,center, radius):
return points[(np.square(points[:,0]-center[0])+np.square(points[:,1]-center[1]) < np.square(radius))]
def draw_vector(v0, v1, idx, ax=None):
ax = ax or plt.gca()
arrowprops=dict(arrowstyle='->',
linewidth=2,
shrinkA=0, shrinkB=0)
ax.annotate('', v1, v0,arrowprops=arrowprops)
ax.annotate('ax'+str(idx), v1)
def local_directions(Points):
#Data is centered by the function already, not scaled though which is convenient here
pca = PCA(n_components=2)
pca.fit(Points)
idx=0
for length, vector in zip(pca.explained_variance_, pca.components_):
v = vector * np.sqrt(length)
cloud_pts_dir[idx] += vector
#draw_vector(pca.mean_, pca.mean_ + np.array([v[1],v[0]]), idx)
idx+=1
Finally, we average all the principal directions we found.
The main algorithm here:
####################FIND SEEDLINGS LINES FROM PCA#####################
import seaborn as sns; sns.set()
from sklearn.decomposition import PCA
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB),zorder=1)
plt.scatter(Centroids[:,0],Centroids[:,1],c='b',s=3,zorder=2)
Ndisks=1000
Disk_radius=800
Xcenters=np.random.randint(1, Ncol-1, Ndisks)
Ycenters=np.random.randint(1, Nrow -1, Ndisks)
cloud_pts_dir=np.array([[0.0,0.0],[0.0,0.0]])
for i in range(Ndisks):
local_directions( Disk_clip( Centroids,[ Xcenters[i], Ycenters[i]],Disk_radius))
img_center = np.array( [ int(Ncol/2), int(Nrow/2)] )
cloud_pts_dir = 1/Ndisks * cloud_pts_dir
cloud_pts_dir/= np.sqrt(np.square(cloud_pts_dir[0]) + np.square(cloud_pts_dir[1]))
draw_vector(img_center, img_center + 500 * np.array([cloud_pts_dir[0,1] , cloud_pts_dir[0,0] ]), 99999)
plt.axis('equal');
plt.show()