7 changed files with 286 additions and 1 deletions
			
			
		| @ -0,0 +1,24 @@@@ -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 @@@@ -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 @@@@ -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