From a16c54d2dbb540aebe9df439283b7a00871b4513 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Wed, 14 Feb 2024 18:03:13 +0000 Subject: [PATCH 1/3] Update raw image processing in scan This matches recent commits in labthings-picamera2 --- src/openflexure_microscope_server/things/smart_scan.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index b03c2d93..807e869a 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -549,8 +549,8 @@ class SmartScanThing(Thing): Cb = np.array(lst["Cb"]) gr, gb = cam.colour_gains G = 1/lum - R = 1/lum/Cr/gr - B = 1/lum/Cb/gb + R = G/Cr/gr*np.min(Cr) # The extra /np.max(Cr) emulates the quirky handling of Cr in + B = G/Cb/gb*np.min(Cb) # the picamera2 pipeline white_norm_lores = np.stack([R, G, B], axis=2) zoom_factors = [i/n for i, n in zip(rgb[...,:3].shape, white_norm_lores.shape)] white_norm = zoom(white_norm_lores, zoom_factors, order=1)[:rgb.shape[0], :rgb.shape[1], :] # Could use some work From 82e2a250fcd1492e6f50c0ac1ca2b9b0b3b77a94 Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 15 Feb 2024 16:38:15 +0000 Subject: [PATCH 2/3] Include colour correction matrix and gamma in saved images I've replicated more of the camera pipeline in the images saved during scans - in particular, I've added the colour correction matrix (increases saturation) and the contrast agorithm (implements gamma correction). This slows down saving to ~0.5-2 seconds, but that's still less time than it takes to move. --- .../things/smart_scan.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 807e869a..3c9d3be6 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -12,6 +12,7 @@ from PIL import Image from pydantic import BaseModel from scipy.stats import norm from scipy.ndimage import zoom +from scipy.interpolate import interp1d from copy import deepcopy from datetime import datetime from subprocess import CompletedProcess, Popen, PIPE, SubprocessError, run @@ -554,6 +555,14 @@ class SmartScanThing(Thing): white_norm_lores = np.stack([R, G, B], axis=2) zoom_factors = [i/n for i, n in zip(rgb[...,:3].shape, white_norm_lores.shape)] white_norm = zoom(white_norm_lores, zoom_factors, order=1)[:rgb.shape[0], :rgb.shape[1], :] # Could use some work + colour_correction_matrix = np.array(cam.colour_correction_matrix).reshape((3,3)) + contrast_algorithm = cam.tuning["algorithms"][9]["rpi.contrast"] + gamma = np.array(contrast_algorithm["gamma_curve"]).reshape((-1,2)) + gamma_8bit = interp1d(gamma[:, 0]/255, gamma[:, 1]/255) + def process_raw_image(img): + normed = img/white_norm + corrected = np.dot(colour_correction_matrix, normed.reshape((-1, 3)).T).T.reshape(normed.shape) + return gamma_8bit(corrected) logger.info( f"Generated normalisation image with shape {white_norm.shape}, " f"max {white_norm.max(axis=(0,1))}, min {white_norm.min(axis=(0,1))}" @@ -579,7 +588,7 @@ class SmartScanThing(Thing): # Save the raw image np.savez(os.path.join(raw_images_folder, name + ".npz"), raw_image=raw_image, **norm_inputs) # Process it into 8 bit RGB - processed = rggb2rgb(raw2rggb(raw_image)) / white_norm + processed = process_raw_image(rggb2rgb(raw2rggb(raw_image))) processed[processed > 255] = 255 processed[processed < 0] = 0 img = Image.fromarray(processed.astype(np.uint8), mode="RGB") From 41f03958c6bb38093f3c1bab7ea583af40f86b9e Mon Sep 17 00:00:00 2001 From: Richard Bowman Date: Thu, 15 Feb 2024 17:34:29 +0000 Subject: [PATCH 3/3] Save images with saturated pixels If any pixels in the manually processed raw image end up saturated (>255), this causes an error. I've now manually clamped the values between 0 and 255 to fix this. It would probably be a good idea to warn when this happens, as it does mean we're losing data. --- src/openflexure_microscope_server/things/smart_scan.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/openflexure_microscope_server/things/smart_scan.py b/src/openflexure_microscope_server/things/smart_scan.py index 3c9d3be6..bd61e7ff 100644 --- a/src/openflexure_microscope_server/things/smart_scan.py +++ b/src/openflexure_microscope_server/things/smart_scan.py @@ -562,6 +562,8 @@ class SmartScanThing(Thing): def process_raw_image(img): normed = img/white_norm corrected = np.dot(colour_correction_matrix, normed.reshape((-1, 3)).T).T.reshape(normed.shape) + corrected[corrected < 0] = 0 + corrected[corrected > 255] = 255 return gamma_8bit(corrected) logger.info( f"Generated normalisation image with shape {white_norm.shape}, "