view data_generation/transformations/Rature.py @ 239:42005ec87747

Mergé (manuellement) les changements de Sylvain pour utiliser le code de dataset d'Arnaud, à cette différence près que je n'utilse pas les givens. J'ai probablement une approche différente pour limiter la taille du dataset dans mon débuggage, aussi.
author fsavard
date Mon, 15 Mar 2010 18:30:21 -0400
parents 1f5937e9e530
children
line wrap: on
line source

#!/usr/bin/python
# coding: utf-8

'''
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 !

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.

Sylvain Pannetier Lebeuf dans le cadre de IFT6266, hiver 2010

'''

import numpy, Image, random
import scipy.ndimage.morphology
from pylearn.io import filetensor as ft


class Rature():
   
    def __init__(self,seed=1256):
        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
        self.seed=seed
        
        #numpy.random.seed(self.seed)
        
        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 ['angle','numero','faire','crop_haut','crop_gauche','largeur_bande','smooth','nb_ratures']
    
    def get_seed(self):
        return self.seed

    def regenerate_parameters(self, complexity,next_rature = False):
        
        
        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 #####
            if next_rature:
                self.faire = 1
            #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
            
            p = numpy.random.rand()
            if p < 0.5:
                self.nb_ratures= 1
            else:
                if p < 0.8:
                    self.nb_ratures = 2
                else:
                    self.nb_ratures = 3
            
            #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.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.ones((self.smooth,self.smooth))
        #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.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: #Rien faire !!
            return image
        
        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,1)
                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]=max(image[i,j],self.patch[i,j])
        self.patch*=0   #Remise a zero de la patch (pas necessaire)
        return image


#---TESTS---

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[0:1000])
    return (w/255.0).astype('float')

def _test(complexite):
    img=_load_image()
    transfo = Rature()
    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(1)