Image analysis for the mTor project
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.
 
 

122 lines
3.4 KiB

from collections import namedtuple
from functools import partial
from multiprocessing import Pool
from PIL import Image
from tqdm import tqdm
import numpy
from .luts import FIRE_LUT
STATS_FUNCS = {
"min": numpy.amin,
"max": numpy.amax,
"1quantile": partial(numpy.percentile, q=25),
"2quantile": partial(numpy.percentile, q=50),
"3quantile": partial(numpy.percentile, q=75),
"average": numpy.average,
"median": numpy.median,
"std": numpy.std,
}
LEFT_GUARD = "left"
ROI_NAME = "roi"
RIGHT_GUARD = "right"
ROI_BORDER_INTENSITY = 2**15
TifArray = namedtuple("TifArray", ["path", "frame", "data"])
def open_as_array(tif_image):
with tif_image.path.open("rb") as filehandle:
image = Image.open(filehandle)
image_array = numpy.array(image, dtype=numpy.int32)
return TifArray(
path=tif_image.path, frame=tif_image.frame, data=image_array
)
def scale_and_colorize(tif_array, settings):
adjusted_array = (tif_array.data - settings.offset) // settings.scale
# paint roi
top = settings.roi_top
bottom = settings.roi_bottom
right = settings.roi_right
left = settings.roi_left
adjusted_array[top, left:right] = ROI_BORDER_INTENSITY
adjusted_array[bottom, left:right] = ROI_BORDER_INTENSITY
adjusted_array[top:bottom, left] = ROI_BORDER_INTENSITY
adjusted_array[top:bottom, right] = ROI_BORDER_INTENSITY
adjusted = Image.fromarray(adjusted_array)
converted = adjusted.convert(mode="L").convert(mode="P")
converted.putpalette(FIRE_LUT)
return converted.convert("RGB")
def safe_color_coded(tif_array, settings):
png_image = scale_and_colorize(tif_array, settings)
png_path = settings.colored_dir / (tif_array.path.stem + ".jpg")
with png_path.open("wb") as outhandle:
png_image.save(outhandle)
del png_image
def safe_cut(tif_array, settings):
cut_path = settings.cuts_dir / (tif_array.path.stem + "_cut.tif")
top = settings.cut_top
bottom = settings.cut_bottom
right = settings.cut_right
left = settings.cut_left
with cut_path.open("wb") as outhandle:
cut_array = tif_array.data[top:bottom, left:right]
cut_img = Image.fromarray(cut_array)
cut_img.save(outhandle)
del cut_img
def get_roi(tif_array, settings):
top = settings.roi_top
bottom = settings.roi_bottom
right = settings.roi_right
left = settings.roi_left
return tif_array.data[top:bottom, left:right]
def analyse_roi(tif_array, settings):
data = get_roi(tif_array, settings)
results = {}
results.update(get_stats(data[:, 1], LEFT_GUARD))
results.update(get_stats(data[:, 1:-1], ROI_NAME))
results.update(get_stats(data[:, -1], RIGHT_GUARD))
return results
def get_stats(array, prefix):
return {f"{prefix}.{k}": f(array) for k, f in STATS_FUNCS.items()}
def process_one_tif(tif_image, settings):
tif_array = open_as_array(tif_image)
safe_color_coded(tif_array, settings)
safe_cut(tif_array, settings)
stats_result = analyse_roi(tif_array, settings)
stats_result["file"] = tif_image.path.name
stats_result["frame"] = tif_image.frame
return stats_result
def image_workflow(settings):
print("2/4: Image analysis and conversion")
func = partial(process_one_tif, settings=settings)
tif_list = settings.tif_list
num_items = len(tif_list)
with Pool(4) as p:
stat_results = list(tqdm(p.imap(func, tif_list), total=num_items))
return stat_results