Skip to content

Commit

Permalink
Fixed size update
Browse files Browse the repository at this point in the history
  • Loading branch information
c-h-benedetti committed Jan 14, 2025
1 parent f790148 commit 4ecaace
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 36 deletions.
2 changes: 1 addition & 1 deletion src/microglia_analyzer/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "1.0.3"
__version__ = "1.0.4"

import re

Expand Down
39 changes: 25 additions & 14 deletions src/microglia_analyzer/_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def segment_microglia_panel(self):
h_layout.addWidget(self.minimal_area_label)
self.minimal_area_input = QSpinBox()
self.minimal_area_input.setRange(0, 1000000)
self.minimal_area_input.setValue(40)
self.minimal_area_input.setValue(15)
self.minimal_area_input.valueChanged.connect(self.min_area_update)
h_layout.addWidget(self.minimal_area_input)
layout.addLayout(h_layout)
Expand Down Expand Up @@ -231,7 +231,7 @@ def measures_panel(self):

self.run_batch_button = QPushButton("▶ Run batch")
# self.run_batch_button.setFont(self.font)
self.run_batch_button.clicked.connect(self.run_batch)
self.run_batch_button.clicked.connect(self.batch_callback)
layout.addWidget(self.run_batch_button)

self.microglia_group.setLayout(layout)
Expand Down Expand Up @@ -291,6 +291,8 @@ def segment_microglia(self):
def update_seg_pp(self):
self.mam.set_cc_min_size(self.minimal_area_input.value())
self.mam.set_proba_threshold(self.probability_threshold_slider.value() / 100)
if _SEGMENTATION_LAYER_NAME not in self.viewer.layers:
return
self.mam.segmentation_postprocessing()
self.show_microglia()

Expand Down Expand Up @@ -330,13 +332,26 @@ def export_measures(self):

self.thread.start()

def batch_callback(self):
if self.thread is None:
self.run_batch()
else:
self.interupt_batch()

def interupt_batch(self):
print("!!! Interupting batch...")
show_info("Interupting batch...")
self.worker.interupt()
self.end_worker()
self.end_batch()

def run_batch(self):
sources = get_all_tiff_files(self.sources_folder)
print("Found sources: ", sources)
self.n_images = len(sources)
self.pbr = progress()
self.pbr.set_description("Running on folder...")
self.run_batch_button.setText(f"▶ Run batch ({str(1).zfill(2)}/{str(self.n_images).zfill(2)})")
self.run_batch_button.setText(f"■ Kill ({str(1).zfill(2)}/{str(self.n_images).zfill(2)})")
self.set_active_ui(False)
self.thread = QThread()

Expand All @@ -362,15 +377,14 @@ def run_batch(self):
# -------- Methods: ----------------------------------

def end_batch(self):
self.pbr.close()
self.set_active_ui(True)
self.end_worker()
self.n_images = 0
self.run_batch_button.setText("▶ Run batch")
show_info("Batch completed.")

def write_measures(self):
self.end_worker()
measures = self.mam.as_csv(self.images_combo.currentText())
measures = self.mam.as_tsv(self.images_combo.currentText())
skeleton = self.mam.skeleton
if _SKELETON_LAYER_NAME not in self.viewer.layers:
layer = self.viewer.add_image(skeleton, name=_SKELETON_LAYER_NAME, colormap='red', blending='additive')
Expand All @@ -382,7 +396,7 @@ def write_measures(self):
root_folder = os.path.join(self.sources_folder, "controls")
if not os.path.exists(root_folder):
os.makedirs(root_folder)
measures_path = os.path.join(root_folder, os.path.splitext(self.images_combo.currentText())[0] + "_measures.csv")
measures_path = os.path.join(root_folder, os.path.splitext(self.images_combo.currentText())[0] + "_measures.tsv")
control_path = os.path.join(root_folder, os.path.splitext(self.images_combo.currentText())[0] + "_control.tif")
tifffile.imwrite(control_path, np.stack([self.mam.skeleton, self.mam.mask], axis=0))
with open(measures_path, 'w') as f:
Expand Down Expand Up @@ -442,9 +456,9 @@ def set_active_ui(self, state):
self.minimal_area_input.setEnabled(state)
self.probability_threshold_slider.setEnabled(state)
self.classify_microglia_button.setEnabled(state)
self.run_batch_button.setEnabled(state)
self.run_batch_button.setEnabled(state)
self.export_measures_button.setEnabled(state)
# self.run_batch_button.setEnabled(state)


def end_worker(self):
if self.active_worker:
Expand All @@ -455,15 +469,12 @@ def end_worker(self):
self.thread.deleteLater()
self.set_active_ui(True)
self.total = -1
self.thread = None

def update_pbr(self, text, current, total):
self.pbr.set_description(text)
# if (total != self.total):
# self.pbr.reset(total=total)
# self.total = total
if (self.n_images > 0):
self.run_batch_button.setText(f"▶ Run batch ({str(current+1).zfill(2)}/{str(self.n_images).zfill(2)})")
# self.pbr.update(current)
self.run_batch_button.setText(f"■ Kill ({str(current+1).zfill(2)}/{str(self.n_images).zfill(2)})")

def clear_attributes(self):
self.sources_folder = None
Expand Down
21 changes: 11 additions & 10 deletions src/microglia_analyzer/ma_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def set_classification_model(self, path, use="best", reload=False):
In there, there must be a "weights" folder, containing either 'best.pt' or 'last.pt'.
Args:
- path (str): Path of the model's folder (containing 'results.csv' and 'weights').
- path (str): Path of the model's folder (containing 'results.tsv' and 'weights').
- use (str): Either 'best' or 'last', to use either 'best.pt' or 'last.pt'.
- reload (bool): Whether to force the reload of the model from the online repo.
"""
Expand Down Expand Up @@ -222,7 +222,9 @@ def filter_cc_by_size(self, mask, connectivity=2):
"""
labeled_map = label(mask, connectivity=connectivity)
regions = regionprops(labeled_map)
labels_to_keep = [region.label for region in regions if region.area >= self.cc_min_size]
n_pixels = int(self.cc_min_size / (self.calibration[0]**2))
print(f"Removed items smaller than {self.cc_min_size} µm² ({n_pixels} pixels)")
labels_to_keep = [region.label for region in regions if region.area >= n_pixels]

if not labels_to_keep:
return np.zeros_like(mask, dtype=np.uint8)
Expand All @@ -233,9 +235,8 @@ def filter_cc_by_size(self, mask, connectivity=2):
def segmentation_postprocessing(self):
self.mask = (self.probability_map > self.segmentation_threshold).astype(np.uint8)
self.mask = self.filter_cc_by_size(self.mask)
selem = morphology.diamond(4)
selem = morphology.diamond(2)
self.mask = morphology.binary_closing(self.mask, selem)
# self.mask = morphology.binary_fill_holes(self.mask)
self.mask = label(self.mask, connectivity=2)

def _filter_garbage(self, garbage=0):
Expand Down Expand Up @@ -380,7 +381,7 @@ def sort_labels_by_class(self, data, valid_labels):
sorted_labels = sorted(filtered.keys(), key=lambda label: filtered[label][0])
return sorted_labels

def as_csv(self, identifier):
def as_tsv(self, identifier):
common_labels = set(self.graph_metrics.keys()) & set(self.bindings.keys())
if len(common_labels) == 0:
return None
Expand All @@ -389,7 +390,7 @@ def as_csv(self, identifier):
first_label = sorted_labels[0]
graph_measure_keys = list(self.graph_metrics[first_label].keys())
headers = ["Identifier"] + graph_measure_keys + ["IoU", "Class"]
buffer = [", ".join(headers)]
buffer = ["\t ".join(headers)]

for i, label in enumerate(sorted_labels):
values = [""]
Expand All @@ -399,7 +400,7 @@ def as_csv(self, identifier):
class_value, iou = self.bindings[label][:2]
class_value = self.classes[int(class_value)] if class_value is not None else ""
values += [graph_measures[key] for key in graph_measure_keys] + [iou, class_value]
line = ", ".join([str(v) for v in values])
line = "\t ".join([str(v) for v in values])
buffer.append(line)

return buffer
Expand All @@ -419,6 +420,6 @@ def as_csv(self, identifier):
ma.classification_postprocessing()
ma.bind_classifications()
ma.analyze_as_graph()
csv = ma.as_csv("adulte 3")
with open("/tmp/metrics.csv", "w") as f:
f.write("\n".join(csv))
tsv = ma.as_tsv("adulte 3")
with open("/tmp/metrics.tsv", "w") as f:
f.write("\n".join(tsv))
36 changes: 25 additions & 11 deletions src/microglia_analyzer/qt_workers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from qtpy.QtCore import QObject
from PyQt5.QtCore import pyqtSignal
from PyQt5.QtCore import pyqtSignal, pyqtSlot
import requests
import os
import numpy as np
Expand Down Expand Up @@ -119,14 +119,21 @@ class QtBatchRunners(QObject):

finished = pyqtSignal()
update = pyqtSignal(str, int, int)
to_kill = pyqtSignal()

def __init__(self, pbr, source_dir, settings):
super().__init__()
self.to_kill.connect(self.interupt)
self.pbr = pbr
self.source_dir = source_dir
self.settings = settings
self.images_pool = get_all_tiff_files(source_dir)
self.csv_lines = []
self.tsv_lines = []
self.is_condamned = False

@pyqtSlot()
def interupt(self):
self.is_condamned = True

def workflow(self, index):
img_path = os.path.join(self.source_dir, self.images_pool[index])
Expand All @@ -146,21 +153,28 @@ def workflow(self, index):
ma.classification_postprocessing()
ma.bind_classifications()
ma.analyze_as_graph()
csv = ma.as_csv(self.images_pool[index])
tsv = ma.as_tsv(self.images_pool[index])
if index == 0:
self.csv_lines += csv
self.tsv_lines += tsv
else:
self.csv_lines += csv[1:]
self.tsv_lines += tsv[1:]
classified = np.zeros_like(ma.mask)
for (cls, _, seg_bbox) in ma.bindings.values():
classified[seg_bbox[0]:seg_bbox[2], seg_bbox[1]:seg_bbox[3]] = int(cls)
mask = (ma.mask > 0).astype(np.uint8) * classified
control_path = os.path.join(self.source_dir, "controls", self.images_pool[index])
tifffile.imwrite(control_path, np.stack([ma.skeleton, ma.mask], axis=0))
tifffile.imwrite(control_path, np.stack([ma.skeleton, mask], axis=0))

def write_csv(self):
with open(os.path.join(self.source_dir, "controls", "results.csv"), 'w') as f:
f.write("\n".join(self.csv_lines))
def write_tsv(self):
with open(os.path.join(self.source_dir, "controls", "results.tsv"), 'w') as f:
f.write("\n".join(self.tsv_lines))

def run(self):
for i in range(len(self.images_pool)):
if self.is_condamned:
return
print(f"=== [{str(i+1).zfill(2)}/{str(len(self.images_pool)).zfill(2)}] Processing {self.images_pool[i]}. ===")
self.workflow(i)
self.write_csv()
self.write_tsv()
self.update.emit(self.images_pool[i], i+1, len(self.images_pool))
self.finished.emit()
self.finished.emit()

0 comments on commit 4ecaace

Please sign in to comment.