changeset 45:f8a92292b299

merge de 4 fevrier
author SylvainPL <sylvain.pannetier.lebeuf@umontreal.ca>
date Thu, 04 Feb 2010 10:27:58 -0500
parents 5deccb161307 (current diff) 05145f4fb609 (diff)
children 48a21d19b8eb
files
diffstat 6 files changed, 370 insertions(+), 121 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transformations/affine_transform.py	Thu Feb 04 10:27:58 2010 -0500
@@ -0,0 +1,69 @@
+#!/usr/bin/python
+# coding: utf-8
+
+'''
+Simple implementation of random affine transformations based on the Python 
+Imaging Module affine transformations.
+
+
+Author: Razvan Pascanu
+'''
+
+import numpy, Image
+
+
+
+class AffineTransformation():
+    def __init__( self, shape = (32,32), seed = None):
+        self.shape = shape
+        self.rng = numpy.random.RandomState(seed)
+
+    def transform(self,NIST_image):
+    
+        im = Image.fromarray( \
+                numpy.asarray(\
+                       NIST_image.reshape(self.shape), dtype='uint8'))
+        # generate random affine transformation
+        # a point (x',y') of the new image corresponds to (x,y) of the old
+        # image where : 
+        #   x' = params[0]*x + params[1]*y + params[2]
+        #   y' = params[3]*x + params[4]*y _ params[5]
+
+        # the ranges are set manually as to look acceptable
+        params = self.rng.uniform(size = 6) -.5
+        params[2] *= 8.
+        params[5] *= 8.
+        params[0] = 1. + params[0]*0.4
+        params[3] = 0. + params[3]*0.4
+        params[1] = 0  + params[1]*0.4
+        params[4] = 1  + params[4]*0.4
+
+        print params
+        nwim = im.transform( (32,32), Image.AFFINE, params)
+        return numpy.asarray(nwim)
+
+
+
+if __name__ =='__main__':
+    print 'random test'
+    
+    from pylearn.io import filetensor as ft
+    import pylab
+
+    datapath = '/data/lisa/data/nist/by_class/'
+
+    f = open(datapath+'digits/digits_train_data.ft')
+    d = ft.read(f)
+    f.close()
+
+
+    transformer = AffineTransformation()
+    id = numpy.random.randint(30)
+    
+    pylab.figure()
+    pylab.imshow(d[id].reshape((32,32)))
+    pylab.figure()
+    pylab.imshow(transformer.transform(d[id]).reshape((32,32)))
+
+    pylab.show()
+
--- a/transformations/gimp_script.py	Wed Feb 03 10:14:33 2010 -0500
+++ b/transformations/gimp_script.py	Thu Feb 04 10:27:58 2010 -0500
@@ -6,75 +6,66 @@
 Auteur: Nicolas Boulanger-Lewandowski
 Date: Hiver 2010
 
-run with:
-gimp -i --batch-interpreter python-fu-eval --batch - < gimp_script.py
+run with: gimp -i --batch-interpreter python-fu-eval --batch - < gimp_script.py
+end with: pdb.gimp_quit(0)
 
-inclure pour terminer gimp à la fin du script:
-pdb.gimp_quit(0)
-
-Décommenter les lignes appropriées pour différents filtres
-Les plug-ins de GIMP et leurs paramètres sont disponibles sous GIMP, menu Help > Plug-in Browser (toujours ignorer le paramètre run-mode).
-Les autres fonctions du programme sont dans la Procedure DataBase (PDB) dans le menu Help > Procedure Browser.
+Implémente le motionblur et le pinch
 '''
 
 from gimpfu import *
 import numpy
 
-class GIMPTransformation():
-    def __init__(self):
-        self.img = gimp.Image(32, 32, GRAY)
-        self.img.disable_undo()
-        self.layer1 = gimp.Layer(self.img, "layer1", 32, 32, GRAY_IMAGE, 100, NORMAL_MODE)
-        self.img.add_layer(self.layer1, 0)
-        self.dest_rgn = self.layer1.get_pixel_rgn(0, 0, 32, 32, True)
+img = gimp.Image(32, 32, GRAY)
+img.disable_undo()
+layer1 = gimp.Layer(img, "layer1", 32, 32, GRAY_IMAGE, 100, NORMAL_MODE)
+img.add_layer(layer1, 0)
+dest_rgn = layer1.get_pixel_rgn(0, 0, 32, 32, True)
+
+def setpix(image):
+    dest_rgn[:,:] = (image.T*255).astype(numpy.uint8).tostring()
+    layer1.flush()
+    layer1.update(0, 0, 32, 32)
+
+def getpix():
+    return numpy.fromstring(dest_rgn[:,:], 'UInt8').astype(numpy.float32).reshape((32,32)).T / 255.0
+
+class GIMP1():
+    def get_settings_names(self):
+        return ['mblur_length', 'mblur_angle', 'pinch']
+    
+    def regenerate_parameters(self, complexity):
+        if complexity:
+            self.mblur_length = abs(int(round(numpy.random.normal(0, 3*complexity))))
+        else:
+            self.mblur_length = 0
+        self.mblur_angle =  int(round(numpy.random.uniform(0,360)))
+        self.pinch = numpy.random.uniform(-complexity, 0.7*complexity)
+
+        return [self.mblur_length, self.mblur_angle, self.pinch]
 
     def transform_image(self, image):
-        ''' si la ligne suivante échoue, upgrader à GIMP v.2.6.7
-        OU utiliser une des 2 versions commentées ci-après (10x plus lent) '''
-        self.dest_rgn[:,:] = (image.T*255).astype(numpy.uint8).tostring()
-
-        #for i in range(32):
-        # self.dest_rgn[i,:] = (image[i,:]*255).astype(numpy.uint8).tostring()
-
-        #for i in range(32):
-        # for j in range(32):
-        #  self.dest_rgn[i,j] = chr(int((image[i,j] *255)))
-
-        self.layer1.flush()
-        self.layer1.update(0, 0, 32, 32)
+        if self.mblur_length or self.pinch:
+            setpix(image)
+            if self.mblur_length:
+                pdb.plug_in_mblur(img, layer1, 0, self.mblur_length, self.mblur_angle, 0, 0)
+            if self.pinch:        
+                pdb.plug_in_whirl_pinch(img, layer1, 0.0, self.pinch, 1.0)
+            image = getpix()
 
-        ''' application des filtres '''
-        #pdb.plug_in_noisify(self.img, self.layer1, 0, 0.4, 0, 0, 0)
-        #pdb.plug_in_c_astretch(self.img, self.layer1)
-        #pdb.plug_in_emboss(self.img, self.layer1, 10, 30, 5, 0)
-        #pdb.plug_in_applylens(self.img, self.layer1, 2, 1, 0, 0)
-        #pdb.plug_in_blur(self.img, self.layer1)
-        pdb.plug_in_gauss_rle(self.img, self.layer1, 9, 1, 0)
-
-        ''' si la ligne suivante échoue, upgrader à GIMP v.2.6.7
-        OU utiliser la version commentée ci-après (10x plus lent) '''
-        image = numpy.fromstring(self.dest_rgn[:,:], 'UInt8').reshape((32,32)).T / 255.0
-
-        #image = image*0
-        #for i in range(32):
-        # for j in range(32):
-        #  image[i,j] = ord(self.dest_rgn[i,j]) /255.0
-        
         return image
 
-# TEST
-if 1:
-    im = numpy.zeros((32,32))
-    for i in range(32):
-     for j in range(32):
-      im[i,j] = i*j/(32.*32.)
+# test
+if __name__ == '__main__':
+    import Image
+    im = numpy.asarray(Image.open("a.bmp").convert("L")) / 255.0
 
-    test = GIMPTransformation()
-    #for i in range(100):
+    test = GIMP1()
+    print test.get_settings_names(), '=', test.regenerate_parameters(1)
+    #for i in range(1000):
     im = test.transform_image(im)
-    
+
     import pylab
-    pylab.imshow(im)
+    pylab.imshow(im, pylab.matplotlib.cm.Greys_r)
     pylab.show()
 
     pdb.gimp_quit(0)
--- a/transformations/pipeline.py	Wed Feb 03 10:14:33 2010 -0500
+++ b/transformations/pipeline.py	Thu Feb 04 10:27:58 2010 -0500
@@ -1,57 +1,208 @@
+#!/usr/bin/python
+# coding: utf-8
+
 from __future__ import with_statement
 
-import sys, os
+import sys, os, getopt
 import numpy
 import filetensor as ft
 import random
 
-BATCH_SIZE = 100
+# This is intended to be run as a GIMP script
+from gimpfu import *
 
-#import <modules> and stuff them in mods below
+DEBUG = True
+BATCH_SIZE = 100
+DEFAULT_NIST_PATH = '/data/lisa/data/nist/by_class/all/all_train_data.ft'
+ARGS_FILE = os.environ['PIPELINE_ARGS_TMPFILE']
+
+if DEBUG:
+    import pylab
+    pylab.ion()
 
-mods = []
+#from add_background_image import AddBackground
+#from affine_transform import AffineTransformation
+#from PoivreSel import PoivreSel
+from thick import Thick
+#from BruitGauss import BruitGauss
+#from gimp_script import GIMPTransformation
+#from Rature import Rature
+#from contrast Contrast
+from local_elastic_distortions import LocalElasticDistorter
+from slant import Slant
+
+MODULE_INSTANCES = [Thick(), LocalElasticDistorter(), Slant()]
 
-# DANGER: HIGH VOLTAGE -- DO NOT EDIT BELOW THIS LINE
-# -----------------------------------------------------------
+class Pipeline():
+    def __init__(self, modules, num_batches, batch_size, image_size=(32,32)):
+        self.modules = modules
+        self.num_batches = num_batches
+        self.batch_size = batch_size
+        self.num_params_stored = 0
+        self.image_size = image_size
+
+        self.init_memory()
+
+    def init_num_params_stored(self):
+        # just a dummy call to regenerate_parameters() to get the
+        # real number of params (only those which are stored)
+        self.num_params_stored = 0
+        for m in self.modules:
+            self.num_params_stored += len(m.regenerate_parameters(0.0))
+
+    def init_memory(self):
+        self.init_num_params_stored()
 
-outf = sys.argv[1]
-paramsf = sys.argv[2]
-dataf = '/data/lisa/data/nist/by_class/all/all_train_data.ft'
-if len(sys.argv) >= 4:
-    dataf = sys.argv[3]
+        total = (self.num_batches + 1) * self.batch_size
+        num_px = self.image_size[0] * self.image_size[1]
+
+        self.res_data = numpy.empty((total, num_px))
+        self.params = numpy.empty((total, self.num_params_stored))
+
+    def run(self, batch_iterator, complexity_iterator):
+        img_size = self.image_size
+
+        for batch_no, batch in enumerate(batch_iterator):
+            complexity = complexity_iterator.next()
+
+            assert len(batch) == self.batch_size
+
+            for img_no, img in enumerate(batch):
+                sys.stdout.flush()
+                global_idx = batch_no*self.batch_size + img_no
+
+                img = img.reshape(img_size)
 
-train_data = open(dataf, 'rb')
+                param_idx = 0
+                for mod in self.modules:
+                    # This used to be done _per batch_,
+                    # ie. out of the "for img" loop                   
+                    p = mod.regenerate_parameters(complexity)
+                    self.params[global_idx, param_idx:param_idx+len(p)] = p
+                    param_idx += len(p)
 
-dim = tuple(ft._read_header(train_data)[3])
+                    img = mod.transform_image(img)
+
+                self.res_data[global_idx] = \
+                        img.reshape((img_size[0] * img_size[1],))*255
 
-res_data = numpy.empty(dim, dtype=numpy.int8)
+                pylab.imshow(img)
+                pylab.draw()
+
+    def write_output(self, output_file_path, params_output_file_path):
+        with open(output_file_path, 'wb') as f:
+            ft.write(f, self.res_data)
 
-all_settings = ['complexity']
+        numpy.save(params_output_file_path, self.params)
+                
+
+##############################################################################
+# COMPLEXITY ITERATORS
+# They're called once every batch, to get the complexity to use for that batch
+# they must be infinite (should never throw StopIteration when calling next())
 
-for mod in mods:
-    all_settings += mod.get_settings_names()
+# probability of generating 0 complexity, otherwise
+# uniform over 0.0-max_complexity
+def range_complexity_iterator(probability_zero, max_complexity):
+    assert max_complexity <= 1.0
+    n = numpy.random.uniform(0.0, 1.0)
+    while True:
+        if n < probability_zero:
+            yield 0.0
+        else:
+            yield numpy.random.uniform(0.0, max_complexity)
+
+##############################################################################
+# DATA ITERATORS
+# They can be used to interleave different data sources etc.
+
+class NistData():
+    def __init__(self, ):
+        nist_path = DEFAULT_NIST_PATH
+        self.train_data = open(nist_path, 'rb')
+        self.dim = tuple(ft._read_header(self.train_data)[3])
 
-params = numpy.empty(((dim[0]/BATCH_SIZE)+1, len(all_settings)))
+def just_nist_iterator(nist, batch_size, stop_after=None):
+    for i in xrange(0, nist.dim[0], batch_size):
+        nist.train_data.seek(0)
+        yield ft.read(nist.train_data, slice(i, i+batch_size)).astype(numpy.float32)/255
+
+        if not stop_after is None and i >= stop_after:
+            break
+
+##############################################################################
+# MAIN
+
+def usage():
+    print '''
+Usage: run_pipeline.sh [-m ...] [-z ...] [-o ...] [-p ...]
+    -m, --max-complexity: max complexity to generate for a batch
+    -z, --probability-zero: probability of using complexity=0 for a batch
+    -o, --output-file: full path to file to use for output of images
+    -p, --params-output-file: path to file to output params to
+    '''
+
+# See run_pipeline.py
+def get_argv():
+    with open(ARGS_FILE) as f:
+        args = [l.rstrip() for l in f.readlines()]
+    return args
 
-for i in xrange(0, dim[0], BATCH_SIZE):
-    train_data.seek(0)
-    imgs = ft.read(train_data, slice(i, i+BATCH_SIZE)).astype(numpy.float32)/255
-    
-    complexity = random.random()
-    p = i/BATCH_SIZE
-    j = 1
-    for mod in mods:
-        par = mod.regenerate_parameters(complexity)
-        params[p, j:j+len(par)] = par
-        j += len(par)
-    
-    for k in range(imgs.shape[0]):
-        c = imgs[k].reshape((32, 32))
-        for mod in mods:
-            c = mod.transform_image(c)
-        res_data[i+k] = c.reshape((1024,))*255
+# Might be called locally or through dbidispatch. In all cases it should be
+# passed to the GIMP executable to be able to use GIMP filters.
+# Ex: 
+def main():
+    max_complexity = 0.5 # default
+    probability_zero = 0.1 # default
+    output_file_path = None
+    params_output_file_path = None
+    stop_after = None
 
-with open(outf, 'wb') as f:
-    ft.write(f, res_data)
+    try:
+        opts, args = getopt.getopt(get_argv(), "m:z:o:p:s:", ["max-complexity=", "probability-zero=", "output-file=", "params-output-file=", "stop-after="])
+    except getopt.GetoptError, err:
+        # print help information and exit:
+        print str(err) # will print something like "option -a not recognized"
+        usage()
+        sys.exit(2)
+    output = None
+    verbose = False
+    for o, a in opts:
+        if o in ('-m', '--max-complexity'):
+            max_complexity = float(a)
+            assert max_complexity >= 0.0 and max_complexity <= 1.0
+        elif o in ("-z", "--probability-zero"):
+            probability_zero = float(a)
+            assert probability_zero >= 0.0 and probability_zero <= 1.0
+        elif o in ("-o", "--output-file"):
+            output_file_path = a
+        elif o in ('-p', "--params-output-file"):
+            params_output_file_path = a
+        elif o in ('-s', "--stop-after"):
+            stop_after = int(a)
+        else:
+            assert False, "unhandled option"
 
-numpy.save(paramsf, params)
+    if output_file_path == None or params_output_file_path == None:
+        print "Must specify both output files."
+        print
+        usage()
+        sys.exit(2)
+
+    nist = NistData()
+    num_batches = nist.dim[0]/BATCH_SIZE
+    if stop_after:
+        num_batches = stop_after
+    pl = Pipeline(modules=MODULE_INSTANCES, num_batches=num_batches, batch_size=BATCH_SIZE, image_size=(32,32))
+    cpx_it = range_complexity_iterator(probability_zero, max_complexity)
+    batch_it = just_nist_iterator(nist, BATCH_SIZE, stop_after)
+
+    pl.run(batch_it, cpx_it)
+    pl.write_output(output_file_path, params_output_file_path)
+
+main()
+
+pdb.gimp_quit(0)
+pylab.ioff()
+pylab.show()
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/transformations/run_pipeline.sh	Thu Feb 04 10:27:58 2010 -0500
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+# This is one _ugly_ hack, but I couldn't figure out how
+# to cleanly pass command line options to the script if
+# invoking using the "gimp --batch < script.py" syntax
+
+# Basically I create a temp file, put the args into it,
+# then the script gets the filename and reads back the
+# args
+
+export PIPELINE_ARGS_TMPFILE=`mktemp`
+
+for arg in "$@"
+do
+	echo $arg >> $PIPELINE_ARGS_TMPFILE
+done
+
+gimp -i --batch-interpreter python-fu-eval --batch - < pipeline.py
+
+
--- a/transformations/slant.py	Wed Feb 03 10:14:33 2010 -0500
+++ b/transformations/slant.py	Thu Feb 04 10:27:58 2010 -0500
@@ -6,8 +6,7 @@
 
 this module add a slant effect to the image. 
 
-To obtain the slant effect, each row of the array is shifted proportionately by a step
-controlled by the complexity.
+To obtain the slant effect, each row of the array is shifted proportionately by a step controlled by the complexity.
 
 '''
 
@@ -74,19 +73,26 @@
             
 
 # Test function
+# Load an image in local and create several samples of the effect on the
+# original image with different parameter. All the samples are saved in a single image, the 1st image being the original.
+
 def test_slant():
-    img_name = "3.png"
-    dest_img_name = "slanted.png"
-    im = Image.open(img_name,)
+    import scipy
+    img_name = "test_img/mnist_0.png"
+    dest_img_name = "test_img/slanted.png"
+    nb_samples = 10
+    im = Image.open(img_name)
     im = im.convert("L")
     image = numpy.asarray(im)
 
+    image_final = image
+    slant = Slant()	
+    for i in range(nb_samples):
+        slant.regenerate_parameters(1)
+        image_slant = slant.transform_image(image)
+        image_final = scipy.hstack((image_final,image_slant))
 
-    slant = Slant()
-    slant.regenerate_parameters(1)
-    image = slant.transform_image(image)
-
-    im = Image.fromarray(image.astype('uint8'), "L")
+    im = Image.fromarray(image_final.astype('uint8'), "L")
     im.save(dest_img_name)
 
 # Test
--- a/transformations/ttf2jpg.py	Wed Feb 03 10:14:33 2010 -0500
+++ b/transformations/ttf2jpg.py	Thu Feb 04 10:27:58 2010 -0500
@@ -8,7 +8,7 @@
     Author: Guillaume Sicard
 '''
 
-import sys, os, fnmatch
+import sys, os, fnmatch, random
 import Image, ImageFont, ImageDraw, numpy
 
 class ttf2jpg():
@@ -19,6 +19,13 @@
         self.font_file = font_file
         self.image_dir = './images/'
         self.pattern = '*.ttf'
+        self.char_list = []
+        for i in range(0,26):
+            self.char_list.append(chr(ord('a') + i) )
+        for i in range(0,26):
+            self.char_list.append(chr(ord('A') + i) )
+        for i in range(0,10):
+            self.char_list.append(chr(ord('0') + i) )
 
     # get font name
     def get_settings_names(self):
@@ -33,6 +40,13 @@
         else:
             image.show()
 
+    # set a random font for character generation
+    def set_random_font(self):
+        files = os.listdir(self.font_dir)
+        font_files = fnmatch.filter(files, self.pattern)
+        i = random.randint(0, len(font_files) - 1)
+        self.font_file = self.font_dir + font_files[i]
+
     # return a picture array of "text" with font "font_file"
     def create_image(self, text):
          # create a w x h black picture, and a drawing space
@@ -53,31 +67,27 @@
 
     # write all the letters and numbers into pictures
     def process_font(self):
-        for i in range(0, 26):
-            image = self.create_image(chr(ord('a') + i))
-            self.save_image(image, self.image_dir + chr(ord('a') + i) + '-' + os.path.basename(self.font_file) + '.jpg')
-            sys.stdout.write('.')
-            sys.stdout.flush()
-        for i in range(0, 26):
-            image = self.create_image(chr(ord('A') + i))
-            self.save_image(image, self.image_dir + chr(ord('A') + i) + '-' + os.path.basename(self.font_file) + '.jpg')
+        for i in range(0, len(self.char_list) ):
+            image = self.create_image(self.char_list[i])
+            self.save_image(image, self.image_dir + self.char_list[i] + '-' + os.path.basename(self.font_file) + '.jpg')
             sys.stdout.write('.')
             sys.stdout.flush()
-        for i in range(0, 10):
-            image = self.create_image(chr(ord('0') + i))
-            self.save_image(image, self.image_dir + chr(ord('0') + i) + '-' + os.path.basename(self.font_file) + '.jpg')
-            sys.stdout.write('.')
-            sys.stdout.flush()
-        return (26 + 26 + 10)
+        return (len(self.char_list))
 
     # generate the character from the font_file and returns a numpy array
-    def generate_image(self, character, font_file = ''):
+    def generate_image_from_char(self, character, font_file = ''):
         if (font_file != ''):
             self.font_file = font_file
 
         return self.create_image(character)
 
-    # test method, create character images for all fonts in "font_dir"
+    # generate random character from random font file as a numpy array
+    def generate_image(self):
+        self.set_random_font()
+        i = random.randint(0, len(self.char_list) - 1)
+        return self.generate_image_from_char(self.char_list[i])
+
+    # test method, create character images for all fonts in "font_dir" in dir "image_dir"
     def test(self):
         import time
 
@@ -106,4 +116,6 @@
 if __name__ == '__main__':
 
     myttf2jpg = ttf2jpg()
-    myttf2jpg.test()
+    #myttf2jpg.test()
+    image = myttf2jpg.generate_image()
+    myttf2jpg.save_image(image, '')
\ No newline at end of file