def _launch_folder(self): """ Launch :class:`lib.multithreading.MultiThread` to retrieve faces from a folder of images. Goes through the file list one at a time, passing each file to a separate background thread for some speed up. """ reader = SingleFrameLoader(self._location) num_threads = min(reader.count, self._num_threads) frame_split = reader.count // self._num_threads logger.debug("total images: %s, num_threads: %s, frames_per_thread: %s", reader.count, num_threads, frame_split) for idx in range(num_threads): is_final = idx == num_threads - 1 start_idx = idx * frame_split end_idx = reader.count if is_final else start_idx + frame_split thread = MultiThread(self._load_from_folder, reader, start_idx, end_idx) thread.start() self._threads.append(thread)
def _load_images(self, frames_location, video_meta_data): """ Load the images in a background thread. """ self._loader = SingleFrameLoader(frames_location, video_meta_data=video_meta_data) self._globals.set_frame_count(self._loader.count)
class FrameLoader(): """ Loads the frames, sets the frame count to :attr:`TkGlobals.frame_count` and handles the return of the correct frame for the GUI. Parameters ---------- tk_globals: :class:`~tools.manual.manual.TkGlobals` The tkinter variables that apply to the whole of the GUI frames_location: str The path to the input frames video_meta_data: dict The meta data held within the alignments file, if it exists and the input is a video """ def __init__(self, tk_globals, frames_location, video_meta_data): logger.debug( "Initializing %s: (tk_globals: %s, frames_location: '%s', " "video_meta_data: %s)", self.__class__.__name__, tk_globals, frames_location, video_meta_data) self._globals = tk_globals self._loader = None self._current_idx = 0 self._init_thread = self._background_init_frames( frames_location, video_meta_data) self._globals.tk_frame_index.trace("w", self._set_frame) logger.debug("Initialized %s", self.__class__.__name__) @property def is_initialized(self): """ bool: ``True`` if the Frame Loader has completed initialization otherwise ``False``. """ thread_is_alive = self._init_thread.is_alive() if thread_is_alive: self._init_thread.check_and_raise_error() else: self._init_thread.join() # Setting the initial frame cannot be done in the thread, so set when queried from main self._set_frame(initialize=True) return not thread_is_alive @property def video_meta_data(self): """ dict: The pts_time and key frames for the loader. """ return self._loader.video_meta_data def _background_init_frames(self, frames_location, video_meta_data): """ Launch the images loader in a background thread so we can run other tasks whilst waiting for initialization. """ thread = MultiThread(self._load_images, frames_location, video_meta_data, thread_count=1, name="{}.init_frames".format( self.__class__.__name__)) thread.start() return thread def _load_images(self, frames_location, video_meta_data): """ Load the images in a background thread. """ self._loader = SingleFrameLoader(frames_location, video_meta_data=video_meta_data) self._globals.set_frame_count(self._loader.count) def _set_frame(self, *args, initialize=False): # pylint:disable=unused-argument """ Set the currently loaded frame to :attr:`_current_frame` and trigger a full GUI update. If the loader has not been initialized, or the navigation position is the same as the current position and the face is not zoomed in, then this returns having done nothing. Parameters ---------- args: tuple :class:`tkinter.Event` arguments. Required but not used. initialize: bool, optional ``True`` if initializing for the first frame to be displayed otherwise ``False``. Default: ``False`` """ position = self._globals.frame_index if not initialize and (position == self._current_idx and not self._globals.is_zoomed): logger.trace( "Update criteria not met. Not updating: (initialize: %s, position: %s, " "current_idx: %s, is_zoomed: %s)", initialize, position, self._current_idx, self._globals.is_zoomed) return if position == -1: filename = "No Frame" frame = np.ones(self._globals.frame_display_dims + (3, ), dtype="uint8") else: filename, frame = self._loader.image_from_index(position) logger.trace("filename: %s, frame: %s, position: %s", filename, frame.shape, position) self._globals.set_current_frame(frame, filename) self._current_idx = position self._globals.tk_update.set(True) self._globals.tk_update_active_viewport.set(True)