# ~*~ encoding: utf-8 ~*~ # import random import time class Shape: # récupère la forme complète autour du pixel donné # #################################################### # @param originalPixel pixel de base # @param pixelMap matrice de pixels # @param seuil Ecart entre le pixel et ses alentours à partir duquel on considère un contour ou une continuité de la forme # # @return retourne la liste des pixels composant la forme (références) # def getShape(self, originalPixel, pixelMap, seuil=10): width = len( pixelMap[0] ) height = len( pixelMap ) shape = [] # contiendra les pixels de la forme waiting = [originalPixel] # contient les pixels à traiter # on continue d'analyser tant qu'il y a des pixels à traiter while len(waiting) > 0: pixel = waiting[0] # on ajoute le pixel à la forme shape.append( pixel ) xm, xM = pixel.x, pixel.x # valeurs minimales et maximales de x autour du pixel ym, yM = pixel.y, pixel.y # valeurs minimales et maximales de y autour du pixel # si on est pas sur le bord gauche if pixel.x > 0: xm = pixel.x - 1 # si on est pas sur le bord droit if pixel.x < width-1: xM = pixel.x + 1 # si on est pas sur le bord haut if pixel.y > 0: ym = pixel.y - 1 # si on est pas sur le bord bas if pixel.y < height-1: yM = pixel.y + 1 # on parcourt les pixels alentours for j in range(ym, yM+1): for i in range(xm, xM+1): currentP = pixelMap[j][i] # si le pixel n'a pas une couleur trop éloignée du pixel central if abs(pixel.r-currentP.r) <= seuil and abs(pixel.g-currentP.g) <= seuil and abs(pixel.b-currentP.b) <= seuil: # on ajoute le pixel à la liste d'attente if not( currentP in shape or currentP in waiting): waiting.append( currentP ) # on retire le pixel de la liste d'attente try: waiting.remove( pixel ) except: pass return shape; # récupère le contour dans lequel est le pixel donné # ###################################################### # @param originalPixel pixel original # @param pixelMap matrice de pixels # @param seuil Ecart entre le pixel et ses alentours à partir duquel on considère un contour ou une continuité de la forme # # couleur de originalPixel = forme # + de seuil par rapport à originalPixel = fond # # @return retourne la liste des pixels composant le contour # def getEdges(self, originalPixel, pixelMap, seuil=10): width = len( pixelMap[0] ) height = len( pixelMap ) # retourne un kernel 2x2 (sur lequel on fera les traitements) def getKernel(pixel, pos): x, y = pixel.x, pixel.y if pos == 0: return [ pixelMap[y][x], pixelMap[y][x+1], pixelMap[y+1][x], pixelMap[y+1][x+1] ]; # [pixel, autre], [autre, autre] elif pos == 1: return [ pixelMap[y][x-1], pixelMap[y][x], pixelMap[y+1][x-1], pixelMap[y+1][x] ]; # [autre, pixel], [autre, autre] elif pos == 2: return [ pixelMap[y-1][x+1], pixelMap[y-1][x], pixelMap[y][x], pixelMap[y][x+1] ]; # [autre, autre], [pixel, autre] elif pos == 3: return [ pixelMap[y-1][x-1], pixelMap[y-1][x], pixelMap[y][x-1], pixelMap[y][x] ]; # [autre, autre], [autre, pixel] # retourne un kernel 2x2 en fonction de la direction actuelle et de la position (changement de position) def flipKernel(kernel, pos, vect): pixel = kernel[pos]; if pos == 0: # si kernel de type [pixel, autre], [autre, autre] if vect == 0: # si direction == 0 (vers la droite) return getKernel(pixel, pos+2) if vect == 1: # si direction == 1 (vers le bas) return getKernel(pixel, pos+1) if pos == 1: # si kernel de type [autre, pixel], [autre, autre] if vect == 1: # si direction == 1 (vers le bas) return getKernel(pixel, pos-1) if vect == 2: # si direction == 2 (vers la gauche) return getKernel(pixel, pos+2) if pos == 2: # si kernel de type [autre, autre], [pixel, autre] if vect == 3: # si direction == 3 (vers le haut) return getKernel(pixel, pos+1) if vect == 0: # si direction == 0 (vers la droite) return getKernel(pixel, pos-2) if pos == 3: # si kernel de type [autre, autre], [autre, pixel] if vect == 2: # si direction == 2 (vers la gauche) return getKernel(pixel, pos-2) if vect == 3: # si direction == 3 (vers le haut) return getKernel(pixel, pos-1) # dans un cas non pris en charge on renvoie une liste de "0" return [0,0,0,0] stroke = [] # le pixel de départ master = originalPixel slaves = []; nextP = None position = 0; # cf. getKernel direction = 0; # 0 = droite, 1 = bas, 2 = gauche, 4 = haut # récupère les pixels while nextP != master: # on définit le kernel k = getKernel(master, position) # on vide la liste des esclaves slaves = [] # on parcourt les pixels du kernel for pix in k: # si pixel sur même ligne ou colonne (contre le pixel maître) if pix.x == pixel.x or pix.y == pixel.y: slaves.append( pix ); maybeNext = [None, 0]; # on parcourt les esclaves for pix in slaves: # si l'esclave est quasiment de la même couleur (différence r+g+b < seuil) if abs(pixel.r-pix.r) <= seuil and abs(pixel.g-pix.g) <= seuil and abs(pixel.b-pix.b) <= seuil: if pixel.x != pix.x: # si le contour suit les x maybeNext = [pix, pix.x-pixel.x] else: # si le contour suit les y maybeNext = [pix, ] return stroke # récupère les contours de chaque forme à partir de formes pleines # #################################################################### # @param pixelMap matrice de pixels # @param seuil Ecart entre le pixel et ses alentours à partir duquel on considère un contour ou une continuité de la forme # # Blanc = fond # Noir = forme # # @return retourne la liste des contours contenant chacun les pixels la composant # def getStrokes(self, pixelMap, seuil=10): strokeArray = [] # contiendra la liste des contours already = [] # contient la liste des contours, séparés par un élément valant "0" stroke = [] # contiendra les pixels du contour en cours # on parcourt tout les pixels for line in pixelMap: for pixel in line: if (pixel.r+pixel.g+pixel.b) >= 3*255/2 and pixel not in already: # si pixel ~blanc~ et pas déjà dans un contour # traitement de forme stroke = self.getEdges(pixel, pixelMap, seuil=seuil); already += [0] + stroke return strokeArray;