def connect(self): """ This function tries to connects to the selected embedder if it is not successful due to any server/connection error it switches to the local embedder and warns the user about that. """ self.Warning.switched_local_embedder.clear() # try to connect to current embedder embedder = ImageEmbedder( model=self.embedders[self.cb_embedder_current_id], layer='penultimate' ) if not embedder.is_local_embedder() and \ not embedder.is_connected_to_server(use_hyper=False): # there is a problem with connecting to the server # switching to local embedder self.Warning.switched_local_embedder() del embedder # remove current embedder self.cb_embedder_current_id = self.embedders.index("squeezenet") print(self.embedders[self.cb_embedder_current_id]) embedder = ImageEmbedder( model=self.embedders[self.cb_embedder_current_id], layer='penultimate' ) return embedder
class OWImageEmbedding(OWWidget): name = "Image Embedding" description = "Image embedding through deep neural networks." icon = "icons/ImageEmbedding.svg" priority = 150 want_main_area = False _auto_apply = Setting(default=True) class Inputs: images = Input('Images', Table) class Outputs: embeddings = Output('Embeddings', Table, default=True) skipped_images = Output('Skipped Images', Table) cb_image_attr_current_id = Setting(default=0) cb_embedder_current_id = Setting(default=0) _NO_DATA_INFO_TEXT = "No data on input." def __init__(self): super().__init__() self.embedders = sorted(list(EMBEDDERS_INFO), key=lambda k: EMBEDDERS_INFO[k]['order']) self._image_attributes = None self._input_data = None self._log = logging.getLogger(__name__) self._task = None self._setup_layout() self._image_embedder = None self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) self.setBlocking(True) QTimer.singleShot(0, self._init_server_connection) def _setup_layout(self): self.controlArea.setMinimumWidth(self.controlArea.sizeHint().width()) self.layout().setSizeConstraint(QLayout.SetFixedSize) widget_box = widgetBox(self.controlArea, 'Info') self.input_data_info = widgetLabel(widget_box, self._NO_DATA_INFO_TEXT) self.connection_info = widgetLabel(widget_box, "") widget_box = widgetBox(self.controlArea, 'Settings') self.cb_image_attr = comboBox( widget=widget_box, master=self, value='cb_image_attr_current_id', label='Image attribute:', orientation=Qt.Horizontal, callback=self._cb_image_attr_changed ) self.cb_embedder = comboBox( widget=widget_box, master=self, value='cb_embedder_current_id', label='Embedder:', orientation=Qt.Horizontal, callback=self._cb_embedder_changed ) names = [EMBEDDERS_INFO[e]['name'] + (" (local)" if EMBEDDERS_INFO[e].get("is_local") else "") for e in self.embedders] self.cb_embedder.setModel(VariableListModel(names)) if not self.cb_embedder_current_id < len(self.embedders): self.cb_embedder_current_id = 0 self.cb_embedder.setCurrentIndex(self.cb_embedder_current_id) current_embedder = self.embedders[self.cb_embedder_current_id] self.embedder_info = widgetLabel( widget_box, EMBEDDERS_INFO[current_embedder]['description'] ) self.auto_commit_widget = auto_commit( widget=self.controlArea, master=self, value='_auto_apply', label='Apply', commit=self.commit ) self.cancel_button = QPushButton( 'Cancel', icon=self.style().standardIcon(QStyle.SP_DialogCancelButton), ) self.cancel_button.clicked.connect(self.cancel) hbox = hBox(self.controlArea) hbox.layout().addWidget(self.cancel_button) self.cancel_button.setDisabled(True) def _init_server_connection(self): self.setBlocking(False) self._image_embedder = ImageEmbedder( model=self.embedders[self.cb_embedder_current_id], layer='penultimate' ) self._set_server_info( self._image_embedder.is_connected_to_server() ) @Inputs.images def set_data(self, data): if not data: self._input_data = None self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) self.input_data_info.setText(self._NO_DATA_INFO_TEXT) return self._image_attributes = ImageEmbedder.filter_image_attributes(data) if not self._image_attributes: input_data_info_text = ( "Data with {:d} instances, but without image attributes." .format(len(data))) input_data_info_text.format(input_data_info_text) self.input_data_info.setText(input_data_info_text) self._input_data = None return if not self.cb_image_attr_current_id < len(self._image_attributes): self.cb_image_attr_current_id = 0 self.cb_image_attr.setModel(VariableListModel(self._image_attributes)) self.cb_image_attr.setCurrentIndex(self.cb_image_attr_current_id) self._input_data = data self.input_data_info.setText( "Data with {:d} instances.".format(len(data))) self._cb_image_attr_changed() def _cb_image_attr_changed(self): self.commit() def _cb_embedder_changed(self): current_embedder = self.embedders[self.cb_embedder_current_id] self._image_embedder = ImageEmbedder( model=current_embedder, layer='penultimate' ) self.embedder_info.setText( EMBEDDERS_INFO[current_embedder]['description']) if self._input_data: self.input_data_info.setText( "Data with {:d} instances.".format(len(self._input_data))) self.commit() else: self.input_data_info.setText(self._NO_DATA_INFO_TEXT) self._set_server_info(self._image_embedder.is_connected_to_server()) def commit(self): if self._task is not None: self.cancel() if self._image_embedder is None: self._set_server_info(connected=False) return if not self._image_attributes or self._input_data is None: self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) return self._set_server_info(connected=True) self.cancel_button.setDisabled(False) self.cb_image_attr.setDisabled(True) self.cb_embedder.setDisabled(True) file_paths_attr = self._image_attributes[self.cb_image_attr_current_id] file_paths = self._input_data[:, file_paths_attr].metas.flatten() origin = file_paths_attr.attributes.get("origin", "") if urlparse(origin).scheme in ("http", "https", "ftp", "data") and \ origin[-1] != "/": origin += "/" assert file_paths_attr.is_string assert file_paths.dtype == np.dtype('O') file_paths_mask = file_paths == file_paths_attr.Unknown file_paths_valid = file_paths[~file_paths_mask] for i, a in enumerate(file_paths_valid): urlparts = urlparse(a) if urlparts.scheme not in ("http", "https", "ftp", "data"): if urlparse(origin).scheme in ("http", "https", "ftp", "data"): file_paths_valid[i] = urljoin(origin, a) else: file_paths_valid[i] = os.path.join(origin, a) ticks = iter(np.linspace(0.0, 100.0, file_paths_valid.size)) set_progress = qconcurrent.methodinvoke( self, "__progress_set", (float,)) def advance(success=True): if success: set_progress(next(ticks)) def cancel(): task.future.cancel() task.cancelled = True task.embedder.set_canceled(True) embedder = self._image_embedder def run_embedding(paths): return embedder( file_paths=paths, image_processed_callback=advance) self.auto_commit_widget.setDisabled(True) self.progressBarInit(processEvents=None) self.progressBarSet(0.0, processEvents=None) self.setBlocking(True) f = self._executor.submit(run_embedding, file_paths_valid) f.add_done_callback( qconcurrent.methodinvoke(self, "__set_results", (object,))) task = self._task = namespace( file_paths_mask=file_paths_mask, file_paths_valid=file_paths_valid, file_paths=file_paths, embedder=embedder, cancelled=False, cancel=cancel, future=f, ) self._log.debug("Starting embedding task for %i images", file_paths.size) return @Slot(float) def __progress_set(self, value): assert self.thread() is QThread.currentThread() if self._task is not None: self.progressBarSet(value) @Slot(object) def __set_results(self, f): assert self.thread() is QThread.currentThread() if self._task is None or self._task.future is not f: self._log.info("Reaping stale task") return assert f.done() task, self._task = self._task, None self.auto_commit_widget.setDisabled(False) self.cancel_button.setDisabled(True) self.cb_image_attr.setDisabled(False) self.cb_embedder.setDisabled(False) self.progressBarFinished(processEvents=None) self.setBlocking(False) try: embeddings = f.result() except ConnectionError: self._log.exception("Error", exc_info=True) self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) self._set_server_info(connected=False) return except Exception as err: self._log.exception("Error", exc_info=True) self.error( "\n".join(traceback.format_exception_only(type(err), err))) self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) return assert self._input_data is not None assert len(self._input_data) == len(task.file_paths_mask) # Missing paths/urls were filtered out. Restore the full embeddings # array from information stored in task.file_path_mask ... embeddings_all = [None] * len(task.file_paths_mask) for i, embedding in zip(np.flatnonzero(~task.file_paths_mask), embeddings): embeddings_all[i] = embedding embeddings_all = np.array(embeddings_all) self._send_output_signals(embeddings_all) def _send_output_signals(self, embeddings): embedded_images, skipped_images, num_skipped =\ ImageEmbedder.prepare_output_data(self._input_data, embeddings) self.Outputs.embeddings.send(embedded_images) self.Outputs.skipped_images.send(skipped_images) if num_skipped is not 0: self.input_data_info.setText( "Data with {:d} instances, {:d} images skipped.".format( len(self._input_data), num_skipped)) def _set_server_info(self, connected): self.clear_messages() if self._image_embedder is None: return if connected: self.connection_info.setText("Connected to server.") elif self._image_embedder.is_local_embedder(): self.connection_info.setText("Using local embedder.") else: self.connection_info.setText("Not connected to server.") self.warning("Click Apply to try again.") def onDeleteWidget(self): self.cancel() super().onDeleteWidget() if self._image_embedder is not None: self._image_embedder.__exit__(None, None, None) def cancel(self): if self._task is not None: task, self._task = self._task, None task.cancel() # wait until done try: task.future.exception() except qconcurrent.CancelledError: pass self.auto_commit_widget.setDisabled(False) self.cancel_button.setDisabled(True) self.progressBarFinished(processEvents=None) self.setBlocking(False) self.cb_image_attr.setDisabled(False) self.cb_embedder.setDisabled(False) self._image_embedder.set_canceled(False) # reset the connection. connected = self._image_embedder.reconnect_to_server() self._set_server_info(connected=connected)
class OWImageEmbedding(OWWidget): name = "Image Embedding" description = "Image embedding through deep neural networks." icon = "icons/ImageEmbedding.svg" priority = 150 want_main_area = False _auto_apply = Setting(default=True) class Inputs: images = Input('Images', Table) class Outputs: embeddings = Output('Embeddings', Table, default=True) skipped_images = Output('Skipped Images', Table) class Warning(OWWidget.Warning): switched_local_embedder = Msg( "No internet connection: switched to local embedder") reapply = Msg("Click Apply to try again.") cb_image_attr_current_id = Setting(default=0) cb_embedder_current_id = Setting(default=0) _NO_DATA_INFO_TEXT = "No data on input." def __init__(self): super().__init__() self.embedders = sorted(list(EMBEDDERS_INFO), key=lambda k: EMBEDDERS_INFO[k]['order']) self._image_attributes = None self._input_data = None self._log = logging.getLogger(__name__) self._task = None self._setup_layout() self._image_embedder = None self._executor = concurrent.futures.ThreadPoolExecutor(max_workers=1) self.setBlocking(True) QTimer.singleShot(0, self._init_server_connection) def _setup_layout(self): self.controlArea.setMinimumWidth(self.controlArea.sizeHint().width()) self.layout().setSizeConstraint(QLayout.SetFixedSize) widget_box = widgetBox(self.controlArea, 'Info') self.input_data_info = widgetLabel(widget_box, self._NO_DATA_INFO_TEXT) self.connection_info = widgetLabel(widget_box, "") widget_box = widgetBox(self.controlArea, 'Settings') self.cb_image_attr = comboBox(widget=widget_box, master=self, value='cb_image_attr_current_id', label='Image attribute:', orientation=Qt.Horizontal, callback=self._cb_image_attr_changed) self.cb_embedder = comboBox(widget=widget_box, master=self, value='cb_embedder_current_id', label='Embedder:', orientation=Qt.Horizontal, callback=self._cb_embedder_changed) names = [ EMBEDDERS_INFO[e]['name'] + (" (local)" if EMBEDDERS_INFO[e].get("is_local") else "") for e in self.embedders ] self.cb_embedder.setModel(VariableListModel(names)) if not self.cb_embedder_current_id < len(self.embedders): self.cb_embedder_current_id = 0 self.cb_embedder.setCurrentIndex(self.cb_embedder_current_id) current_embedder = self.embedders[self.cb_embedder_current_id] self.embedder_info = widgetLabel( widget_box, EMBEDDERS_INFO[current_embedder]['description']) self.auto_commit_widget = auto_commit(widget=self.controlArea, master=self, value='_auto_apply', label='Apply', commit=self.commit) self.cancel_button = QPushButton( 'Cancel', icon=self.style().standardIcon(QStyle.SP_DialogCancelButton), ) self.cancel_button.clicked.connect(self.cancel) hbox = hBox(self.controlArea) hbox.layout().addWidget(self.cancel_button) self.cancel_button.setDisabled(True) def _init_server_connection(self): self.setBlocking(False) self._image_embedder = ImageEmbedder( model=self.embedders[self.cb_embedder_current_id], layer='penultimate') self._set_server_info(self._image_embedder.is_connected_to_server()) @Inputs.images def set_data(self, data): if not data: self._input_data = None self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) self.input_data_info.setText(self._NO_DATA_INFO_TEXT) return self._image_attributes = ImageEmbedder.filter_image_attributes(data) if not self._image_attributes: input_data_info_text = ( "Data with {:d} instances, but without image attributes.". format(len(data))) input_data_info_text.format(input_data_info_text) self.input_data_info.setText(input_data_info_text) self._input_data = None return if not self.cb_image_attr_current_id < len(self._image_attributes): self.cb_image_attr_current_id = 0 self.cb_image_attr.setModel(VariableListModel(self._image_attributes)) self.cb_image_attr.setCurrentIndex(self.cb_image_attr_current_id) self._input_data = data self.input_data_info.setText("Data with {:d} instances.".format( len(data))) self.check_connection() self._cb_image_attr_changed() def _cb_image_attr_changed(self): self.commit() def check_connection(self): """ The method check if it is possible to send images to embedder if not embedder is switched to local """ self.Warning.switched_local_embedder.clear() if not self._image_embedder.is_local_embedder() and \ not self._image_embedder.is_connected_to_server(use_hyper=False): # switching to local embedder self.Warning.switched_local_embedder() self.cb_embedder_current_id = self.embedders.index("squeezenet") self._init_server_connection() def _cb_embedder_changed(self): current_embedder = self.embedders[self.cb_embedder_current_id] self._image_embedder = ImageEmbedder(model=current_embedder, layer='penultimate') self.embedder_info.setText( EMBEDDERS_INFO[current_embedder]['description']) if self._input_data: self.input_data_info.setText("Data with {:d} instances.".format( len(self._input_data))) self.commit() else: self.input_data_info.setText(self._NO_DATA_INFO_TEXT) self._set_server_info(self._image_embedder.is_connected_to_server()) def commit(self): if self._task is not None: self.cancel() if self._image_embedder is None: self._set_server_info(connected=False) return if not self._image_attributes or self._input_data is None: self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) return self._set_server_info(connected=True) self.cancel_button.setDisabled(False) self.cb_image_attr.setDisabled(True) self.cb_embedder.setDisabled(True) file_paths_attr = self._image_attributes[self.cb_image_attr_current_id] file_paths = self._input_data[:, file_paths_attr].metas.flatten() origin = file_paths_attr.attributes.get("origin", "") if urlparse(origin).scheme in ("http", "https", "ftp", "data") and \ origin[-1] != "/": origin += "/" assert file_paths_attr.is_string assert file_paths.dtype == np.dtype('O') file_paths_mask = file_paths == file_paths_attr.Unknown file_paths_valid = file_paths[~file_paths_mask] for i, a in enumerate(file_paths_valid): urlparts = urlparse(a) if urlparts.scheme not in ("http", "https", "ftp", "data"): if urlparse(origin).scheme in ("http", "https", "ftp", "data"): file_paths_valid[i] = urljoin(origin, a) else: file_paths_valid[i] = os.path.join(origin, a) ticks = iter(np.linspace(0.0, 100.0, file_paths_valid.size)) set_progress = qconcurrent.methodinvoke(self, "__progress_set", (float, )) def advance(success=True): if success: set_progress(next(ticks)) def cancel(): task.future.cancel() task.cancelled = True task.embedder.set_canceled(True) embedder = self._image_embedder def run_embedding(paths): return embedder(file_paths=paths, image_processed_callback=advance) self.auto_commit_widget.setDisabled(True) self.progressBarInit(processEvents=None) self.progressBarSet(0.0, processEvents=None) self.setBlocking(True) f = self._executor.submit(run_embedding, file_paths_valid) f.add_done_callback( qconcurrent.methodinvoke(self, "__set_results", (object, ))) task = self._task = namespace( file_paths_mask=file_paths_mask, file_paths_valid=file_paths_valid, file_paths=file_paths, embedder=embedder, cancelled=False, cancel=cancel, future=f, ) self._log.debug("Starting embedding task for %i images", file_paths.size) return @Slot(float) def __progress_set(self, value): assert self.thread() is QThread.currentThread() if self._task is not None: self.progressBarSet(value) @Slot(object) def __set_results(self, f): assert self.thread() is QThread.currentThread() if self._task is None or self._task.future is not f: self._log.info("Reaping stale task") return assert f.done() task, self._task = self._task, None self.auto_commit_widget.setDisabled(False) self.cancel_button.setDisabled(True) self.cb_image_attr.setDisabled(False) self.cb_embedder.setDisabled(False) self.progressBarFinished(processEvents=None) self.setBlocking(False) try: embeddings = f.result() except ConnectionError: self._log.exception("Error", exc_info=True) self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) self._set_server_info(connected=False) return except Exception as err: self._log.exception("Error", exc_info=True) self.error("\n".join( traceback.format_exception_only(type(err), err))) self.Outputs.embeddings.send(None) self.Outputs.skipped_images.send(None) return assert self._input_data is not None assert len(self._input_data) == len(task.file_paths_mask) # Missing paths/urls were filtered out. Restore the full embeddings # array from information stored in task.file_path_mask ... embeddings_all = [None] * len(task.file_paths_mask) for i, embedding in zip(np.flatnonzero(~task.file_paths_mask), embeddings): embeddings_all[i] = embedding embeddings_all = np.array(embeddings_all) self._send_output_signals(embeddings_all) def _send_output_signals(self, embeddings): embedded_images, skipped_images, num_skipped =\ ImageEmbedder.prepare_output_data(self._input_data, embeddings) self.Outputs.embeddings.send(embedded_images) self.Outputs.skipped_images.send(skipped_images) if num_skipped is not 0: self.input_data_info.setText( "Data with {:d} instances, {:d} images skipped.".format( len(self._input_data), num_skipped)) def _set_server_info(self, connected): self.Warning.reapply.clear() if self._image_embedder is None: return if connected: self.connection_info.setText("Connected to server.") elif self._image_embedder.is_local_embedder(): self.connection_info.setText("Using local embedder.") else: self.connection_info.setText("Not connected to server.") self.Warning.reapply() def onDeleteWidget(self): self.cancel() super().onDeleteWidget() if self._image_embedder is not None: self._image_embedder.__exit__(None, None, None) def cancel(self): if self._task is not None: task, self._task = self._task, None task.cancel() # wait until done try: task.future.exception() except qconcurrent.CancelledError: pass self.auto_commit_widget.setDisabled(False) self.cancel_button.setDisabled(True) self.progressBarFinished(processEvents=None) self.setBlocking(False) self.cb_image_attr.setDisabled(False) self.cb_embedder.setDisabled(False) self._image_embedder.set_canceled(False) # reset the connection. connected = self._image_embedder.reconnect_to_server() self._set_server_info(connected=connected)