Diviser pour régner#
Parfois, les programmes deviennent très longs et difficiles à lire. On parle de code spaghetti. Une façon de rendre le code plus facile à lire et à maintenir est de le diviser en fonctions plus petites et de les utiliser simplement dans des flux de travail plus complexes. Le principe de conception logicielle s’appelle Diviser pour régner.
from skimage.io import imread
from skimage.morphology import white_tophat, disk
from skimage.filters import gaussian, threshold_otsu
from skimage.measure import label, regionprops_table
import pandas as pd
import numpy as np
image = imread("../../data/blobs.tif")
footprint = disk(15)
background_subtracted = white_tophat(image,
footprint=footprint)
particle_radius = 5
denoised = gaussian(background_subtracted,
sigma=particle_radius)
binary = denoised > threshold_otsu(denoised)
labels = label(binary)
requested_measurements = ["label", "area", "mean_intensity"]
regionprops = regionprops_table(image,
labels,
properties=requested_measurements)
table = pd.DataFrame(regionprops)
mean_total_intensity = np.mean(table["area"] * table["mean_intensity"])
mean_total_intensity
17136.90322580645
Une façon courante et facile de rendre ce code plus lisible est de le diviser en sections qui commencent chacune par un commentaire.
# configuration
file_to_analyze = "../../data/blobs.tif"
background_subtraction_radius = 15
particle_radius = 5
requested_measurements = ["area", "mean_intensity"]
# load data
image = imread(file_to_analyze)
# preprocess image
footprint = disk(background_subtraction_radius)
background_subtracted = white_tophat(image,
footprint=footprint)
denoised = gaussian(background_subtracted,
sigma=particle_radius)
# segment image
binary = denoised > threshold_otsu(denoised)
labels = label(binary)
# extract features
regionprops = regionprops_table(image,
labels,
properties=requested_measurements)
table = pd.DataFrame(regionprops)
# descriptive statistics
mean_total_intensity = np.mean(table["area"] * table["mean_intensity"])
mean_total_intensity
17136.90322580645
Il serait plus professionnel de placer tout ce code dans des sous-routines significatives et de les appeler à partir d’une fonction centrale.
# reusable functions
def preprocess_image(image, background_subtraction_radius, particle_radius):
"""Apply background removal and denoising"""
footprint = disk(background_subtraction_radius)
background_subtracted = white_tophat(image, footprint=footprint)
denoised = gaussian(background_subtracted, sigma=particle_radius)
return denoised
def segment_image(image):
"""Apply thresholding and connected component analysis"""
binary = image > threshold_otsu(image)
labels = label(binary)
return labels
def extract_features(image, labels, requested_measurements):
"""Measure specified properties"""
regionprops = regionprops_table(image,
labels,
properties=requested_measurements)
table = pd.DataFrame(regionprops)
return table
Après avoir regroupé les étapes de traitement dans des fonctions, nous pouvons les appeler à partir d’une fonction principale. Cette fonction pourra ensuite être réutilisée pour appliquer la même procédure à toutes les images d’un dossier. Elle sert également d’index, d’aperçu du flux de travail de traitement d’image. En lisant simplement cette fonction, nous connaissons toutes les étapes de traitement et leurs paramètres.
def analyse_average_total_intensity(filename,
background_subtraction_radius = 15,
particle_radius = 5):
"""Load an image, segment objects and measure their mean total intensity."""
image = imread(filename)
denoised = preprocess_image(image,
background_subtraction_radius,
particle_radius)
labels = segment_image(denoised)
requested_measurements = ["area", "mean_intensity"]
table = extract_features(image,
labels,
requested_measurements)
# descriptive statistics
mean_total_intensity = np.mean(table["area"] * table["mean_intensity"])
return mean_total_intensity
# configuration
file_to_analyze = "../../data/blobs.tif"
Cette fonction centrale peut alors être lue facilement ; elle ne comporte que 6 lignes de code
analyse_average_total_intensity(file_to_analyze)
17136.90322580645
Cette fonction peut ensuite être réutilisée pour d’autres fichiers image.
analyse_average_total_intensity("../../data/BBBC007_batch/20P1_POS0005_D_1UL.tif")
884.2620087336245