You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
187 lines
7.4 KiB
187 lines
7.4 KiB
import pathlib |
|
|
|
from PIL import Image |
|
|
|
from .commons import RE_DIGITS, OUTPUT_FOLDER_NAMES, TifImage |
|
|
|
|
|
class Parameters(dict): |
|
""" namespacing the parameters for a analysis with nice access |
|
|
|
parmeters used for analysing the images |
|
|
|
boost: linear factor for brightness on color coded images |
|
charts_y_limit: common scale for the y axis in graphs |
|
colored_dir: path to the directory to hold color coded images |
|
cut_bottom: bottom edge for croping an image |
|
cut_left: left edge for croping an image |
|
cut_pad_x: pixels added to left or right edge to crop an image |
|
cut_pad_y: pixels added to top or bottom edge to crop an image |
|
cut_right: right edge for croping an image |
|
cut_top: left edge for croping an image |
|
cuts_dir: path to the directory of croped images |
|
data_dir: path to the directory for graphs and numeric data |
|
folder: name of the directory that holds the tif images |
|
guard_filter_polynom: polynomal scalar for smoothing the guards histogram |
|
guard_filter_window: window scalar for smoothing the guards histogram |
|
guard_histogram_bins: number of bins when constructing guards histogram |
|
guard_max_value: calculated threshold for the gurards |
|
guard_stats: which values to use for caclulation of guards |
|
guards_minima_min_dist: min distance between minimas in guards histogram |
|
image_height: height of an image |
|
image_width: width of an image |
|
intensity_max: highest intensity found in all images |
|
intensity_min: lowest intensity found in all images |
|
left_guard_column: name of the most used left guard value column |
|
left_guard_name: left guard value group name |
|
offset: calculated the lowest intensity to scale by |
|
outlier_upper_limit: calculated threshold value for outliers |
|
peak_min_distance: min distance for peaks in the final results |
|
peak_threshold: threshold peak height in the final results |
|
right_guard_column: name of the most used left guard value column |
|
right_guard_name: right guard value group name |
|
roi_bottom: bottom edge of the ROI |
|
roi_column: name for the ROI |
|
roi_left: bottom left of the ROI |
|
roi_name: roi value group name |
|
roi_right: bottom right of the ROI |
|
roi_stats: which values to use for caclulation of roi |
|
roi_top: top edge of the ROI |
|
rolling_min_window: size of the window for the rolling min calculation |
|
savgol_filter_polynom: polynomal scalar for smoothing the final results |
|
savgol_filter_window: window scalar for smoothing the final results |
|
scale: calculated factor to boost image intensities |
|
tif_dir: path to the directory with the original tif images |
|
|
|
""" |
|
|
|
def __init__(self, folder, top, right, bottom, left, **kargs): |
|
super().__init__(self) |
|
|
|
self.folder = folder |
|
|
|
# defaults |
|
self.boost = 5 |
|
self.cut_pad_x = 50 |
|
self.cut_pad_y = 25 |
|
self.guard_histogram_bins = 100 |
|
self.guard_filter_window = 7 |
|
self.guard_filter_polynom = 3 |
|
self.guards_minima_min_dist = 10 |
|
self.rolling_min_window = 5 |
|
self.savgol_filter_window = 51 |
|
self.savgol_filter_polynom = 1 |
|
self.peak_min_distance = 5 |
|
self.peak_threshold = 0.2 |
|
|
|
# labels for data items |
|
self.roi_name = "roi" |
|
self.roi_stats = "average" |
|
self.roi_column = f"{self.roi_name}.{self.roi_stats}" |
|
self.left_guard_name = "left" |
|
self.right_guard_name = "right" |
|
self.guard_stats = "average" |
|
self.left_guard_column = f"{self.left_guard_name}.{self.guard_stats}" |
|
self.right_guard_column = f"{self.right_guard_name}.{self.guard_stats}" |
|
|
|
# arbitrary keyword arguments, updating defaults if necessary |
|
self.update(kargs) |
|
|
|
# create list of images |
|
self.tif_dir = pathlib.Path(folder) |
|
if not self.tif_dir.is_dir(): |
|
raise IOError(f"Not a directory: {self.folder}") |
|
self.tif_list = self.scan_image_dir() |
|
|
|
# set the names of output directories |
|
for name in OUTPUT_FOLDER_NAMES: |
|
sub_path = self.tif_dir / name |
|
self[f"{name}_dir"] = sub_path |
|
|
|
# get image dimensions |
|
width, height = self.get_size_of_one_image() |
|
self.image_width = width |
|
self.image_height = height |
|
|
|
self.set_roi_and_cut_size(top, right, bottom, left) |
|
|
|
def __getattr__(self, key): |
|
""" return a parameters value as instance property """ |
|
return self[key] |
|
|
|
def __setattr__(self, key, value): |
|
""" set a parameters value via instance property """ |
|
self[key] = value |
|
|
|
def __getstate__(self): |
|
# Copy the object's state from self.__dict__ which contains |
|
# all our instance attributes. Always use the dict.copy() |
|
# method to avoid modifying the original state. |
|
return self.__dict__.copy() |
|
|
|
def __setstate__(self, state): |
|
# Restore instance attributes (i.e., filename and lineno). |
|
self.__dict__.update(state) |
|
|
|
def scan_image_dir(self): |
|
""" scan a folder for tif images """ |
|
visible = ( |
|
item |
|
for item in self.tif_dir.iterdir() |
|
if not item.stem.startswith(".") |
|
) |
|
tifs = (item for item in visible if item.suffix == ".tif") |
|
results = [] |
|
for path in tifs: |
|
try: |
|
match = RE_DIGITS.search(path.name).group() |
|
except AttributeError as ex: |
|
raise ValueError( |
|
f"no sequence number found in {path.name}" |
|
) from ex |
|
results.append(TifImage(path=path, frame=int(match))) |
|
return results |
|
|
|
def get_size_of_one_image(self): |
|
""" get the dimension of one Image |
|
|
|
the dimensions should be the same on all images. this is ensured in |
|
an other workflow. |
|
""" |
|
image = self.tif_list[0] |
|
with open(image.path, "rb") as filehandle: |
|
image = Image.open(filehandle) |
|
size = image.size |
|
return size |
|
|
|
def set_roi_and_cut_size(self, top, right, bottom, left): |
|
if right < left: |
|
left, right = right, left |
|
if top > bottom: |
|
top, bottom = bottom, top |
|
if ( |
|
top < 0 |
|
or left < 0 |
|
or bottom > self.image_height |
|
or right > self.image_width |
|
): |
|
raise ValueError("ROI out of bounce") |
|
|
|
self.roi_top = top |
|
self.roi_bottom = bottom |
|
self.roi_right = right |
|
self.roi_left = left |
|
|
|
self.cut_top = min(self.image_height, top - self.cut_pad_y) |
|
self.cut_bottom = max(0, bottom + self.cut_pad_y) |
|
self.cut_right = min(self.image_width, right + self.cut_pad_x) |
|
self.cut_left = max(0, left - self.cut_pad_x) |
|
|
|
def get_descriptions(self): |
|
docs = type(self).__doc__ |
|
lines = (line.strip() for line in docs.split("\n")) |
|
has_colon = (line for line in lines if ":" in line) |
|
pairs = (line.split(":") for line in has_colon) |
|
pairs_cleanup = ((k.strip(), v.strip()) for k, v in pairs) |
|
non_empty = ((k, v) for k, v in pairs_cleanup if k and v) |
|
return dict(non_empty)
|
|
|