from pathlib import Path from collections import namedtuple import numpy from PIL import Image, ImageDraw, ImageFont Point = namedtuple("Point", ["x", "y"]) Spot = namedtuple("Spot", ["found", "x", "y"]) THIS_DIR = Path(__file__).absolute().parent FONT_PATH = THIS_DIR / "arial.ttf" FONT = ImageFont.truetype(str(FONT_PATH), 32) GREEN = (0, 255, 0) RED = (255, 0, 0) def recalculate(iterable, factor): recalculated = [v * factor for v in iterable] cls = type(iterable) try: return cls(*recalculated) except TypeError: return tuple(recalculated) def convert_16bit_to_8bit(img): array = numpy.uint8(numpy.array(img) / 256) return Image.fromarray(array) def convert_16bit_grey_to_color(img): return convert_16bit_to_8bit(img).convert(mode="RGB") def resize(img, scale=1): return img.resize(recalculate(img.size, scale)) def load_array_image(file_path, scale=1): img = Image.open(file_path) colored = convert_16bit_grey_to_color(img) return resize(colored, scale) def get_position(data_series, actual=True): prefix = "Pos" if actual else "Pos.Nom" x = int(data_series[f"{prefix}.X"]) y = int(data_series[f"{prefix}.Y"]) return Point(x, y) def get_box(center, width, height=None): height = width if height is None else height dx = width / 2 dy = height / 2 return (center.x - dx, center.y - dy, center.x + dx, center.y + dy) def annotate_spot(canvas, spot_parameters, spot_data, scale=1): found = spot_data["Spot.Found"] center = recalculate(get_position(spot_data, found), scale) color = GREEN if found else RED box = get_box(center, spot_parameters.roi_x, spot_parameters.roi_y) canvas.rectangle(box, outline=color, width=1) box = get_box(center, spot_parameters.radius_bkg) canvas.ellipse(box, outline=color, width=2) box = get_box(center, spot_parameters.radius_spot) canvas.ellipse(box, outline=color, width=1) canvas.text(center, str(spot_data["Pos.Id"]), RED, font=FONT) def annotate_image(array_img, spot_parameters, array_data, scale): canvas = ImageDraw.Draw(array_img) for index, spot_data in array_data.iterrows(): annotate_spot(canvas, spot_parameters, spot_data, scale) def crop(array_img, spot_parameters, spot_data, scale=1): found = spot_data["Spot.Found"] center = recalculate(get_position(spot_data, found), scale) box = get_box(center, spot_parameters.crop_x, spot_parameters.crop_y) return array_img.crop(box)