From b21040f22ef42d632d2edc0727a2100aeba0f921 Mon Sep 17 00:00:00 2001 From: Joel Collins Date: Fri, 30 Nov 2018 10:33:39 +0000 Subject: [PATCH] Better fix for RGB capture memory issues --- openflexure_microscope/camera/base.py | 12 ++++-- openflexure_microscope/camera/pi.py | 41 ++++++++----------- .../utilities/recalibrate.py | 6 ++- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/openflexure_microscope/camera/base.py b/openflexure_microscope/camera/base.py index d7ec18e3..eeac7838 100644 --- a/openflexure_microscope/camera/base.py +++ b/openflexure_microscope/camera/base.py @@ -96,6 +96,9 @@ class BaseCamera(object): self.last_access = 0 #: time: Time of last client access to the camera self.event = CameraEvent() + self.stream_timeout = 20 #: int: Number of inactive seconds before timing out the stream + self.stream_timeout_enabled = True #: bool: Enable or disable timing out the stream + self.state = {} #: dict: Dictionary for capture state self._config = {} #: dict: Dictionary of base camera settings self.paths = { @@ -257,9 +260,12 @@ class BaseCamera(object): self.event.set() # send signal to clients time.sleep(0) - # if there hasn't been any clients asking for frames in - # the last 10 seconds then stop the thread - if time.time() - self.last_access > 20: + # Handle timeout + if ( + self.stream_timeout_enabled and # If using timeout + (time.time() - self.last_access > self.stream_timeout) and # And timeout time + not self.state['preview_active'] # And GPU preview is not active + ): self.frames_iterator.close() break diff --git a/openflexure_microscope/camera/pi.py b/openflexure_microscope/camera/pi.py index f327309c..c6e5adf8 100644 --- a/openflexure_microscope/camera/pi.py +++ b/openflexure_microscope/camera/pi.py @@ -17,7 +17,7 @@ Camera capture resolution set to video_resolution in frames() Video port uses that resolution for everything. If a different resolution is specified for video capture, this is handled by the resizer. -Still capture (if use_video_port == False) uses pause_stream_for_capture +Still capture (if use_video_port == False) uses pause_stream to temporarily increase the capture resolution. """ @@ -208,7 +208,7 @@ class StreamingCamera(BaseCamera): # Pause stream while changing settings if self.state['stream_active']: # If stream is active logging.info("Pausing stream to update config.") - self.pause_stream_for_capture() # Pause stream + self.pause_stream() # Pause stream paused_stream = True # Remember to unpause stream when done # PiCamera parameters (applied directly to PiCamera object) @@ -237,7 +237,7 @@ class StreamingCamera(BaseCamera): # If stream was paused to update config, unpause if paused_stream: logging.info("Resuming stream.") - self.resume_stream_for_capture() + self.resume_stream() else: raise Exception( @@ -355,7 +355,7 @@ class StreamingCamera(BaseCamera): # Update state dictionary self.state['record_active'] = False - def pause_stream_for_capture( + def pause_stream( self, splitter_port: int=1, resolution: Tuple[int, int]=None) -> None: @@ -377,7 +377,7 @@ class StreamingCamera(BaseCamera): # Increase the resolution for taking an image self.camera.resolution = resolution - def resume_stream_for_capture( + def resume_stream( self, splitter_port: int=1, resolution: Tuple[int, int]=None) -> None: @@ -388,7 +388,7 @@ class StreamingCamera(BaseCamera): splitter_port (int): Splitter port to start recording on resolution ((int, int)): Resolution to set the camera to, before starting recording. """ - logging.debug("Unpausing stream") + logging.debug("Resuming stream") if not resolution: resolution = self.config['video_resolution'] @@ -425,7 +425,7 @@ class StreamingCamera(BaseCamera): fmt (str): Format of the capture. resize ((int, int)): Resize the captured image. """ - + # If no filename is specified, build a non-clashing one if not filename: filename = self.generate_basename(self.images) @@ -453,7 +453,7 @@ class StreamingCamera(BaseCamera): if not use_video_port: # Pause video splitter port 1 - self.pause_stream_for_capture() + self.pause_stream() self.camera.capture( target, @@ -463,7 +463,7 @@ class StreamingCamera(BaseCamera): bayer=True) # Resume video splitter port 1 - self.resume_stream_for_capture() + self.resume_stream() else: self.camera.capture( @@ -498,7 +498,7 @@ class StreamingCamera(BaseCamera): size = resolution if not use_video_port: - self.pause_stream_for_capture(resolution=resolution) + self.pause_stream(resolution=resolution) logging.debug("Creating PiYUVArray") with picamera.array.PiYUVArray(self.camera, size=size) as output: @@ -512,7 +512,7 @@ class StreamingCamera(BaseCamera): use_video_port=use_video_port) if not use_video_port: - self.resume_stream_for_capture() + self.resume_stream() if rgb: logging.debug("Converting to RGB") @@ -523,19 +523,13 @@ class StreamingCamera(BaseCamera): def array( self, use_video_port: bool=True, - resize: Tuple[int, int]=None, - stop_worker: bool=True) -> np.ndarray: + resize: Tuple[int, int]=None) -> np.ndarray: """Capture an uncompressed still RGB image to a Numpy array. Args: use_video_port (bool): Capture from the video port used for streaming. Lower resolution, faster. resize ((int, int)): Resize the captured image. - stop_worker (bool): Auto-stop worker, to avoid GPU memory issues """ - restart_worker = False - if stop_worker is True and self.stop is False: - self.stop_worker() - restart_worker = True if use_video_port: resolution = self.config['video_resolution'] @@ -547,8 +541,8 @@ class StreamingCamera(BaseCamera): else: size = resolution - if not use_video_port: - self.pause_stream_for_capture(resolution=resolution) + # Always pause stream, to prevent resizer memory issues + self.pause_stream(resolution=resolution) logging.debug("Creating PiRGBArray") with picamera.array.PiRGBArray(self.camera, size=size) as output: @@ -561,11 +555,8 @@ class StreamingCamera(BaseCamera): format='rgb', use_video_port=use_video_port) - if not use_video_port: - self.resume_stream_for_capture() - - if restart_worker: - self.start_worker() + # Resume stream + self.resume_stream() return output.array diff --git a/openflexure_microscope/utilities/recalibrate.py b/openflexure_microscope/utilities/recalibrate.py index acdede53..a3b777ce 100644 --- a/openflexure_microscope/utilities/recalibrate.py +++ b/openflexure_microscope/utilities/recalibrate.py @@ -104,7 +104,6 @@ def generate_lens_shading_table_closed_loop(output_fname="shadingtable.npy", # Open the microscope and start with flat (i.e. no) lens shading correction. cam.start_preview() logging.info("Stopping worker thread during calibration, to avoid GPU memory issues") - cam.stop_worker() def get_rgb_image(): # shorthand for taking an RGB image return cam.array(use_video_port=True, resize=(max_res[0]//2, max_res[1]//2)) @@ -125,8 +124,11 @@ def generate_lens_shading_table_closed_loop(output_fname="shadingtable.npy", rgb_image = np.mean(images, axis=0, dtype=np.float) incremental_gains = lens_shading_correction_from_rgb(rgb_image, 64//2) gains *= incremental_gains + # Apply this change (actually apply a bit less than the change) + cam.pause_stream() cam.camera.lens_shading_table = gains_to_lst(gains*32) + cam.resume_stream() time.sleep(2) # Fix the AWB gains so the image is neutral @@ -158,6 +160,8 @@ def generate_lens_shading_table_closed_loop(output_fname="shadingtable.npy", logging.debug("Merging config...") config.merge_config(settings, safe=True, backup=True) + #cam.resume_stream_for_capture() + if __name__ == '__main__': generate_lens_shading_table_closed_loop() \ No newline at end of file