|
|
|
@ -1,9 +1,9 @@
@@ -1,9 +1,9 @@
|
|
|
|
|
""" Sensospot Images |
|
|
|
|
|
|
|
|
|
Creating nice spot images from scans |
|
|
|
|
Creating nice grid images from scans |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
__version__ = "0.0.1" |
|
|
|
|
__version__ = "0.1.0" |
|
|
|
|
|
|
|
|
|
import sys |
|
|
|
|
from pathlib import Path |
|
|
|
@ -24,12 +24,21 @@ from .parameters import get_spot_parameters, get_array_parameters
@@ -24,12 +24,21 @@ from .parameters import get_spot_parameters, get_array_parameters
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def calulate_pixel_size(data_frame, array_definition): |
|
|
|
|
"""calculate the pixel size in micrometers |
|
|
|
|
|
|
|
|
|
data_frame: the numerical analysis from sensospot of one image |
|
|
|
|
array_definition: named tuple with sensospot settings |
|
|
|
|
|
|
|
|
|
returns: pixel size in micrometers |
|
|
|
|
""" |
|
|
|
|
# get the first and last probe position in the data |
|
|
|
|
first = get_position(data_frame.iloc[0], actual=False) |
|
|
|
|
last = get_position(data_frame.iloc[-1], actual=False) |
|
|
|
|
|
|
|
|
|
x_dist_pixel = last.x - first.x |
|
|
|
|
y_dist_pixel = last.y - first.y |
|
|
|
|
|
|
|
|
|
# calculate the distance between the first and last spot on x and y axis |
|
|
|
|
ad = array_definition |
|
|
|
|
x_dist_um = ad.dist_x * (ad.size_x - 1) |
|
|
|
|
y_dist_um = ad.dist_y * (ad.size_y - 1) |
|
|
|
@ -49,6 +58,16 @@ def calulate_pixel_size(data_frame, array_definition):
@@ -49,6 +58,16 @@ def calulate_pixel_size(data_frame, array_definition):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_example_data_path(input_dir): |
|
|
|
|
"""returns the path to one data file in the scan folder |
|
|
|
|
|
|
|
|
|
this will search an array image file first and use this as a template |
|
|
|
|
filename for a csv file. Some raw data folders contain csv files that |
|
|
|
|
are unrelated to image files and should be avoided. |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory to search in |
|
|
|
|
|
|
|
|
|
returns: path to one csv file |
|
|
|
|
""" |
|
|
|
|
input_path = Path(input_dir) |
|
|
|
|
tif_files = input_path.glob("*.tif") |
|
|
|
|
example_tif = next(tif_files) |
|
|
|
@ -56,6 +75,16 @@ def get_example_data_path(input_dir):
@@ -56,6 +75,16 @@ def get_example_data_path(input_dir):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_filename_prefix(input_dir): |
|
|
|
|
"""returns the common file name prefix for tif and csv files |
|
|
|
|
|
|
|
|
|
The raw data in the scanfolder has the format: |
|
|
|
|
`[prefix]_[well]_[exposure].[suffix]` |
|
|
|
|
To be able to find all well and exposure files, the prefix is needed. |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory to search in |
|
|
|
|
|
|
|
|
|
returns: the prefix for array analysis files |
|
|
|
|
""" |
|
|
|
|
file_path = get_example_data_path(input_dir) |
|
|
|
|
example_name = file_path.stem |
|
|
|
|
prefix, well, exposure = example_name.rsplit("_", 2) |
|
|
|
@ -63,6 +92,17 @@ def get_filename_prefix(input_dir):
@@ -63,6 +92,17 @@ def get_filename_prefix(input_dir):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def retrieve_spot_parameters(input_dir, scale): |
|
|
|
|
"""returns the spot parameter settings of the analysis |
|
|
|
|
|
|
|
|
|
All setting parameters are stored in a special folder in the raw data |
|
|
|
|
directory. This parses the files needed and recalculates the values |
|
|
|
|
in micrometers to pixels, |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory to search in |
|
|
|
|
scale: scale factor for the image |
|
|
|
|
|
|
|
|
|
returns: a named tuple with spot parameters |
|
|
|
|
""" |
|
|
|
|
parameters_path = _search_measurement_params_file(input_dir) |
|
|
|
|
if parameters_path is None: |
|
|
|
|
sys.exit(f"Could not find parameter files in {input_dir}") |
|
|
|
@ -79,6 +119,14 @@ def retrieve_spot_parameters(input_dir, scale):
@@ -79,6 +119,14 @@ def retrieve_spot_parameters(input_dir, scale):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def search_image_files(input_dir, wells, exposures): |
|
|
|
|
"""searches for tif files in the raw data directory |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory to search in |
|
|
|
|
wells: glob pattern for well matching |
|
|
|
|
exposures: glob pattern for exposure id matching |
|
|
|
|
|
|
|
|
|
return: iterator of tif file paths |
|
|
|
|
""" |
|
|
|
|
input_path = Path(input_dir) |
|
|
|
|
prefix = get_filename_prefix(input_path) |
|
|
|
|
tmp_pattern = f"{prefix}_*{wells}*_*{exposures}.tif" |
|
|
|
@ -87,10 +135,23 @@ def search_image_files(input_dir, wells, exposures):
@@ -87,10 +135,23 @@ def search_image_files(input_dir, wells, exposures):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_file_map(input_dir, wells, exposures): |
|
|
|
|
"""creates a dictionary relating array data files to image files |
|
|
|
|
|
|
|
|
|
Only the first exposure of an exposure series is used for spot location, |
|
|
|
|
therfore only the data of the exposure id 1 should be used for any |
|
|
|
|
exposure of the well |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory to search in |
|
|
|
|
wells: glob pattern for well matching |
|
|
|
|
exposures: glob pattern for exposure id matching |
|
|
|
|
|
|
|
|
|
returns: dict, data file paths as keys, list of image file paths as values |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
file_map = {} |
|
|
|
|
for tif_path in search_image_files(input_dir, wells, exposures): |
|
|
|
|
rest, exposure = tif_path.stem.rsplit("_", 1) |
|
|
|
|
csv_path = tif_path.parent / f"{rest}_1.csv" |
|
|
|
|
well, rest = tif_path.stem.rsplit("_", 1) |
|
|
|
|
csv_path = tif_path.parent / f"{well}_1.csv" |
|
|
|
|
if csv_path.is_file(): |
|
|
|
|
if csv_path not in file_map: |
|
|
|
|
file_map[csv_path] = [] |
|
|
|
@ -98,15 +159,32 @@ def create_file_map(input_dir, wells, exposures):
@@ -98,15 +159,32 @@ def create_file_map(input_dir, wells, exposures):
|
|
|
|
|
return file_map |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def process_image(image_file, spot_parameters, spot_data, scale): |
|
|
|
|
def process_image(image_file, spot_parameters, array_data, scale): |
|
|
|
|
"""load and annotate image |
|
|
|
|
|
|
|
|
|
image_file: file path to array image |
|
|
|
|
spot_parameters: settings of spot analysis |
|
|
|
|
array_data: numerical data of analysed image |
|
|
|
|
scale: scale factor for the image |
|
|
|
|
|
|
|
|
|
returns: annotated image file |
|
|
|
|
""" |
|
|
|
|
img = load_array_image(image_file, scale=scale) |
|
|
|
|
annotate_image(img, spot_parameters, spot_data, scale) |
|
|
|
|
annotate_image(img, spot_parameters, array_data, scale) |
|
|
|
|
return img |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_crops( |
|
|
|
|
output_path, img, image_path, spot_parameters, array_data, scale |
|
|
|
|
): |
|
|
|
|
"""create cropped images for each spot |
|
|
|
|
|
|
|
|
|
output_path: directory to store the cropped images in |
|
|
|
|
image_file: file path to array image |
|
|
|
|
spot_parameters: settings of spot analysis |
|
|
|
|
array_data: numerical data of analysed image |
|
|
|
|
scale: scale factor for the image |
|
|
|
|
""" |
|
|
|
|
base_name = image_path.stem |
|
|
|
|
for index, spot_data in array_data.iterrows(): |
|
|
|
|
cropped_img = crop(img, spot_parameters, spot_data, scale) |
|
|
|
@ -120,8 +198,17 @@ def process(
@@ -120,8 +198,17 @@ def process(
|
|
|
|
|
wells="*", |
|
|
|
|
exposures="*", |
|
|
|
|
scale=3, |
|
|
|
|
add_single_spots=False, |
|
|
|
|
crop_single_spots=False, |
|
|
|
|
): |
|
|
|
|
"""process a complete raw data directory |
|
|
|
|
|
|
|
|
|
input_dir: the raw data directory |
|
|
|
|
output_dir: directory to store the cropped images in |
|
|
|
|
wells: glob pattern for well matching |
|
|
|
|
exposures: glob pattern for exposure id matching |
|
|
|
|
scale: scale factor for the output images |
|
|
|
|
crop_single_spots: create cropped images for each spot |
|
|
|
|
""" |
|
|
|
|
spot_parameters = retrieve_spot_parameters(input_dir, scale) |
|
|
|
|
file_map = create_file_map(input_dir, wells, exposures) |
|
|
|
|
output_path = Path(output_dir) |
|
|
|
@ -133,7 +220,7 @@ def process(
@@ -133,7 +220,7 @@ def process(
|
|
|
|
|
for image_path in image_files: |
|
|
|
|
img = process_image(image_path, spot_parameters, array_data, scale) |
|
|
|
|
img.save(output_path / image_path.name) |
|
|
|
|
if add_single_spots: |
|
|
|
|
if crop_single_spots: |
|
|
|
|
create_crops( |
|
|
|
|
output_path, |
|
|
|
|
img, |
|
|
|
@ -184,18 +271,26 @@ def process(
@@ -184,18 +271,26 @@ def process(
|
|
|
|
|
help="scale-up of images", |
|
|
|
|
) |
|
|
|
|
@click.option( |
|
|
|
|
"--spots", |
|
|
|
|
"--crop", |
|
|
|
|
default=False, |
|
|
|
|
is_flag=True, |
|
|
|
|
show_default=True, |
|
|
|
|
help="include cropped images of spots", |
|
|
|
|
help="create cropped images of each spots", |
|
|
|
|
) |
|
|
|
|
def run(source, output=None, wells="*", exposures="*", scale=3, spots=False): |
|
|
|
|
def run(source, output=None, wells="*", exposures="*", scale=3, crop=False): |
|
|
|
|
"""command line interface to process a complete raw data directory |
|
|
|
|
|
|
|
|
|
source: the raw data directory |
|
|
|
|
output: directory to store the cropped images in |
|
|
|
|
wells: glob pattern for well matching |
|
|
|
|
exposures: glob pattern for exposure id matching |
|
|
|
|
scale: scale factor for the output images |
|
|
|
|
crop: create cropped images for each spot |
|
|
|
|
""" |
|
|
|
|
source = Path(source) |
|
|
|
|
if output is None or not Path(output).is_dir(): |
|
|
|
|
parent = source.absolute().parent |
|
|
|
|
now = datetime.now().strftime("%Y-%m-%d %H-%M-%S") |
|
|
|
|
output = parent / now |
|
|
|
|
output = source.absolute().parent / now |
|
|
|
|
output.mkdir(exist_ok=True) |
|
|
|
|
|
|
|
|
|
process(source, output, wells, exposures, scale, spots) |
|
|
|
|
process(source, output, wells, exposures, scale, crop) |
|
|
|
|