Teile und herrsche#
Manchmal werden Programme sehr lang und schwer zu lesen. Wir sprechen von Spaghetti-Code. Eine Möglichkeit, Code leichter lesbar und wartbar zu machen, besteht darin, ihn in kleinere Funktionen zu unterteilen und diese in komplexeren Workflows zu verwenden. Das Softwaredesign-Prinzip wird Teile und herrsche genannt.
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
Eine übliche und einfache Möglichkeit, solchen Code leichter lesbar zu machen, besteht darin, ihn in Abschnitte zu unterteilen, die jeweils mit einem Kommentar beginnen.
# 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
Professioneller wäre es, diesen gesamten Code in sinnvolle Unterroutinen zu packen und sie von einer zentralen Funktion aus aufzurufen.
# 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
Nachdem wir Gruppen von Verarbeitungsschritten in Funktionen gepackt haben, können wir sie von einer Hauptfunktion aus aufrufen. Diese Funktion kann später wiederverwendet werden, um das gleiche Verfahren auf alle Bilder in einem Ordner anzuwenden. Sie dient auch als Index, als Überblick über den Bildverarbeitungsworkflow. Durch das Lesen nur dieser Funktion kennen wir alle Verarbeitungsschritte und deren Parameter.
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"
Diese zentrale Funktion kann dann leicht gelesen werden; sie hat nur 6 Codezeilen
analyse_average_total_intensity(file_to_analyze)
17136.90322580645
Diese Funktion kann dann auch für andere Bilddateien wiederverwendet werden.
analyse_average_total_intensity("../../data/BBBC007_batch/20P1_POS0005_D_1UL.tif")
884.2620087336245