Holger Frey
4 years ago
7 changed files with 286 additions and 1 deletions
@ -0,0 +1,24 @@ |
|||||||
|
from pathlib import Path |
||||||
|
from PIL import Image, ImageDraw, ImageFont |
||||||
|
|
||||||
|
from sensospot_images import process |
||||||
|
from sensospot_data import parse_file |
||||||
|
from sensospot_data.parameters import _search_measurement_params_file |
||||||
|
|
||||||
|
from defusedxml import ElementTree |
||||||
|
|
||||||
|
import itertools |
||||||
|
|
||||||
|
THIS_FILE_DIR = Path(__file__).absolute().parent |
||||||
|
|
||||||
|
EXAMPLE_DATA_DIR = THIS_FILE_DIR / "example_data" |
||||||
|
EXAMPLE_IMAGE = EXAMPLE_DATA_DIR / "11Aug2020 NQC0329 Dry SG2-127-002_1_A01_1.tif" |
||||||
|
EXAMPLE_DATA = EXAMPLE_DATA_DIR / "11Aug2020 NQC0329 Dry SG2-127-002_1_A01_1.csv" |
||||||
|
|
||||||
|
OUTPUT_DIR = THIS_FILE_DIR / "example_output" |
||||||
|
old_output_files = [i for i in OUTPUT_DIR.iterdir() if i.is_file()] |
||||||
|
for path in old_output_files: |
||||||
|
path.unlink() |
||||||
|
|
||||||
|
|
||||||
|
process(EXAMPLE_DATA_DIR, OUTPUT_DIR) |
Binary file not shown.
@ -0,0 +1,90 @@ |
|||||||
|
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) |
@ -0,0 +1,54 @@ |
|||||||
|
from pathlib import Path |
||||||
|
from collections import namedtuple |
||||||
|
|
||||||
|
from defusedxml import ElementTree |
||||||
|
|
||||||
|
ArrayParameters = namedtuple( |
||||||
|
"ArrayParameters", ["size_x", "size_y", "dist_x", "dist_y"] |
||||||
|
) |
||||||
|
|
||||||
|
SpotParameters = namedtuple( |
||||||
|
"SpotParameters", |
||||||
|
["radius_spot", "radius_bkg", "roi_x", "roi_y", "crop_x", "crop_y"], |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
def _to_micro_meters(value): |
||||||
|
as_float = float(value) |
||||||
|
return int(as_float * 1000) |
||||||
|
|
||||||
|
|
||||||
|
def array_parameters_from_xml(tree): |
||||||
|
layout = tree.find("Layout") |
||||||
|
sx = int(layout.attrib["NofSpotsX"]) |
||||||
|
sy = int(layout.attrib["NofSpotsY"]) |
||||||
|
dx = _to_micro_meters(layout.attrib["SpotDistMmX"]) |
||||||
|
dy = _to_micro_meters(layout.attrib["SpotDistMmY"]) |
||||||
|
return ArrayParameters(sx, sy, dx, dy) |
||||||
|
|
||||||
|
|
||||||
|
def spot_parameters_from_xml(tree, array_parameters): |
||||||
|
array = tree.find("MicroArrayAnalysis").find("Settings") |
||||||
|
reg = tree.find("Registration").find("Settings") |
||||||
|
rs = _to_micro_meters(array.attrib["MinSpotSizeMm"]) |
||||||
|
rb = _to_micro_meters(array.attrib["MaxSpotSizeMm"]) |
||||||
|
rx = array_parameters.dist_x |
||||||
|
ry = array_parameters.dist_y |
||||||
|
# ROI is strangely named in params file, it's actually the outside crop |
||||||
|
cx = _to_micro_meters(reg.attrib["ROIMarginWidth"]) |
||||||
|
cy = _to_micro_meters(reg.attrib["ROIMarginHeight"]) |
||||||
|
return SpotParameters(rs, rb, rx, ry, cx, cy) |
||||||
|
|
||||||
|
|
||||||
|
def get_array_parameters(params_file): |
||||||
|
svary_file = Path(params_file).with_suffix(".svary") |
||||||
|
with svary_file.open("r") as file_handle: |
||||||
|
tree = ElementTree.parse(file_handle) |
||||||
|
return array_parameters_from_xml(tree) |
||||||
|
|
||||||
|
|
||||||
|
def get_spot_parameters(params_file, array_parameters): |
||||||
|
svalg_file = Path(params_file).with_suffix(".svalg") |
||||||
|
with svalg_file.open("r") as file_handle: |
||||||
|
tree = ElementTree.parse(file_handle) |
||||||
|
return spot_parameters_from_xml(tree, array_parameters) |
Loading…
Reference in new issue