def blend_channel(channel): with log_timer("%s.native" % self.__class__.__name__): solution = self.cache_native[channel].blend(self.epsilon, self.max_iterations) self.cache_target[t_top:t_bottom, t_left:t_right, channel] = solution errorlog = self.cache_errorlog[channel] LOG.debug("Quickdescent iterations: %d" % (errorlog.nonzero()[0][-1] + 1)) LOG.debug("Final error value: %f" % (errorlog[errorlog.nonzero()[0][-1]]))
def draw_numpy(self, ndarray): """ Draws a numpy array (H * W * 3) as an image on this canvas. """ assert ndarray.dtype == np.float32 ndarray = np.clip(ndarray * 255., 0., 255.).astype(np.uint8) with log_timer("EditorViewCanvas.draw_numpy"): if self.active_image_container is None: self.active_image_container = ImageTk.PhotoImage(Image.fromarray(ndarray)) self.itemconfig(self.active_image_id, image=self.active_image_container) else: self.active_image_container.paste(Image.fromarray(ndarray))
def get_fusion(self): with log_timer("%s.get_fusion" % self.__class__.__name__): target = np.copy(self.canvas) if not self.active: return target s_top, s_bottom, s_left, s_right = self.source_bounds t_top, t_bottom, t_left, t_right = self.target_bounds source = self.active_source[s_top:s_bottom, s_left:s_right] tinyt = target[t_top:t_bottom, t_left:t_right, :] s_mask = self.active_mask[t_top:t_bottom, t_left:t_right] for channel in range(3): solution = self.poisson_blend(source[:, :, channel], s_mask, tinyt[:, :, channel]) target[t_top:t_bottom, t_left:t_right, channel] = solution return target
def get_fusion(self): with log_timer("%s.get_fusion" % self.__class__.__name__): if not self.active: return np.copy(self.canvas) s_top, s_bottom, s_left, s_right = self.source_bounds t_top, t_bottom, t_left, t_right = self.target_bounds if (self.source_bounds != self.cache_source_bounds or self.target_bounds != self.cache_target_bounds): with log_timer("%s.setup" % self.__class__.__name__): # We copy cache_mask here, because the native code would need to copy it anyway # (since it is a view, not an actual ndarray). However, we don't copy source or # tinyt, because it is cloned per-channel in the "for channel in range(3)" loop # below. source = self.active_source[s_top:s_bottom, s_left:s_right] tinyt = self.canvas[t_top:t_bottom, t_left:t_right, :] self.cache_mask = np.copy(self.active_mask[t_top:t_bottom, t_left:t_right], order="C") # Used for storing our QuickdescentContext instances self.cache_native = [] # Used for storing all our errorlogs self.cache_errorlog = [] # Used for storing the fusion result (we can reuse this buffer) self.cache_target = np.copy(self.canvas) for channel in range(3): solution = np.zeros(tinyt.shape[:2], dtype=np.float32, order="C") errorlog = np.zeros(self.max_iterations, dtype=np.float32, order="C") q = _quickdescent.QuickdescentContext(source[:, :, channel].copy(order="C"), self.cache_mask, tinyt[:, :, channel].copy(order="C"), solution, errorlog) q.initializeGuess() self.cache_native.append(q) self.cache_errorlog.append(errorlog) self.cache_source_bounds = self.source_bounds self.cache_target_bounds = self.target_bounds def blend_channel(channel): with log_timer("%s.native" % self.__class__.__name__): solution = self.cache_native[channel].blend(self.epsilon, self.max_iterations) self.cache_target[t_top:t_bottom, t_left:t_right, channel] = solution errorlog = self.cache_errorlog[channel] LOG.debug("Quickdescent iterations: %d" % (errorlog.nonzero()[0][-1] + 1)) LOG.debug("Final error value: %f" % (errorlog[errorlog.nonzero()[0][-1]])) if self.threading: threads = [] for channel in range(3): thread = threading.Thread(target=blend_channel, args=(channel,)) threads.append(thread) for thread in threads: thread.start() for thread in threads: thread.join() else: for channel in range(3): blend_channel(channel) return self.cache_target.clip(0, 1)