# HG changeset patch # User SylvainPL # Date 1265297278 18000 # Node ID f8a92292b2991be84c28ae05fd856c3a158102ed # Parent 5deccb161307e17e615e5b405139f417d1d4e003# Parent 05145f4fb60975e7870e9c746224093f56acc3ee merge de 4 fevrier diff -r 5deccb161307 -r f8a92292b299 transformations/affine_transform.py --- /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() + diff -r 5deccb161307 -r f8a92292b299 transformations/gimp_script.py --- 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) diff -r 5deccb161307 -r f8a92292b299 transformations/pipeline.py --- 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 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() + diff -r 5deccb161307 -r f8a92292b299 transformations/run_pipeline.sh --- /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 + + diff -r 5deccb161307 -r f8a92292b299 transformations/slant.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 diff -r 5deccb161307 -r f8a92292b299 transformations/ttf2jpg.py --- 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