Hola gente, estoy diseñando una GUI con Tkinter en python 2.7 para un proyecto ya desarrollado y que esta funcionando. El proyecto realiza un fotomosaico a partir de un banco de imágenes, lo realiza a través de la libreria PIL.
El script al trabajar con procesamiento de imágenes utiliza la librería multiprocessing para utilizar todos los núcleos disponibles del CPU (o al menos eso creo).
El problema surge cuando después de modificar el código para obtener los datos desde TKinter, obtengo un error que me clava el script pero no genera ninguna advertencia ni nada por consola. Por las pruebas que estuve haciendo, el error (al parecer) esta en los argumentos de la llamada de ejecución del proceso. Quisiera por favor si pueden, darle una mirada y darme su opinión...
El script falla llegado a este punto:
Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large, tamanio)).start()
###Los valores para prueba que estoy utilizando para correr el script son= Recortar Mosaicos: 50, Resolucion Mosaicos: 4, Agrandamiento: 6
ahi se cuelga y para cerrar la interfaz windows me pide finalizar python.
me siento bastante frustrado con esto porquehace bastante tiempo intento hacerlo funcionar sin resultados . Muchas gracias desde ya...
El script al trabajar con procesamiento de imágenes utiliza la librería multiprocessing para utilizar todos los núcleos disponibles del CPU (o al menos eso creo).
El problema surge cuando después de modificar el código para obtener los datos desde TKinter, obtengo un error que me clava el script pero no genera ninguna advertencia ni nada por consola. Por las pruebas que estuve haciendo, el error (al parecer) esta en los argumentos de la llamada de ejecución del proceso. Quisiera por favor si pueden, darle una mirada y darme su opinión...
El script falla llegado a este punto:
Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large, tamanio)).start()
###Los valores para prueba que estoy utilizando para correr el script son= Recortar Mosaicos: 50, Resolucion Mosaicos: 4, Agrandamiento: 6
ahi se cuelga y para cerrar la interfaz windows me pide finalizar python.
me siento bastante frustrado con esto porquehace bastante tiempo intento hacerlo funcionar sin resultados . Muchas gracias desde ya...
Código:
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 09 19:59:07 2017
@author: Daniel
"""
#Los valores para prueba que estoy utilizando son Recortar Mosaicos: 50, Resolucion Mosaicos: 4, Agrandamiento: 6
import sys, os, tkFileDialog, unicodedata
from multiprocessing import Process, Queue, cpu_count
from Tkinter import *
from PIL import Image
WORKER_COUNT = max(cpu_count() - 1, 1) #selecciona el maximo entre 1 y 1 menos el numero de procesador
EOQ_VALUE = None #inicializa una variable como un objeto del tipo None (nada)
class TileProcessor:
def __init__(self, tiles_directory, tamanio, ENLARGEMENT, TILE_BLOCK_SIZE):
self.tiles_directory = tiles_directory
self.tamanio=tamanio
self.ENLARGEMENT=ENLARGEMENT
self.TILE_BLOCK_SIZE=TILE_BLOCK_SIZE
def __process_tile(self, tile_path):
try:
img = Image.open(tile_path)
# los mosaicos deben ser cuadrados, entonces seleciona el minimo entre largo y ancho y los recorta
w = img.size[0]
h = img.size[1]
min_dimension = min(w, h)
w_crop = (w - min_dimension) / 2
h_crop = (h - min_dimension) / 2
img = img.crop((w_crop, h_crop, w - w_crop, h - h_crop))
large_tile_img = img.resize((self.tamanio, self.tamanio), Image.ANTIALIAS)
small_tile_img = img.resize((self.tamanio/self.TILE_BLOCK_SIZE, self.tamanio/self.TILE_BLOCK_SIZE), Image.ANTIALIAS)
return (large_tile_img.convert('RGB'), small_tile_img.convert('RGB'))
except:
return (None, None)
def get_tiles(self):
large_tiles = []
small_tiles = []
#i=1
print 'Leyendo mosaicos de \'%s\'...' % (self.tiles_directory, )
#print (tamanio)
# se buscan los mosaicos recursivamente en las subcarpetas de la direccion
for root, subFolders, files in os.walk(self.tiles_directory):
for tile_name in files:
tile_path = os.path.join(root, tile_name)
large_tile, small_tile = self.__process_tile(tile_path)
if large_tile:
large_tiles.append(large_tile)
small_tiles.append(small_tile)
print 'Se procesaron %s mosaicos.' % (len(large_tiles),)
return (large_tiles, small_tiles)
class TargetImage:
def __init__(self, image_path, ENLARGEMENT, TILE_BLOCK_SIZE, tamanio):
self.image_path = image_path
self.ENLARGEMENT=ENLARGEMENT
self.TILE_BLOCK_SIZE=TILE_BLOCK_SIZE
self.tamanio=tamanio
def get_data(self):
print 'Procesando imagen principal...'
img = Image.open(self.image_path)
w = img.size[0] * self.ENLARGEMENT #tamaño ancho
h = img.size[1] * self.ENLARGEMENT #tamaño alto
large_img = img.resize((w, h), Image.ANTIALIAS) #retorna una copia cambiada de tamaño y filtrada con antialias
w_diff = (w % self.tamanio)/2
h_diff = (h % self.tamanio)/2
print()
# si es necesario recorta la imagen ligeramente para utilizar un numero entero de mosaicos horizontales y verticales
if w_diff or h_diff:
large_img = large_img.crop((w_diff, h_diff, w - w_diff, h - h_diff)) #recorta la imagen de cada lado es decir 4 datos necesarios
small_img = large_img.resize((w/self.TILE_BLOCK_SIZE, h/self.TILE_BLOCK_SIZE), Image.ANTIALIAS)
image_data = (large_img.convert('RGB'), small_img.convert('RGB'))
print 'Imagen principal procesada.'
return image_data
class TileFitter:
def __init__(self, tiles_data):
self.tiles_data = tiles_data
def __get_tile_diff(self, t1, t2, bail_out_value):
diff = 0
for i in range(len(t1)):
#diff += (abs(t1[i][0] - t2[i][0]) + abs(t1[i][1] - t2[i][1]) + abs(t1[i][2] - t2[i][2]))
diff += ((t1[i][0] - t2[i][0])**2 + (t1[i][1] - t2[i][1])**2 + (t1[i][2] - t2[i][2])**2)
if diff > bail_out_value:
# we know already that this isnt going to be the best fit, so no point continuing with this tile
return diff
return diff
def get_best_fit_tile(self, img_data):
best_fit_tile_index = None
min_diff = sys.maxint
tile_index = 0
# go through each tile in turn looking for the best match for the part of the image represented by 'img_data'
for tile_data in self.tiles_data:
diff = self.__get_tile_diff(img_data, tile_data, min_diff)
if diff < min_diff:
min_diff = diff
best_fit_tile_index = tile_index
tile_index += 1
return best_fit_tile_index
def fit_tiles(work_queue, result_queue, tiles_data):
# this function gets run by the worker processes, one on each CPU core
tile_fitter = TileFitter(tiles_data)
while True:
try:
img_data, img_coords = work_queue.get(True)
if img_data == EOQ_VALUE:
break
tile_index = tile_fitter.get_best_fit_tile(img_data)
result_queue.put((img_coords, tile_index))
except KeyboardInterrupt:
pass
# let the result handler know that this worker has finished everything
result_queue.put((EOQ_VALUE, EOQ_VALUE))
class ProgressCounter:
def __init__(self, total):
self.total = total
self.counter = 0
def update(self):
self.counter += 1
sys.stdout.write("Progress: %s%% %s" % (100 * self.counter / self.total, "\r"))
sys.stdout.flush();
class MosaicImage:
def __init__(self, original_img, tamanio):
self.tamanio=tamanio
print(tamanio)
self.image = Image.new(original_img.mode, original_img.size)
self.x_tile_count = original_img.size[0] / self.tamanio
self.y_tile_count = original_img.size[1] / self.tamanio
self.total_tiles = self.x_tile_count * self.y_tile_count
print('ajam..')
def add_tile(self, tile_data, coords):
img = Image.new('RGB', (self.tamanio, self.tamanio))
img.putdata(tile_data)
self.image.paste(img, coords)
def save(self, path):
self.image.save(path)
def build_mosaic(result_queue, all_tile_data_large, original_img_large, tamanio):
mosaic = MosaicImage(original_img_large, tamanio)
active_workers = WORKER_COUNT
print('fuera')
while True:
try:
img_coords, best_fit_tile_index = result_queue.get()
if img_coords == EOQ_VALUE:
print('dentro')
active_workers -= 1
if not active_workers:
break
else:
print('else')
tile_data = all_tile_data_large[best_fit_tile_index]
mosaic.add_tile(tile_data, img_coords)
except KeyboardInterrupt:
pass
mosaic.save(salida)
print '\nFinalizado, la imagen de salida se encuentra en', salida
def terminal(original_img, tiles, tamanio, TILE_BLOCK_SIZE): # terminal determina la parte visual del programa en la terminal de windows
print 'Construyendo imagen, Ctrl+C para abortar...'
tamanio=tamanio
original_img_large, original_img_small = original_img
tiles_large, tiles_small = tiles
mosaic = MosaicImage(original_img_large, tamanio)
all_tile_data_large = map(lambda tile : list(tile.getdata()), tiles_large)
all_tile_data_small = map(lambda tile : list(tile.getdata()), tiles_small)
work_queue = Queue(WORKER_COUNT)
result_queue = Queue()
print('aqui')
try:
print('hola hola')
# start the worker processes that will build the mosaic image
#todo funciona perfecto hasta este punto... luego de esta orden de ejecutar este proceso el script falla...
Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large, tamanio)).start()
print('hola')
# start the worker processes that will perform the tile fitting
for n in range(WORKER_COUNT):
Process(target=fit_tiles, args=(work_queue, result_queue, all_tile_data_small)).start()
progress = ProgressCounter(mosaic.x_tile_count * mosaic.y_tile_count)
print('hola 2')
for x in range(mosaic.x_tile_count):
for y in range(mosaic.y_tile_count):
large_box = (x * tamanio, y * tamanio, (x + 1) * tamanio, (y + 1) * tamanio)
small_box = (x * tamanio/TILE_BLOCK_SIZE, y * tamanio/TILE_BLOCK_SIZE, (x + 1) * tamanio/TILE_BLOCK_SIZE, (y + 1) * tamanio/TILE_BLOCK_SIZE)
work_queue.put((list(original_img_small.crop(small_box).getdata()), large_box))
progress.update()
except:
print '\nGuardando imagen parcial, espere...'
finally:
print('aqui tambien')
# put these special values onto the queue to let the workers know they can terminate
for n in range(WORKER_COUNT):
work_queue.put((EOQ_VALUE, EOQ_VALUE))
def mosaic(): # definicion del metodo mosaic
tamanio = int(entero1.get())# alto y ancho de los mosaicos en pixeles
TILE_MATCH_RES = int(entero2.get()) # tile matching resolution (higher values give better fit but require more processing)
ENLARGEMENT = int(entero3.get()) # la imagen mosaico sera esta cantidad de veces mas grande (en largo y ancho) que la imagen original
TILE_BLOCK_SIZE = tamanio / max(min(TILE_MATCH_RES, tamanio), 1) #tamaño del bloque final
os.remove('C:\Users\Daniel Doyharzabal\Desktop\Imagenes\principal.pyc')
salida = 'mosaico.jpeg' #da le nombre al archivo que se crea
path=tkFileDialog.askdirectory()
label_6.config(text=path)
archivo=tkFileDialog.askopenfilename()
label_7.config(text=archivo)
tiles_path=path #"C:/ImageOutput"
img_path=archivo #"C:/nene.jpg"
print(tiles_path)
print(img_path)
tiles_data = TileProcessor(tiles_path, tamanio, ENLARGEMENT, TILE_BLOCK_SIZE).get_tiles() # utiliza la direccion de los mosaicos de entrada para iniciar el procesamiento
image_data = TargetImage(img_path, ENLARGEMENT, TILE_BLOCK_SIZE, tamanio).get_data() #
terminal(image_data, tiles_data, tamanio, TILE_BLOCK_SIZE)
if __name__ == '__main__': #verifica si el modulo ha sido importado o si es ejecutado como modulo principal (main)
root=Tk()
root.title("Fotomosaico con Python")
root.geometry('650x400+200+200')
label_1= Label(root, text="Recortar Mosaicos")
label_2= Label(root, text="Resolucion Mosaicos")
label_3= Label(root, text="Agrandamiento")
label_4= Label(root, text="Direccion de Mosaicos")
label_5= Label(root, text="Direccion de Imagen")
label_6= Label(root, text="...")
label_7= Label(root, text="...")
label_8= Label(root, text="...")
x=StringVar()
y=StringVar()
z=StringVar()
entero1= Entry(root, textvariable='x')
entero2= Entry(root, textvariable='y')
entero3= Entry(root, textvariable='z')
label_1.grid(row=0, column=0, sticky=E)
label_2.grid(row=1, column=0, sticky=E)
label_3.grid(row=2, column=0, sticky=E)
label_4.grid(row=3, column=0, sticky=E)
label_5.grid(row=4, column=0, sticky=E)
label_6.grid(row=3, column=1, sticky=W) #direccion mosaicos
label_7.grid(row=4, column=1, sticky=W) #direccion imagen
entero1.grid(row=0, column=1)
entero2.grid(row=1, column=1)
entero3.grid(row=2, column=1)
boton_3=Button(root, text="Aceptar", command= mosaic )
boton_3.grid(column= 4, row=5)
root.mainloop() #ejecuta la GUI
else :
print ('fallo')