From d998db68afcec2a8d321a0bdd03391f097561006 Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 20 May 2025 19:54:32 +0000 Subject: [PATCH 01/15] modified display_results method in PixelDecoder to display all bits in napari --- examples/bioprotean_smFISH/smFISH_decoding.py | 34 +++++++++++++++++++ src/merfish3danalysis/PixelDecoder.py | 25 ++++++++++---- 2 files changed, 53 insertions(+), 6 deletions(-) create mode 100644 examples/bioprotean_smFISH/smFISH_decoding.py diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py new file mode 100644 index 0000000..88fb799 --- /dev/null +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -0,0 +1,34 @@ +from merfish3danalysis.qi2labDataStore import qi2labDataStore +from merfish3danalysis.PixelDecoder import PixelDecoder +from pathlib import Path +# import numpy as np + +root_path = Path(r"/data/smFISH/02202025_Bartelle_control_smFISH_TqIB") + +# initialize datastore +datastore_path = root_path / Path(r"qi2labdatastore") +datastore = qi2labDataStore(datastore_path) +merfish_bits = datastore.num_bits + +# initialize decodor class +decoder = PixelDecoder( + datastore=datastore, + use_mask=False, + merfish_bits=merfish_bits, + verbose=1, + smFISH = True +) + +# decode one tile +decoder.decode_one_tile( + tile_idx=0, # Specify the tile index + display_results=True, # Set to True to visualize results in Napari + lowpass_sigma=(3, 1, 1), # Lowpass filter sigma + magnitude_threshold=0.9, # L2-norm threshold + minimum_pixels=3.0, # Minimum number of pixels for a barcode + use_normalization=False, # Use normalization + ufish_threshold=0.5 # Ufish threshold +) + +# # Save barcodes +# decoder._save_barcodes() \ No newline at end of file diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 1fcea1f..21cb72f 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -71,6 +71,7 @@ def __init__( use_mask: Optional[bool] = False, z_range: Optional[Sequence[int]] = None, include_blanks: Optional[bool] = True, + smFISH: bool = False ): self._datastore = datastore self._verbose = verbose @@ -79,6 +80,10 @@ def __init__( self._n_merfish_bits = merfish_bits + # Is this data smFISH or MERFISH? + # Default is False, meaning data is MERFISH + self._smFISH = smFISH + if self._datastore.microscope_type == "2D": self._is_3D = False else: @@ -1841,12 +1846,20 @@ def on_close_callback(): app = QApplication.instance() app.lastWindowClosed.connect(on_close_callback) - - viewer.add_image( - self._scaled_pixel_images, - scale=[self._axial_step, self._pixel_size, self._pixel_size], - name="pixels", - ) + + if self._smFISH: + for bit in range(self._datastore.num_bits): + viewer.add_image( + self._scaled_pixel_images[bit], + scale=[self._axial_step, self._pixel_size, self._pixel_size], + name="pixels_" + str(bit), + ) + else: + viewer.add_image( + self._scaled_pixel_images, + scale=[self._axial_step, self._pixel_size, self._pixel_size], + name="pixels", + ) viewer.add_image( self._decoded_image, From 58c6f114339e6fd56640a56fb05cb9084ccedf3d Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 27 May 2025 21:01:06 +0000 Subject: [PATCH 02/15] added upper magnitude threshold and added a different distance threshold for smFISH --- src/merfish3danalysis/PixelDecoder.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 21cb72f..435d160 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -110,7 +110,11 @@ def __init__( self._global_normalization_loaded = False self._iterative_normalization_loaded = False self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits - self._magnitude_threshold = 0.9 # default for HW4D4 code + if self._smFISH: + # establish lower threshold for magnitude + self._magnitude_threshold = 0.75 + else: + self._magnitude_threshold = 0.9 # default for HW4D4 code def _load_codebook(self): """Load and parse codebook into gene_id and codeword matrix.""" @@ -857,7 +861,14 @@ def _decode_pixels( decoded_trace = cp.full(distance_trace.shape[0], -1, dtype=cp.int16) mask_trace = distance_trace < distance_threshold decoded_trace[mask_trace] = codebook_index_trace[mask_trace] - decoded_trace[pixel_magnitude_trace <= magnitude_threshold] = -1 + + # For smFISH data, we are adding an upper magnitude threshold and setting pixels above this threshold to -1. + if self._smFISH: + upper_magnitude_threshold = 1.75 + decoded_trace[pixel_magnitude_trace >= upper_magnitude_threshold] = -1 + decoded_trace[pixel_magnitude_trace <= magnitude_threshold] = -1 + else: + decoded_trace[pixel_magnitude_trace <= magnitude_threshold] = -1 self._decoded_image[z_idx, :] = cp.asnumpy( cp.reshape(cp.round(decoded_trace, 3), z_plane_shape[1:]) From 9b1fde83e9f160121b9240d4075a8baa72ed344b Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 27 May 2025 22:15:36 +0000 Subject: [PATCH 03/15] improved the logic behind calling magnitude thresholds --- src/merfish3danalysis/PixelDecoder.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 435d160..beeea6e 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -111,8 +111,9 @@ def __init__( self._iterative_normalization_loaded = False self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits if self._smFISH: - # establish lower threshold for magnitude + # establish lower threshold for magnitude for smFISH data self._magnitude_threshold = 0.75 + self._upper_magnitude_threshold = 1.75 else: self._magnitude_threshold = 0.9 # default for HW4D4 code @@ -781,7 +782,8 @@ def _calculate_distances( def _decode_pixels( self, distance_threshold: float = 0.5172, - magnitude_threshold: float = 1.0 + magnitude_threshold: float, + upper_magnitude_threshold: float = None, # Only used for smFISH data ): """Decode pixels using the decoding matrix. @@ -864,7 +866,6 @@ def _decode_pixels( # For smFISH data, we are adding an upper magnitude threshold and setting pixels above this threshold to -1. if self._smFISH: - upper_magnitude_threshold = 1.75 decoded_trace[pixel_magnitude_trace >= upper_magnitude_threshold] = -1 decoded_trace[pixel_magnitude_trace <= magnitude_threshold] = -1 else: @@ -1927,7 +1928,8 @@ def decode_one_tile( tile_idx: int = 0, display_results: bool = False, lowpass_sigma: Optional[Sequence[float]] = (3, 1, 1), - magnitude_threshold: Optional[float] = 0.9, + magnitude_threshold: Optional[float] = None, + upper_magnitude_threshold: Optional[float] = None, minimum_pixels: Optional[float] = 3.0, use_normalization: Optional[bool] = True, ufish_threshold: Optional[float] = 0.5, @@ -1956,6 +1958,10 @@ def decode_one_tile( if use_normalization: self._load_iterative_normalization_vectors() + if magnitude_threshold is None: + magnitude_threshold = self._magnitude_threshold + if upper_magnitude_threshold is None: + upper_magnitude_threshold = getattr(self, "_upper_magnitude_threshold", None) self._tile_idx = tile_idx self._load_bit_data(ufish_threshold=ufish_threshold) @@ -1963,7 +1969,8 @@ def decode_one_tile( self._lp_filter(sigma=lowpass_sigma) self._decode_pixels( distance_threshold=self._distance_threshold, - magnitude_threshold=magnitude_threshold, + magnitude_threshold=magnitude_threshold, + upper_magnitude_threshold=upper_magnitude_threshold, ) if display_results: self._display_results() From e013b22980b6e87b39e4977e49472e534ee5f97d Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 27 May 2025 22:19:54 +0000 Subject: [PATCH 04/15] added different distance threshold for smFISH --- src/merfish3danalysis/PixelDecoder.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index beeea6e..e53dd20 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -109,13 +109,15 @@ def __init__( self._optimize_normalization_weights = False self._global_normalization_loaded = False self._iterative_normalization_loaded = False - self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits + if self._smFISH: # establish lower threshold for magnitude for smFISH data self._magnitude_threshold = 0.75 self._upper_magnitude_threshold = 1.75 + self._distance_threshold = 1.0 else: self._magnitude_threshold = 0.9 # default for HW4D4 code + self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits def _load_codebook(self): """Load and parse codebook into gene_id and codeword matrix.""" From 3650936ddd408b03f9ff962146950c47d082f962 Mon Sep 17 00:00:00 2001 From: mspend Date: Wed, 28 May 2025 19:04:06 +0000 Subject: [PATCH 05/15] fixed parameters passed into decode_one_tile function --- examples/bioprotean_smFISH/smFISH_decoding.py | 10 ++++++++-- src/merfish3danalysis/PixelDecoder.py | 6 ++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index 88fb799..5121284 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -18,17 +18,23 @@ verbose=1, smFISH = True ) +print(f"The distance threshold is {decoder._distance_threshold}") +print(f"The lower magnitude threshold is {decoder._magnitude_threshold}") +print(f"The upper magnitude threshold is {decoder._upper_magnitude_threshold}") # decode one tile decoder.decode_one_tile( tile_idx=0, # Specify the tile index display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma - magnitude_threshold=0.9, # L2-norm threshold + magnitude_threshold=0.75, # L2-norm threshold + upper_magnitude_threshold=1.75, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode - use_normalization=False, # Use normalization + use_normalization=True, # Use normalization ufish_threshold=0.5 # Ufish threshold ) +print("Decoding complete.") + # # Save barcodes # decoder._save_barcodes() \ No newline at end of file diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index e53dd20..fdf0a5d 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -783,8 +783,8 @@ def _calculate_distances( return min_distances, min_indices def _decode_pixels( - self, distance_threshold: float = 0.5172, - magnitude_threshold: float, + self, distance_threshold: float = None, + magnitude_threshold: float = None, upper_magnitude_threshold: float = None, # Only used for smFISH data ): """Decode pixels using the decoding matrix. @@ -797,6 +797,8 @@ def _decode_pixels( magnitude_threshold : float, default 1.0. Magnitude threshold for decoding. """ + if distance_threshold is None: + distance_threshold = self._distance_threshold if self._filter_type == "lp": original_shape = self._image_data_lp.shape From 626ead286d32dcd7d5610c2fb00bf4b89c57960a Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 10 Jun 2025 20:40:20 +0000 Subject: [PATCH 06/15] added a bunch of print statements to assess logic flow --- src/merfish3danalysis/PixelDecoder.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index fdf0a5d..85fea1d 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -348,8 +348,12 @@ def _load_iterative_normalization_vectors(self): self._iterative_normalization_vector = cp.asarray(normalization_vector) self._iterative_background_vector = cp.asarray(background_vector) self._iterative_normalization_loaded = True + print('Iterative normalization vectors loaded in line 351') else: self._iterative_normalization_vectors() + print('Iterative normalization vectors loaded in line 354') + print(f'The background vector is {background_vector}') + print(f'The normalization vector is {normalization_vector}') def _iterative_normalization_vectors(self): """Calculate iterative normalization and background vectors.""" @@ -473,6 +477,7 @@ def _iterative_normalization_vectors(self): self._datastore.iterative_background_vector = barcode_based_background_vector self._iterative_normalization_loaded = True + print('Iterative normalization vectors loaded in line 480') del df_barcodes_loaded_no_blanks gc.collect() @@ -838,6 +843,7 @@ def _decode_pixels( ) if self._iterative_normalization_loaded: + print('Iterative normalization vectors loaded in line 846') scaled_pixel_traces = self._scale_pixel_traces( scaled_pixel_traces, self._iterative_background_vector, @@ -845,6 +851,7 @@ def _decode_pixels( self._n_merfish_bits, ) elif self._global_normalization_loaded: + print('Iterative normalization vectors not loaded, so doing global normalization') scaled_pixel_traces = self._scale_pixel_traces( scaled_pixel_traces, self._global_background_vector, From 006cc796c5a10e7083f7fc4afe0090dd7ced2012 Mon Sep 17 00:00:00 2001 From: mspend Date: Mon, 30 Jun 2025 22:10:50 +0000 Subject: [PATCH 07/15] split the decoded image into its 16 bits in display_results method in PixelDecoder.py --- src/merfish3danalysis/PixelDecoder.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 85fea1d..9909bec 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -1883,12 +1883,13 @@ def on_close_callback(): scale=[self._axial_step, self._pixel_size, self._pixel_size], name="pixels", ) - - viewer.add_image( - self._decoded_image, - scale=[self._axial_step, self._pixel_size, self._pixel_size], - name="decoded", - ) + + for bit in range(self._datastore.num_bits): + viewer.add_image( + self._decoded_image, + scale=[self._axial_step, self._pixel_size, self._pixel_size], + name="decoded_" + str(bit), + ) viewer.add_image( self._magnitude_image, From 80d55ea4cfca8ea66809849f23625d053f672af1 Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 1 Jul 2025 23:24:49 +0000 Subject: [PATCH 08/15] made sure the dimension of the decoded image and what Napari expects match to resolve an error --- src/merfish3danalysis/PixelDecoder.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 9909bec..ffafacf 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -348,12 +348,10 @@ def _load_iterative_normalization_vectors(self): self._iterative_normalization_vector = cp.asarray(normalization_vector) self._iterative_background_vector = cp.asarray(background_vector) self._iterative_normalization_loaded = True - print('Iterative normalization vectors loaded in line 351') + # print('Iterative normalization vectors loaded in line 351') else: self._iterative_normalization_vectors() - print('Iterative normalization vectors loaded in line 354') - print(f'The background vector is {background_vector}') - print(f'The normalization vector is {normalization_vector}') + # print('Iterative normalization vectors loaded in line 354') def _iterative_normalization_vectors(self): """Calculate iterative normalization and background vectors.""" @@ -477,7 +475,7 @@ def _iterative_normalization_vectors(self): self._datastore.iterative_background_vector = barcode_based_background_vector self._iterative_normalization_loaded = True - print('Iterative normalization vectors loaded in line 480') + # print('Iterative normalization vectors loaded in line 480') del df_barcodes_loaded_no_blanks gc.collect() @@ -843,7 +841,7 @@ def _decode_pixels( ) if self._iterative_normalization_loaded: - print('Iterative normalization vectors loaded in line 846') + # print('Iterative normalization vectors loaded in line 846') scaled_pixel_traces = self._scale_pixel_traces( scaled_pixel_traces, self._iterative_background_vector, @@ -1885,9 +1883,18 @@ def on_close_callback(): ) for bit in range(self._datastore.num_bits): + img = self._decoded_image[bit] + print("Adding image with shape:", img.shape) + if img.ndim == 3: + scale = [self._axial_step, self._pixel_size, self._pixel_size] + elif img.ndim == 2: + scale = [self._pixel_size, self._pixel_size] + else: + raise ValueError(f"Unexpected image dimension: {img.ndim}") + print("Using scale:", scale) viewer.add_image( - self._decoded_image, - scale=[self._axial_step, self._pixel_size, self._pixel_size], + img, + scale=scale, name="decoded_" + str(bit), ) From d4ebe8e3d0a55c7d884a1bbd9670afb777f01b65 Mon Sep 17 00:00:00 2001 From: mspend Date: Thu, 3 Jul 2025 01:56:22 +0000 Subject: [PATCH 09/15] changed magnitude thresholds --- examples/bioprotean_smFISH/smFISH_decoding.py | 8 ++++---- src/merfish3danalysis/PixelDecoder.py | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index 5121284..6af7173 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -18,16 +18,16 @@ verbose=1, smFISH = True ) -print(f"The distance threshold is {decoder._distance_threshold}") -print(f"The lower magnitude threshold is {decoder._magnitude_threshold}") -print(f"The upper magnitude threshold is {decoder._upper_magnitude_threshold}") +# print(f"The distance threshold is {decoder._distance_threshold}") +# print(f"The lower magnitude threshold is {decoder._magnitude_threshold}") +# print(f"The upper magnitude threshold is {decoder._upper_magnitude_threshold}") # decode one tile decoder.decode_one_tile( tile_idx=0, # Specify the tile index display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma - magnitude_threshold=0.75, # L2-norm threshold + magnitude_threshold=0.2, # L2-norm threshold upper_magnitude_threshold=1.75, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode use_normalization=True, # Use normalization diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index ffafacf..6490e13 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -1982,6 +1982,10 @@ def decode_one_tile( if upper_magnitude_threshold is None: upper_magnitude_threshold = getattr(self, "_upper_magnitude_threshold", None) + print(f"The distance threshold is {self._distance_threshold}") + print(f"The lower magnitude threshold is {self._magnitude_threshold}") + print(f"The upper magnitude threshold is {self._upper_magnitude_threshold}") + self._tile_idx = tile_idx self._load_bit_data(ufish_threshold=ufish_threshold) if not (np.any(lowpass_sigma == 0)): From faa733d2d59f0713c9790ec440ec6ef46aa3f8c8 Mon Sep 17 00:00:00 2001 From: mspend Date: Thu, 3 Jul 2025 20:22:50 +0000 Subject: [PATCH 10/15] fixed something in the print statements --- src/merfish3danalysis/PixelDecoder.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 6490e13..85b3a44 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -1983,8 +1983,8 @@ def decode_one_tile( upper_magnitude_threshold = getattr(self, "_upper_magnitude_threshold", None) print(f"The distance threshold is {self._distance_threshold}") - print(f"The lower magnitude threshold is {self._magnitude_threshold}") - print(f"The upper magnitude threshold is {self._upper_magnitude_threshold}") + print(f"The lower magnitude threshold is {magnitude_threshold}") + print(f"The upper magnitude threshold is {upper_magnitude_threshold}") self._tile_idx = tile_idx self._load_bit_data(ufish_threshold=ufish_threshold) From 0b5bfa97deaada2f76e7d3e08730dc3cc22602fe Mon Sep 17 00:00:00 2001 From: mspend Date: Thu, 3 Jul 2025 21:35:35 +0000 Subject: [PATCH 11/15] removed some print statements and adjusted magnitude thresholds --- examples/bioprotean_smFISH/smFISH_decoding.py | 2 +- src/merfish3danalysis/PixelDecoder.py | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index 6af7173..7c03cb0 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -28,7 +28,7 @@ display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma magnitude_threshold=0.2, # L2-norm threshold - upper_magnitude_threshold=1.75, # Upper L2-norm threshold + upper_magnitude_threshold=1.05, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode use_normalization=True, # Use normalization ufish_threshold=0.5 # Ufish threshold diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 85b3a44..bbb6ddc 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -1884,14 +1884,12 @@ def on_close_callback(): for bit in range(self._datastore.num_bits): img = self._decoded_image[bit] - print("Adding image with shape:", img.shape) if img.ndim == 3: scale = [self._axial_step, self._pixel_size, self._pixel_size] elif img.ndim == 2: scale = [self._pixel_size, self._pixel_size] else: raise ValueError(f"Unexpected image dimension: {img.ndim}") - print("Using scale:", scale) viewer.add_image( img, scale=scale, From 87b13e41408347abe67eb28a32a43981540cbdef Mon Sep 17 00:00:00 2001 From: mspend Date: Fri, 4 Jul 2025 19:06:48 +0000 Subject: [PATCH 12/15] changed distance threshold --- examples/bioprotean_smFISH/smFISH_decoding.py | 2 +- src/merfish3danalysis/PixelDecoder.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index 7c03cb0..59e9015 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -28,7 +28,7 @@ display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma magnitude_threshold=0.2, # L2-norm threshold - upper_magnitude_threshold=1.05, # Upper L2-norm threshold + upper_magnitude_threshold=0.75, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode use_normalization=True, # Use normalization ufish_threshold=0.5 # Ufish threshold diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index bbb6ddc..98be2c2 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -114,7 +114,7 @@ def __init__( # establish lower threshold for magnitude for smFISH data self._magnitude_threshold = 0.75 self._upper_magnitude_threshold = 1.75 - self._distance_threshold = 1.0 + self._distance_threshold = 0.9 else: self._magnitude_threshold = 0.9 # default for HW4D4 code self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits From d08aac811522a85b53cdc6bc39076056ba7d4411 Mon Sep 17 00:00:00 2001 From: mspend Date: Mon, 21 Jul 2025 16:26:30 -0700 Subject: [PATCH 13/15] new threshold --- examples/bioprotean_smFISH/smFISH_decoding.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index 59e9015..c0e4158 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -28,7 +28,7 @@ display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma magnitude_threshold=0.2, # L2-norm threshold - upper_magnitude_threshold=0.75, # Upper L2-norm threshold + upper_magnitude_threshold=1.25, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode use_normalization=True, # Use normalization ufish_threshold=0.5 # Ufish threshold From 672671a5cf095c5b20a5c5ea9fcf4b8c6a5ed1fe Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 29 Jul 2025 15:00:02 -0700 Subject: [PATCH 14/15] modified display_results so it spits out decoded spots by bit - working code --- src/merfish3danalysis/PixelDecoder.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index 98be2c2..e6ff954 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -841,7 +841,6 @@ def _decode_pixels( ) if self._iterative_normalization_loaded: - # print('Iterative normalization vectors loaded in line 846') scaled_pixel_traces = self._scale_pixel_traces( scaled_pixel_traces, self._iterative_background_vector, @@ -849,7 +848,6 @@ def _decode_pixels( self._n_merfish_bits, ) elif self._global_normalization_loaded: - print('Iterative normalization vectors not loaded, so doing global normalization') scaled_pixel_traces = self._scale_pixel_traces( scaled_pixel_traces, self._global_background_vector, @@ -1873,7 +1871,7 @@ def on_close_callback(): viewer.add_image( self._scaled_pixel_images[bit], scale=[self._axial_step, self._pixel_size, self._pixel_size], - name="pixels_" + str(bit), + name="pixels_" + str(int(bit)+1), ) else: viewer.add_image( @@ -1881,19 +1879,18 @@ def on_close_callback(): scale=[self._axial_step, self._pixel_size, self._pixel_size], name="pixels", ) - + for bit in range(self._datastore.num_bits): - img = self._decoded_image[bit] - if img.ndim == 3: - scale = [self._axial_step, self._pixel_size, self._pixel_size] - elif img.ndim == 2: - scale = [self._pixel_size, self._pixel_size] - else: - raise ValueError(f"Unexpected image dimension: {img.ndim}") + # Create a mask for pixels decoded as this bit + mask = self._decoded_image == bit + # Create an image with intensities where mask is True, zeros elsewhere + decoded_intensity_image = np.zeros_like(self._magnitude_image) + decoded_intensity_image[mask] = self._magnitude_image[mask] + viewer.add_image( - img, - scale=scale, - name="decoded_" + str(bit), + decoded_intensity_image, + scale=[self._axial_step, self._pixel_size, self._pixel_size], + name="decoded_" + str(int(bit)+1), ) viewer.add_image( From 516ccd13ca916b4f8d7f8af6fbd2862eed873947 Mon Sep 17 00:00:00 2001 From: mspend Date: Tue, 29 Jul 2025 16:08:00 -0700 Subject: [PATCH 15/15] removed unecessary print statements --- examples/bioprotean_smFISH/smFISH_decoding.py | 12 +++--------- src/merfish3danalysis/PixelDecoder.py | 7 ++----- 2 files changed, 5 insertions(+), 14 deletions(-) diff --git a/examples/bioprotean_smFISH/smFISH_decoding.py b/examples/bioprotean_smFISH/smFISH_decoding.py index c0e4158..4b54833 100644 --- a/examples/bioprotean_smFISH/smFISH_decoding.py +++ b/examples/bioprotean_smFISH/smFISH_decoding.py @@ -18,23 +18,17 @@ verbose=1, smFISH = True ) -# print(f"The distance threshold is {decoder._distance_threshold}") -# print(f"The lower magnitude threshold is {decoder._magnitude_threshold}") -# print(f"The upper magnitude threshold is {decoder._upper_magnitude_threshold}") # decode one tile decoder.decode_one_tile( tile_idx=0, # Specify the tile index display_results=True, # Set to True to visualize results in Napari lowpass_sigma=(3, 1, 1), # Lowpass filter sigma - magnitude_threshold=0.2, # L2-norm threshold - upper_magnitude_threshold=1.25, # Upper L2-norm threshold + magnitude_threshold=0.75, # L2-norm threshold + upper_magnitude_threshold=1.75, # Upper L2-norm threshold minimum_pixels=3.0, # Minimum number of pixels for a barcode use_normalization=True, # Use normalization ufish_threshold=0.5 # Ufish threshold ) -print("Decoding complete.") - -# # Save barcodes -# decoder._save_barcodes() \ No newline at end of file +print("Decoding complete.") \ No newline at end of file diff --git a/src/merfish3danalysis/PixelDecoder.py b/src/merfish3danalysis/PixelDecoder.py index e6ff954..addaf9d 100644 --- a/src/merfish3danalysis/PixelDecoder.py +++ b/src/merfish3danalysis/PixelDecoder.py @@ -111,10 +111,10 @@ def __init__( self._iterative_normalization_loaded = False if self._smFISH: - # establish lower threshold for magnitude for smFISH data + # establish lower magnitude threshold for smFISH data self._magnitude_threshold = 0.75 self._upper_magnitude_threshold = 1.75 - self._distance_threshold = 0.9 + self._distance_threshold = 1.0 else: self._magnitude_threshold = 0.9 # default for HW4D4 code self._distance_threshold = 0.5172 # default for HW4D4 code. TO DO: calculate based on self._num_on-bits @@ -348,10 +348,8 @@ def _load_iterative_normalization_vectors(self): self._iterative_normalization_vector = cp.asarray(normalization_vector) self._iterative_background_vector = cp.asarray(background_vector) self._iterative_normalization_loaded = True - # print('Iterative normalization vectors loaded in line 351') else: self._iterative_normalization_vectors() - # print('Iterative normalization vectors loaded in line 354') def _iterative_normalization_vectors(self): """Calculate iterative normalization and background vectors.""" @@ -475,7 +473,6 @@ def _iterative_normalization_vectors(self): self._datastore.iterative_background_vector = barcode_based_background_vector self._iterative_normalization_loaded = True - # print('Iterative normalization vectors loaded in line 480') del df_barcodes_loaded_no_blanks gc.collect()