Missing Crop Vegetation Areas Detection with Python and Scikit Learn

An applied case for the recognition of missing crop vegetation areas based on a drone orthophoto. Contours have been identified from an enhanced combination of raster bands with a marching squares method to find constant valued contours and then exported as geospatial polygons.

Tutorial content:

  • A python class to find crop rows

  • Remove shadows on the raster image

  • Define missing vegetation by marching squares

  • Export results as geospatial polygon

Tutorial



Code

from workingFunctions import cropRecognition
import geopandas as gpd
C:\Users\saulm\anaconda3\Lib\site-packages\paramiko\transport.py:219: CryptographyDeprecationWarning: Blowfish has been deprecated
  "class": algorithms.Blowfish,
# create a crop recognition object
cropLines = cropRecognition('Data/Rst/Ortho-DroneMapper_Clipped2.tif')
# refine band red=1 green=2 blue=3
cropLines.band = 1
#plot selected band
cropLines.plotBand(500,500)
cropLines.fixBand()
cropLines.plotBand(500,500)
#define canny filter sigma to represent
cropLines.cannyMin, cropLines.cannyMean, cropLines.cannyMax = 1,2,3
#display different canny filters
cropLines.cannyFilterDisplay()
#define selected canny filter
cropLines.cannySigma = 2
#show canny filter array
cropLines.cannyFilter
array([[False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       ...,
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False],
       [False, False, False, ..., False, False, False]])
#perform hough transform for crop line recornition
cropLines.displayHoughTransform()
#define the geometry of the area of interest
aoi = gpd.read_file('Data/Shp/aoi.shp')
cropLines.aoiGeom = aoi.iloc[0].geometry
cropLines.aoiGeom
#create lines in the area of interest extension
cropLines.linesToAoi()
#export interpreted data to shapefile
cropLines.createShapefile('Data/cropRows.shp')
88.0
87.3167701863354
36.413843241690586
#plot crop lines 
import rasterio
import matplotlib.pyplot as plt
from rasterio.plot import show

df = gpd.read_file('Data/cropRows.shp')
image = rasterio.open('Data/Rst/Ortho-DroneMapper_Clipped2.tif')
fig, ax = plt.subplots(figsize=(12,12))
lineShow = df.plot(column='angle', cmap='RdBu', ax=ax)
rasterShow = show(image, ax=ax, alpha=0.8)
plt.show()

Missing plants

image = rasterio.open('Data/Rst/Ortho-DroneMapper_Clipped2.tif')
image.crs.to_epsg()
32616
defaultRGB = {1:159,2:181,3:118}
redBand = image.read(1)
redBand[redBand<defaultRGB[1]]=defaultRGB[1]
greenBand = image.read(2)
greenBand[greenBand<defaultRGB[2]]=defaultRGB[2]
blueBand = image.read(3)
blueBand[blueBand<defaultRGB[3]]=defaultRGB[3]

hatariIndex = (redBand + blueBand)/greenBand
plt.imshow(hatariIndex[:500,:500])
<matplotlib.image.AxesImage at 0x1f984f44e90>
from skimage import measure
import numpy as np
import fiona
# Find contours at a constant value of 0.8
contours = measure.find_contours(hatariIndex, 0.5)
contours[0][:5]
array([[ 0.        , 34.13134851],
       [ 1.        , 34.21213536],
       [ 2.        , 34.71721311],
       [ 2.25373134, 35.        ],
       [ 2.65689742, 36.        ]])
sampleList = []
for contour in contours:
    sampleList.append(contour.shape[0])
sampleList[:5]
[77, 3, 387, 14, 4]
np.quantile(sampleList, 0.9)
31.0
# Display the image and plot all contours found
fig, ax = plt.subplots(figsize=(12,12))
ax.imshow(hatariIndex, alpha=0.5)

contourList = []
for contour in contours:
    if contour.shape[0] > np.quantile(sampleList, 0.99): 
        ax.plot(contour[:, 1], contour[:, 0], linewidth=2)
        contourList.append(contour)
ax.set_xlim(0,500)
ax.set_ylim(0,500)
plt.show()
# define schema
schema = {
    'geometry':'Polygon',
    'properties':[('Id','int')]
}
#open a fiona object
polyShp = fiona.open('Data/missingVegetation.shp', mode='w', driver='ESRI Shapefile',
          schema = schema, crs = image.crs.to_string())

for index, contour in enumerate(contourList):
    xyList = []
    for vertex in contour:
        x,y = image.xy(vertex[0],vertex[1])
        xyList.append((x,y))  
    rowDict = {
        'geometry' : {'type':'Polygon',
                         'coordinates': [xyList]}, #Here the xyList is in brackets
        'properties': {'Id' : index},
        }
    polyShp.write(rowDict)
polyShp.close()
df1 = gpd.read_file('Data/cropRows.shp')
df2 = gpd.read_file('Data/missingVegetation.shp')

image = rasterio.open('Data/Rst/Ortho-DroneMapper_Clipped2.tif')
fig, ax = plt.subplots(figsize=(12,12))
lineShow = df1.plot(color ='crimson', ls = 'dashed', ax=ax, alpha=0.5)
polyShow = df2.plot(color = 'royalblue', ax=ax)
rasterShow = show(image, ax=ax, alpha=0.8)
ax.set_xlim(536630,536660)
ax.set_ylim(4346830,4346860)
plt.show()

Input data

You can download the input data from this link:

owncloud.hatarilabs.com/s/2LjVQVGDIcdy0Xy

Password to download data: Hatarilabs.

2 Comments

Saul Montoya

Saul Montoya es Ingeniero Civil graduado de la Pontificia Universidad Católica del Perú en Lima con estudios de postgrado en Manejo e Ingeniería de Recursos Hídricos (Programa WAREM) de la Universidad de Stuttgart con mención en Ingeniería de Aguas Subterráneas y Hidroinformática.

 

Suscribe to our online newsletter

Subscribe for free newsletter, receive news, interesting facts and dates of our courses in water resources.