Dans cette note on va voir un exemple simple de vision par ordinateur (computer vision), le cas de la détection de lignes droites par la transformée de Hough (voir la page de wikipedia en anglais qui est plus complète) avec un code python. Considérons l'image de depart:

Lire l'image
La premiere étape consiste à lire l'image (avec python il existe plusieurs façons de lire une image, on peut passer par numpy ou scipy). Exemple avec scipy:

from scipy import miscimport matplotlib.pyplot as pltimport numpy as npimport mathimport scipy.ndimage.filters as filtersimport scipy.ndimage as ndimageimg = misc.imread('pentagon.png')print 'image shape: ', img.shapeplt.imshow(img, )plt.savefig("image.png",bbox_inches='tight')plt.close()
Espace de Hough
Deuxième étape calculer l'espace de Hough pour une liste de r et de theta.

img_shape = img.shapex_max = img_shape[0]y_max = img_shape[1]theta_max = 1.0 * math.pitheta_min = 0.0r_min = 0.0r_max = math.hypot(x_max, y_max)r_dim = 200theta_dim = 300hough_space = np.zeros((r_dim,theta_dim))for x in range(x_max):for y in range(y_max):if img[x,y,0] == 255: continuefor itheta in range(theta_dim):theta = 1.0 * itheta * theta_max / theta_dimr = x * math.cos(theta) + y * math.sin(theta)ir = r_dim * ( 1.0 * r ) / r_maxhough_space[ir,itheta] = hough_space[ir,itheta] + 1plt.imshow(hough_space, origin='lower')plt.xlim(0,theta_dim)plt.ylim(0,r_dim)tick_locs = [i for i in range(0,theta_dim,40)]tick_lbls = [round( (1.0 * i * theta_max) / theta_dim,1) for i in range(0,theta_dim,40)]plt.xticks(tick_locs, tick_lbls)tick_locs = [i for i in range(0,r_dim,20)]tick_lbls = [round( (1.0 * i * r_max ) / r_dim,1) for i in range(0,r_dim,20)]plt.yticks(tick_locs, tick_lbls)plt.xlabel(r'Theta')plt.ylabel(r'r')plt.title('Hough Space')plt.savefig("hough_space_r_theta.png",bbox_inches='tight')plt.close()
Trouver les maximums
Après avoir calculer l'espace de Hough sur une grille de r et de theta, l'étape suivante consiste a trouver les extremums locaux. Il existe plusieurs approches pour faire cela (déterministe ou stochastique), ici on va choisir une approche déterministe (voir Get coordinates of local maxima in 2D array above certain value):
neighborhood_size = 20threshold = 140data_max = filters.maximum_filter(hough_space, neighborhood_size)maxima = (hough_space == data_max)data_min = filters.minimum_filter(hough_space, neighborhood_size)diff = ((data_max - data_min) > threshold)maxima[diff == 0] = 0labeled, num_objects = ndimage.label(maxima)slices = ndimage.find_objects(labeled)x, y = [], []for dy,dx in slices:x_center = (dx.start + dx.stop - 1)/2x.append(x_center)y_center = (dy.start + dy.stop - 1)/2y.append(y_center)print xprint yplt.imshow(hough_space, origin='lower')plt.savefig('hough_space_i_j.png', bbox_inches = 'tight')plt.autoscale(False)plt.plot(x,y, 'ro')plt.savefig('hough_space_maximas.png', bbox_inches = 'tight')plt.close()
Tracer les lignes
Connaissant les 5 premiers extremums locaux on peut ensuite simplement tracer les droites sur l'image de départ pour voir le résultat :
On constate dans cet exemple que certaines lignes du pentagone ne sont pas parfaitement détectées (léger désalignement). Pour améliorer cela on pourrait par exemple utiliser une grille de r et theta plus fine ou améliorer la recherche d'extremums locaux avec un gradient descent, etc
line_index = 1for i,j in zip(y, x):r = round( (1.0 * i * r_max ) / r_dim,1)theta = round( (1.0 * j * theta_max) / theta_dim,1)fig, ax = plt.subplots()ax.imshow(img)ax.autoscale(False)px = []py = []for i in range(-y_max-40,y_max+40,1):px.append( math.cos(-theta) * i - math.sin(-theta) * r )py.append( math.sin(-theta) * i + math.cos(-theta) * r )ax.plot(px,py, linewidth=10)plt.savefig("image_line_"+ "%02d" % line_index +".png",bbox_inches='tight')#plt.show()plt.close()line_index = line_index + 1
Remarque: pour détecter des lignes droites dans une image plus complexe on peut utiliser au préalable un filtre de canny pour détecter dans un premier temps les contours.
Code source
from scipy import miscimport matplotlib.pyplot as pltimport numpy as npimport math#----------------------------------------------------------------------------------------## Step 1: read imageimg = misc.imread('pentagon.png')print 'image shape: ', img.shapeplt.imshow(img, )plt.savefig("image.png",bbox_inches='tight')plt.close()#----------------------------------------------------------------------------------------## Step 2: Hough Spaceimg_shape = img.shapex_max = img_shape[0]y_max = img_shape[1]theta_max = 1.0 * math.pitheta_min = 0.0r_min = 0.0r_max = math.hypot(x_max, y_max)r_dim = 200theta_dim = 300hough_space = np.zeros((r_dim,theta_dim))for x in range(x_max):for y in range(y_max):if img[x,y,0] == 255: continuefor itheta in range(theta_dim):theta = 1.0 * itheta * theta_max / theta_dimr = x * math.cos(theta) + y * math.sin(theta)ir = r_dim * ( 1.0 * r ) / r_maxhough_space[ir,itheta] = hough_space[ir,itheta] + 1plt.imshow(hough_space, origin='lower')plt.xlim(0,theta_dim)plt.ylim(0,r_dim)tick_locs = [i for i in range(0,theta_dim,40)]tick_lbls = [round( (1.0 * i * theta_max) / theta_dim,1) for i in range(0,theta_dim,40)]plt.xticks(tick_locs, tick_lbls)tick_locs = [i for i in range(0,r_dim,20)]tick_lbls = [round( (1.0 * i * r_max ) / r_dim,1) for i in range(0,r_dim,20)]plt.yticks(tick_locs, tick_lbls)plt.xlabel(r'Theta')plt.ylabel(r'r')plt.title('Hough Space')plt.savefig("hough_space_r_theta.png",bbox_inches='tight')plt.close()#----------------------------------------------------------------------------------------## Find maximas 1'''Sorted_Index_HoughTransform = np.argsort(hough_space, axis=None)print 'Sorted_Index_HoughTransform[0]', Sorted_Index_HoughTransform[0]#print Sorted_Index_HoughTransform.shape, r_dim * theta_dimshape = Sorted_Index_HoughTransform.shapek = shape[0] - 1list_r = []list_theta = []for d in range(5):i = int( Sorted_Index_HoughTransform[k] / theta_dim )#print i, round( (1.0 * i * r_max ) / r_dim,1)list_r.append(round( (1.0 * i * r_max ) / r_dim,1))j = Sorted_Index_HoughTransform[k] - theta_dim * iprint 'Maxima', d+1, 'r: ', j, 'theta', round( (1.0 * j * theta_max) / theta_dim,1)list_theta.append(round( (1.0 * j * theta_max) / theta_dim,1))print "--------------------"k = k - 1#theta = list_theta[7]#r = list_r[7]#print " r,theta",r,theta, math.degrees(theta)'''#----------------------------------------------------------------------------------------## Step 3: Find maximas 2import scipy.ndimage.filters as filtersimport scipy.ndimage as ndimageneighborhood_size = 20threshold = 140data_max = filters.maximum_filter(hough_space, neighborhood_size)maxima = (hough_space == data_max)data_min = filters.minimum_filter(hough_space, neighborhood_size)diff = ((data_max - data_min) > threshold)maxima[diff == 0] = 0labeled, num_objects = ndimage.label(maxima)slices = ndimage.find_objects(labeled)x, y = [], []for dy,dx in slices:x_center = (dx.start + dx.stop - 1)/2x.append(x_center)y_center = (dy.start + dy.stop - 1)/2y.append(y_center)print xprint yplt.imshow(hough_space, origin='lower')plt.savefig('hough_space_i_j.png', bbox_inches = 'tight')plt.autoscale(False)plt.plot(x,y, 'ro')plt.savefig('hough_space_maximas.png', bbox_inches = 'tight')plt.close()#----------------------------------------------------------------------------------------## Step 4: Plot linesline_index = 1for i,j in zip(y, x):r = round( (1.0 * i * r_max ) / r_dim,1)theta = round( (1.0 * j * theta_max) / theta_dim,1)fig, ax = plt.subplots()ax.imshow(img)ax.autoscale(False)px = []py = []for i in range(-y_max-40,y_max+40,1):px.append( math.cos(-theta) * i - math.sin(-theta) * r )py.append( math.sin(-theta) * i + math.cos(-theta) * r )ax.plot(px,py, linewidth=10)plt.savefig("image_line_"+ "%02d" % line_index +".png",bbox_inches='tight')#plt.show()plt.close()line_index = line_index + 1#----------------------------------------------------------------------------------------## Plot lines'''i = 11j = 264i = y[1]j = x[1]print i,jr = round( (1.0 * i * r_max ) / r_dim,1)theta = round( (1.0 * j * theta_max) / theta_dim,1)print 'r', rprint 'theta', thetafig, ax = plt.subplots()ax.imshow(img)ax.autoscale(False)px = []py = []for i in range(-y_max-40,y_max+40,1):px.append( math.cos(-theta) * i - math.sin(-theta) * r )py.append( math.sin(-theta) * i + math.cos(-theta) * r )print pxprint pyax.plot(px,py, linewidth=10)plt.savefig("PlottedLine_07.png",bbox_inches='tight')#plt.show()'''
Références
| Liens | Site |
|---|---|
| Hough transform | wikipedia |
| Transformée de Hough | wikipedia |
| scikit | scikit |
| Get coordinates of local maxima in 2D array above certain value | stackoverflow |
