Closed danieldoyharzabal closed 7 years ago
Thanks for letting me know about this problem. There are a few things that might cause this, it would be helpful to know the following values when the error happens: len(t1), len(t2), i, len(t1[i]), len(t2[i]) if you can get me these I can investigate further
I could solve the problem with a little help, also add a small GUI with TKinter, I leave the code ... greetings
# -*- coding: utf-8 -*-
"""
Created on Thu Mar 09 19:59:07 2017
@author: Daniel Doyharzabal
"""
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)
salida = 'mosaico.jpeg' #da le nombre al archivo que se crea
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 = []
print 'Leyendo mosaicos de \'%s\'...' % (self.tiles_directory, )
# 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 and small_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
# 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
if len(t1) - len(t2): return -1 #parcheado
for i in range(len(t1)):
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:
# sabemos que este no es el mejor encaje, entonces no tiene sentido continuar con este mosaico
return diff
return diff
def get_best_fit_tile(self, img_data):
best_fit_tile_index = None
min_diff = sys.maxint
tile_index = 0
# va a traves de cada mosaico buscando el mejor encaje para la cuadricula de la imagen representada en 'img_data'
for tile_data in self.tiles_data:
diff = self.__get_tile_diff(img_data, tile_data, min_diff)
if 0 < diff < min_diff: #cambio
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):
# Esta función se ejecuta por los procesos de trabajo, uno en cada núcleo de la CPU
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)
if tile_index:
result_queue.put((img_coords, tile_index))
except KeyboardInterrupt:
break
# deja que result handler sepa que este procesador ha terminado
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
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
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)
# parcheado active_workers = WORKER_COUNT
while True:
try:
img_coords, best_fit_tile_index = result_queue.get()
if img_coords == EOQ_VALUE:
# parcheado active_workers -= 1
# parcheado if not active_workers:
# parcheado break
break
else:
tile_data = all_tile_data_large[best_fit_tile_index]
mosaic.add_tile(tile_data, img_coords)
except KeyboardInterrupt:
break
# parcheado pass
mosaic.save(salida)
print '\nFinalizado, la imagen de salida se encuentra en', salida
def terminal(original_img, tiles, taman, TILE_BLOCK): # terminal determina la parte visual del programa en la terminal de windows
print 'Construyendo imagen, Ctrl+C para abortar...'
original_img_large, original_img_small = original_img
tiles_large, tiles_small = tiles
mosaic = MosaicImage(original_img_large, taman)
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()
try:
# comienza el proceso de construccion del mosaico
p=Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large, taman))
p.start()
# comienza el proceso de encaje de los mosaicos
for n in range(WORKER_COUNT):
Process(target=fit_tiles, args=(work_queue, result_queue, all_tile_data_small)).start()
print("Proceso de construccion inicializado...")
progress = ProgressCounter(mosaic.x_tile_count * mosaic.y_tile_count)
for x in range(mosaic.x_tile_count):
for y in range(mosaic.y_tile_count):
large_box = (x * taman, y * taman, (x + 1) * taman, (y + 1) * taman)
small_box = (x * taman/TILE_BLOCK, y * taman/TILE_BLOCK, (x + 1) * taman/TILE_BLOCK, (y + 1) * taman/TILE_BLOCK)
work_queue.put((list(original_img_small.crop(small_box).getdata()), large_box))
progress.update()
except:
print '\nGuardando imagen parcial, espere...'
finally:
# pone estos valores especiales en cola para dejar saber a los CPU que pueden terminar
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()) # resolulcion de encaje de mosaicos (valores altos mejoran el resultado pero requieren mas procesamiento)
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 de bloque de mosaico
path=tkFileDialog.askdirectory() #seleccionar carpeta de mosaicos
label_6.config(text=path)
archivo=tkFileDialog.askopenfilename() #seleccionar que archivo abrir como imagen principal
label_7.config(text=archivo)
tiles_path=path
img_path=archivo
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)
#-------------------------- Interfaz Grafica ----------------------------------------------#
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)
if __name__ == '__main__': #verifica si el modulo ha sido importado o si es ejecutado como modulo principal (main)
root.mainloop() #ejecuta la GUI
Hi, im working with this script and i found this problem...
The error does not always appear. At first I thought that by removing the pyc file generated by the interpreter, the error would be solved, but it was not.
I'm working on Windows 10, with python 2.7 64 bit.