Esempio n. 1
0
	def __init__(self, input_PIL_img, lib_Lab_vectors, photoLog, cellLog, subimage, progress=0):
		# Set instance variables
		self.input = input_PIL_img
		self.photoLog = photoLog # The photoLog, cellLog, and progress should probably be in it's own information expert
		self.cellLog = cellLog
		self.progress = int(progress)
		self.ImgAnalyzer = ImageAnalyzer()
		self.ImgFinder = ImageFinder(lib_Lab_vectors)
		self.ImgLoader = ImageLoader()
		self.subimage = subimage
Esempio n. 2
0
class Photomosaic:
	# Variables
	total_rows = 0
	total_cols = 0
	progress = 0
	photoLog = []
	cellLog = []
	photos = {}
	subimage = {}
	
	# Convenience objects
	ImgAnalyzer = None
	ImgFinder = None
	ImgLoader = None

	# The input image to analyze
	input = None
	input_grid_width = 0
	input_grid_height = 0
	input_cell_width = 0
	input_cell_height = 0
	
	# The output photomosiac based on the input image
	output = None
	output_mag = 1
	output_grid_width = 0
	output_grid_height = 0
	output_cell_width = 0
	output_cell_height = 0
	
	# Constructor
	def __init__(self, input_PIL_img, lib_Lab_vectors, photoLog, cellLog, subimage, progress=0):
		# Set instance variables
		self.input = input_PIL_img
		self.photoLog = photoLog # The photoLog, cellLog, and progress should probably be in it's own information expert
		self.cellLog = cellLog
		self.progress = int(progress)
		self.ImgAnalyzer = ImageAnalyzer()
		self.ImgFinder = ImageFinder(lib_Lab_vectors)
		self.ImgLoader = ImageLoader()
		self.subimage = subimage
		
	# Calculate all of the closests photos in the photomosaic
	def calculate_photomosaic(self, rows, cols):
		# Initialize input image
		self._input_dims(int(rows), int(cols))
		
		# Find the initial coordinates
		row = int(len(self.cellLog) / self.total_cols)
		col = int(len(self.cellLog) % self.total_cols)
		x0 = col*self.input_cell_width
		y0 = row*self.input_cell_height
		
		# Traverse the cells, and call "_process_cell" for each one of them
		self._traverse_cells(self._process_cell, x0, y0)
		
	# Create the photomosiac - currently only works with a square (rows=cols)
	def create_photomosaic(self, rows, cols, mag):
		# Initialize input and output images
		self._input_dims(int(rows), int(cols))
		self._output_dims(float(mag))
		
		# Find the initial coordinates to start pasting cells
		row = int(self.progress / self.total_cols)
		col = int(self.progress % self.total_cols)
		x0 = col*self.input_cell_width
		y0 = row*self.input_cell_height
		
		# Traverse the cells, and call "_paste_cell" for each one of them
		self._traverse_cells(self._paste_cell, x0, y0)
		
		# Return the ouput image as a base64 encoded string
		return self.base64()
		
	# Return the output image as a base64 encoded string
	def base64(self):
		file = StringIO()
		self.output.save(file, "png")
		return b64encode(file.getvalue())
		
	# Traverse the cells
	def _traverse_cells(self, fnc_process_cell, x0=0, y0=0):
		for y in range(y0, self.input_grid_height, self.input_cell_height):
			if y != y0: x0 = 0 # Only start in the middle of the grid on the first iteration
			for x in range(x0, self.input_grid_width, self.input_cell_width):
				fnc_process_cell(x, y)
				self.progress += 1

	# Analyze the input cell, find the best match, and add it to the cell log
	def _process_cell(self, x0, y0):
		labVec = self.ImgAnalyzer.getLABVector(self.input, x0, y0, self.input_cell_width, self.input_cell_height)
		closest_photoId = self.ImgFinder.closest_match(labVec)
		self.cellLog.append(closest_photoId)
		
	# Download the image and paste it into the output image
	def _paste_cell(self, x0, y0):
		# Translate the x and y coordinates into a cellLog index and find the photoId
		row = int(y0/self.input_cell_height)
		col = int(x0/self.input_cell_width)
		cellLogIndex = row*self.total_cols + col
		photoId = self.cellLog[cellLogIndex]
		
		# Get the image associated with the photo
		if photoId in self.photos:
			# We already have the image, so grab it from the dictionary
			closest_img = self.photos[photoId]
		else:
			# We haven't previously downloaded the image, so get it from Facebook
			closest_img = self.ImgLoader.load_url_image(self._get_url(photoId))
			
			# Crop the image and resize it into a square
			closest_img = self._crop_resize_square(closest_img)
			
			# Add the image to the photos dictionary
			self.photos[photoId] = closest_img
		
		# Now that the image has been found, paste it into the corresponding output cell
		self.output.paste( closest_img, (col*self.output_cell_width, row*self.output_cell_height) )
		
	# Returns the URL associated with the photoId
	def _get_url(self, photoId):
		for log in self.photoLog:
			if log["photoId"] == photoId:
				return log["url"]
		else: return None
		
	# Crop the image into a square and resize it to the output cell size
	def _crop_resize_square(self, im):
		cols, rows = im.size
		
		# Landscape Layout (or Square)
		if cols >= rows:
			target_cols = rows
			left = (cols-target_cols)/2
			right = left+target_cols
			return im.transform( (self.output_cell_width, self.output_cell_height), Image.EXTENT, (left, 0, right, rows) )
			
		# Portrait Layout
		else:
			target_rows = cols
			upper = (rows-target_rows)/2
			lower = upper+target_rows
			return im.transform( (self.output_cell_width, self.output_cell_height), Image.EXTENT, (0, upper, cols, lower) )

	# Defines input dimensions
	def _input_dims(self, rows, cols):
		# Since number of columns is the width and number of rows is height
		self.input_grid_width  = self.subimage["w"]
		self.input_grid_height = self.subimage["h"]
	
		self.total_rows = int(rows)
		self.total_cols = int(cols)
		
		self.input_grid_width  -= self.input_grid_width  % self.total_cols
		self.input_grid_height -= self.input_grid_height % self.total_rows
		
		self.input_cell_width  = self.input_grid_width  / self.total_cols
		self.input_cell_height = self.input_grid_height / self.total_rows
		
		# Crop Image
		self.input = self.input.crop( (self.subimage["x"], self.subimage["y"], self.subimage["x"]+self.input_cell_width*self.total_cols, self.subimage["y"]+self.input_cell_height*self.total_rows) )


	# Defines output dimensions and creates output image
	def _output_dims(self, mag):
		# Set dimensions
		self.output_mag = float(mag)
		self.output_grid_width  = int(self.input_grid_width  * self.output_mag)
		self.output_grid_height = int(self.input_grid_height * self.output_mag)
		
		self.output_grid_width  -= self.output_grid_width  % self.total_cols
		self.output_grid_height -= self.output_grid_height % self.total_rows
		
		self.output_cell_width  = self.output_grid_width  / self.total_cols
		self.output_cell_height = self.output_grid_height / self.total_rows
		
		# Create output image
		self.output = Image.new("RGB", (self.output_grid_width, self.output_grid_height), "red")