# HG changeset patch # User SylvainPL # Date 1266508416 18000 # Node ID 4a29910eae938d3371dd82baa77793220fa72a8a # Parent 5e00ed18ae325a77509d9f6d4fbbc34712d23d99 Maintenant, les ratures sont fabriquees a partir de 1 qui ont ete transformes pas mal. Les ratures peuvent maintenant etre plus d'une. Proba 0.15 d'effectuer une rature diff -r 5e00ed18ae32 -r 4a29910eae93 transformations/Rature.py --- a/transformations/Rature.py Wed Feb 17 17:45:48 2010 -0500 +++ b/transformations/Rature.py Thu Feb 18 10:53:36 2010 -0500 @@ -2,15 +2,12 @@ # coding: utf-8 ''' -Ajout de rature sur le caractère. La rature peut etre horizontale, verticale -(dans ces deux cas, l'amplacement de la bande est aleatoire) ou sur la diagonale -(et anti-diagonale). +Ajout d'une rature sur le caractère. La rature est en fait un 1 qui recoit une +rotation et qui est ensuite appliqué sur le caractère. Un grossissement, puis deux +erosions sont effectuees sur le 1 afin qu'il ne soit plus reconnaissable. +Il y a des chances d'avoir plus d'une seule rature ! -La largeur de la bande ainsi que sa clarté sont definies a l'aide de complexity -et d'une composante aleatoire. -clarte: 0=blanc et 1=noir - -Il y a 15% d'effectuer une rature +Il y a 15% d'effectuer une rature. Ce fichier prend pour acquis que les images sont donnees une a la fois sous forme de numpy.array de 1024 (32 x 32) valeurs entre 0 et 1. @@ -19,95 +16,196 @@ ''' -import numpy +import numpy, Image, random +import scipy.ndimage.morphology +from pylearn.io import filetensor as ft class Rature(): def __init__(self): - self.largeur=2 #Largeur de la bande - self.deplacement=0 #Deplacement par rapport au milieu - self.orientation=0 #0=horizontal, 1=vertical, 2=oblique - self.clarte=0.5 #Clarte de la ligne appliquee - self.faire=1 #Si ==1, on applique une rature + self.angle=0 #Angle en degre de la rotation (entre 0 et 180) + self.numero=0 #Le numero du 1 choisi dans la banque de 1 + self.gauche=-1 #Le numero de la colonne la plus a gauche contenant le 1 + self.droite=-1 + self.haut=-1 + self.bas=-1 + self.faire=1 #1=on effectue et 0=fait rien + + self.crop_haut=0 + self.crop_gauche=0 #Ces deux valeurs sont entre 0 et 31 afin de definir + #l'endroit ou sera pris le crop dans l'image du 1 + + self.largeur_bande=-1 #La largeur de la bande + self.smooth=-1 #La largeur de la matrice carree servant a l'erosion + self.nb_ratures=-1 #Le nombre de ratures appliques + self.fini=0 #1=fini de mettre toutes les couches 0=pas fini + self.complexity=0 #Pour garder en memoire la complexite si plusieurs couches sont necessaires + + f3 = open('/data/lisa/data/ift6266h10/un_rature.ft') #Doit etre sur le reseau DIRO. + #f3 = open('/home/sylvain/Dropbox/Msc/IFT6266/donnees/un_rature.ft') + #Il faut arranger le path sinon + w=ft.read(f3) + f3.close() + self.d=(w.astype('float'))/255 + + self.patch=self.d[0].reshape((32,32)) #La patch de rature qui sera appliquee sur l'image def get_settings_names(self): - return ['orientation','deplacement','clarte','faire'] + return ['angle','numero','faire','crop_haut','crop_gauche','largeur_bande','smooth','nb_ratures'] def regenerate_parameters(self, complexity): - #Il faut choisir parmis vertical, horizontal et diagonal. - #La methode n'est pas exacte, mais un peu plus rapide que generer un int. - #Complexity n'a rien a voir avec ce choix + - choix=numpy.random.random() - - if choix <0.34: - self.orientation=0 - elif choix <0.67: - self.orientation=1 + self.numero=random.randint(0,4999) #Ces bornes sont inclusives ! + self.fini=0 + self.complexity=complexity + + if float(complexity) > 0: + + self.gauche=self.droite=self.haut=self.bas=-1 #Remet tout a -1 + + self.angle=int(numpy.random.normal(90,100*complexity)) + + self.faire=numpy.random.binomial(1,0.15) ##### 15% d'effectuer une rature ##### + #self.faire=1 #Pour tester seulement + + self.crop_haut=random.randint(0,17) + self.crop_gauche=random.randint(0,17) + if complexity <= 0.25 : + self.smooth=6 + elif complexity <= 0.5: + self.smooth=5 + elif complexity <= 0.75: + self.smooth=4 + else: + self.smooth=3 + self.nb_ratures=int(numpy.random.normal(complexity*2,complexity*1.5))+1 + + #Creation de la "patch" de rature qui sera appliquee sur l'image + if self.faire == 1: + self.get_size() + self.get_image_rot() #On fait la "patch" + else: - self.orientation=2 - - if float(complexity) > 0: - self.largeur=min(32,max(1,int(numpy.ceil(complexity*5)*numpy.random.normal(1,float(complexity)/2)))) - self.clarte=min(1,max(0,complexity*numpy.random.normal(1,float(complexity)/2))) - self.faire=numpy.random.binomial(1,0.15) ##### 15% d'effectuer une rature ##### - else: - self.largeur=0 - self.clarte=0 - self.faire=0 #On ne fait rien !!! + self.faire=0 #On ne fait rien si complexity=0 !! return self._get_current_parameters() + + + def get_image_rot(self): + image2=(self.d[self.numero].reshape((32,32))[self.haut:self.bas,self.gauche:self.droite]) + + im = Image.fromarray(numpy.asarray(image2*255,dtype='uint8')) + + #La rotation et le resize sont de belle qualite afin d'avoir une image nette + im2 = im.rotate(self.angle,Image.BICUBIC,expand=False) + im3=im2.resize((50,50),Image.ANTIALIAS) + + grosse=numpy.asarray(numpy.asarray(im3)/255.0,dtype='float32') + crop=grosse[self.haut:self.haut+32,self.gauche:self.gauche+32] + + self.get_patch(crop) + + def get_patch(self,crop): + + + smooting=numpy.zeros((self.smooth,self.smooth))+1 #Au plus c'est gros, au plus ca rapetisse la ligne + + #Il y a deux erosions afin d'avoir un beau resultat. Pas trop large et + #pas trop mince + trans=scipy.ndimage.morphology.grey_erosion\ + (crop,size=smooting.shape,structure=smooting,mode='wrap') + trans1=scipy.ndimage.morphology.grey_erosion\ + (trans,size=smooting.shape,structure=smooting,mode='wrap') + + + patch_img=Image.fromarray(numpy.asarray(trans1*255,dtype='uint8')) + + patch_img2=patch_img.crop((4,4,28,28)).resize((32,32)) #Pour contrer les effets de bords ! + + trans2=numpy.asarray(numpy.asarray(patch_img2)/255.0,dtype='float32') + + + #Tout ramener entre 0 et 1 + trans2=trans2-trans2.min() #On remet tout positif + trans2=trans2/trans2.max() + + #La rayure a plus de chance d'etre en bas ou oblique le haut a 10h + if random.random() <= 0.5: #On renverse la matrice dans ce cas + for i in xrange(0,32): + self.patch[i,:]=trans2[31-i,:] + else: + self.patch=trans2 + + + + + def get_size(self): + image=self.d[self.numero].reshape((32,32)) + + #haut + for i in xrange(0,32): + for j in xrange(0,32): + if(image[i,j]) != 0: + if self.haut == -1: + self.haut=i + break + if self.haut > -1: + break + + #bas + for i in xrange(31,-1,-1): + for j in xrange(0,32): + if(image[i,j]) != 0: + if self.bas == -1: + self.bas=i + break + if self.bas > -1: + break + + #gauche + for i in xrange(0,32): + for j in xrange(0,32): + if(image[j,i]) != 0: + if self.gauche == -1: + self.gauche=i + break + if self.gauche > -1: + break + + #droite + for i in xrange(31,-1,-1): + for j in xrange(0,32): + if(image[j,i]) != 0: + if self.droite == -1: + self.droite=i + break + if self.droite > -1: + break + def _get_current_parameters(self): - return [self.orientation,self.largeur,self.clarte,self.faire] + return [self.angle,self.numero,self.faire,self.crop_haut,self.crop_gauche,self.largeur_bande,self.smooth,self.nb_ratures] def transform_image(self, image): - if self.faire == 0: + if self.faire == 0: #Rien faire !! return image - if self.orientation == 0: - return self._horizontal(image) - elif self.orientation == 1: - return self._vertical(image) - else: - return self._oblique(image) - - def _horizontal(self,image): - self.deplacement=numpy.random.normal(0,5) - #On s'assure de rester dans l'image - if self.deplacement < -16: #Si on recule trop - self.deplacement = -16 - if self.deplacement+self.largeur > 16: #Si on avance trop - self.deplacement=16-self.largeur - for i in xrange(0,self.largeur): - for j in xrange(0,32): - image[i+15+self.deplacement,j]=min(1,max(image[i+15+self.deplacement,j],self.clarte)) - return image - - def _vertical(self,image): - self.deplacement=numpy.random.normal(0,5) - #On s'assure de rester dans l'image - if self.deplacement < -16: #Si on recule trop - self.deplacement = -16 - if self.deplacement+self.largeur > 16: #Si on avance trop - self.deplacement=16-self.largeur - for i in xrange(0,self.largeur): - for j in xrange(0,32): - image[j,i+15+self.deplacement]=min(1,max(image[j,i+15+self.deplacement],self.clarte)) - return image - - def _oblique(self,image): - decision=numpy.random.random() - D=numpy.zeros((32,32)) #La matrice qui sera additionnee - for i in xrange(int(-numpy.floor(self.largeur/2)),int(numpy.ceil((self.largeur+1)/2))): - D+=numpy.eye(32,32,i) - if decision<0.5: #On met tout sur l'anti-diagonale - D = D[:,::-1] - D*=self.clarte + if self.fini == 0: #S'il faut rajouter des couches + patch_temp=self.patch + for w in xrange(1,self.nb_ratures): + self.regenerate_parameters(self.complexity) + for i in xrange(0,32): + for j in xrange(0,32): + patch_temp[i,j]=max(patch_temp[i,j],self.patch[i,j]) + self.fini=1 + self.patch=patch_temp + for i in xrange(0,32): for j in xrange(0,32): - image[i,j]=min(1,max(image[i,j],D[i,j])) + image[i,j]=max(image[i,j],self.patch[i,j]) + self.patch*=0 #Remise a zero de la patch (pas necessaire) return image @@ -116,27 +214,29 @@ def _load_image(): f = open('/home/sylvain/Dropbox/Msc/IFT6266/donnees/lower_test_data.ft') #Le jeu de donnees est en local. d = ft.read(f) - w=numpy.asarray(d[1]) + w=numpy.asarray(d[0:1000]) return (w/255.0).astype('float') def _test(complexite): img=_load_image() transfo = Rature() - pylab.imshow(img.reshape((32,32))) - pylab.show() - print transfo.get_settings_names() - print transfo.regenerate_parameters(complexite) - img=img.reshape((32,32)) - - img_trans=transfo.transform_image(img) - - pylab.imshow(img_trans.reshape((32,32))) - pylab.show() + for i in xrange(0,10): + img2=img[random.randint(0,1000)] + pylab.imshow(img2.reshape((32,32))) + pylab.show() + print transfo.get_settings_names() + print transfo.regenerate_parameters(complexite) + img2=img2.reshape((32,32)) + + img2_trans=transfo.transform_image(img2) + + pylab.imshow(img2_trans.reshape((32,32))) + pylab.show() if __name__ == '__main__': from pylearn.io import filetensor as ft import pylab - _test(0.8) + _test(1)