# HG changeset patch
# User imgteam
# Date 1727458881 0
# Node ID de611b3b5ae86f6c5bcad29986f0df6c7d112d3e
# Parent 30ca5d5d03ec6d770971d6856ebc0559c8584606
planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/points2labelimage/ commit 6fc9ab8db9ef72ac7ded30d7373768feeae9390d
diff -r 30ca5d5d03ec -r de611b3b5ae8 creators.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/creators.xml Fri Sep 27 17:41:21 2024 +0000
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -r 30ca5d5d03ec -r de611b3b5ae8 points2label.py
--- a/points2label.py Mon Nov 13 22:11:46 2023 +0000
+++ b/points2label.py Fri Sep 27 17:41:21 2024 +0000
@@ -1,47 +1,125 @@
import argparse
-import sys
+import os
import warnings
+import giatools.pandas
import numpy as np
import pandas as pd
+import scipy.ndimage as ndi
import skimage.io
+import skimage.segmentation
-def points2label(labels, shape, output_file=None, has_header=False, is_TSV=True):
- labelimg = np.zeros([shape[0], shape[1]], dtype=np.int32)
+def rasterize(point_file, out_file, shape, has_header=False, swap_xy=False, bg_value=0, fg_value=None):
- if is_TSV:
+ img = np.full(shape, dtype=np.uint16, fill_value=bg_value)
+ if os.path.exists(point_file) and os.path.getsize(point_file) > 0:
+
+ # Read the tabular file with information from the header
if has_header:
- df = pd.read_csv(labels, sep='\t', skiprows=1, header=None)
- else:
- df = pd.read_csv(labels, sep='\t', header=None)
- else:
- if has_header:
- df = pd.read_csv(labels, skiprows=1, header=None)
+ df = pd.read_csv(point_file, delimiter='\t')
+
+ pos_x_column = giatools.pandas.find_column(df, ['pos_x', 'POS_X'])
+ pos_y_column = giatools.pandas.find_column(df, ['pos_y', 'POS_Y'])
+ pos_x_list = df[pos_x_column].round().astype(int)
+ pos_y_list = df[pos_y_column].round().astype(int)
+ assert len(pos_x_list) == len(pos_y_list)
+
+ try:
+ radius_column = giatools.pandas.find_column(df, ['radius', 'RADIUS'])
+ radius_list = df[radius_column]
+ assert len(pos_x_list) == len(radius_list)
+ except KeyError:
+ radius_list = [0] * len(pos_x_list)
+
+ try:
+ label_column = giatools.pandas.find_column(df, ['label', 'LABEL'])
+ label_list = df[label_column]
+ assert len(pos_x_list) == len(label_list)
+ except KeyError:
+ label_list = list(range(1, len(pos_x_list) + 1))
+
+ # Read the tabular file without header
else:
- df = pd.read_csv(labels, header=None)
+ df = pd.read_csv(point_file, header=None, delimiter='\t')
+ pos_x_list = df[0].round().astype(int)
+ pos_y_list = df[1].round().astype(int)
+ assert len(pos_x_list) == len(pos_y_list)
+ radius_list = [0] * len(pos_x_list)
+ label_list = list(range(1, len(pos_x_list) + 1))
+
+ # Optionally swap the coordinates
+ if swap_xy:
+ pos_x_list, pos_y_list = pos_y_list, pos_x_list
- for i in range(0, len(df)):
- a_row = df.iloc[i]
- labelimg[a_row[0], a_row[1]] = i + 1
+ # Perform the rasterization
+ for y, x, radius, label in zip(pos_y_list, pos_x_list, radius_list, label_list):
+ if fg_value is not None:
+ label = fg_value
+
+ if y < 0 or x < 0 or y >= shape[0] or x >= shape[1]:
+ raise IndexError(f'The point x={x}, y={y} exceeds the bounds of the image (width: {shape[1]}, height: {shape[0]})')
+
+ # Rasterize circle and distribute overlapping image area
+ if radius > 0:
+ mask = np.ones(shape, dtype=bool)
+ mask[y, x] = False
+ mask = (ndi.distance_transform_edt(mask) <= radius)
- if output_file is not None:
- with warnings.catch_warnings():
- warnings.simplefilter("ignore")
- skimage.io.imsave(output_file, labelimg, plugin='tifffile')
+ # Compute the overlap (pretend there is none if the rasterization is binary)
+ if fg_value is None:
+ overlap = np.logical_and(img > 0, mask)
+ else:
+ overlap = np.zeros(shape, dtype=bool)
+
+ # Rasterize the part of the circle which is disjoint from other foreground.
+ #
+ # In the current implementation, the result depends on the order of the rasterized circles if somewhere
+ # more than two circles overlap. This is probably negligable for most applications. To achieve results
+ # that are invariant to the order, first all circles would need to be rasterized independently, and
+ # then blended together. This, however, would either strongly increase the memory consumption, or
+ # require a more complex implementation which exploits the sparsity of the rasterized masks.
+ #
+ disjoint_mask = np.logical_xor(mask, overlap)
+ if disjoint_mask.any():
+ img[disjoint_mask] = label
+
+ # Distribute the remaining part of the circle
+ if overlap.any():
+ dist = ndi.distance_transform_edt(overlap)
+ foreground = (img > 0)
+ img[overlap] = 0
+ img = skimage.segmentation.watershed(dist, img, mask=foreground)
+
+ # Rasterize point (there is no overlapping area to be distributed)
+ else:
+ img[y, x] = label
+
else:
- return labelimg
+ raise Exception("{} is empty or does not exist.".format(point_file)) # appropriate built-in error?
+
+ with warnings.catch_warnings():
+ warnings.simplefilter("ignore")
+ skimage.io.imsave(out_file, img, plugin='tifffile') # otherwise we get problems with the .dat extension
if __name__ == "__main__":
parser = argparse.ArgumentParser()
- parser.add_argument('label_file', type=argparse.FileType('r'), default=sys.stdin, help='label file')
- parser.add_argument('out_file', type=argparse.FileType('w'), default=sys.stdin, help='out file')
- parser.add_argument('org_file', type=argparse.FileType('r'), default=sys.stdin, help='input original file')
- parser.add_argument('--has_header', dest='has_header', type=bool, default=False, help='label file has header')
- parser.add_argument('--is_tsv', dest='is_tsv', type=bool, default=True, help='label file is TSV')
+ parser.add_argument('point_file', type=argparse.FileType('r'), help='point file')
+ parser.add_argument('out_file', type=str, help='out file (TIFF)')
+ parser.add_argument('shapex', type=int, help='shapex')
+ parser.add_argument('shapey', type=int, help='shapey')
+ parser.add_argument('--has_header', dest='has_header', default=False, help='set True if point file has header')
+ parser.add_argument('--swap_xy', dest='swap_xy', default=False, help='Swap X and Y coordinates')
+ parser.add_argument('--binary', dest='binary', default=False, help='Produce binary image')
+
args = parser.parse_args()
- original_shape = skimage.io.imread(args.org_file.name, plugin='tifffile').shape
-
- points2label(args.label_file.name, original_shape, args.out_file.name, args.has_header, args.is_tsv)
+ rasterize(
+ args.point_file.name,
+ args.out_file,
+ (args.shapey, args.shapex),
+ has_header=args.has_header,
+ swap_xy=args.swap_xy,
+ fg_value=0xffff if args.binary else None,
+ )
diff -r 30ca5d5d03ec -r de611b3b5ae8 points2label.xml
--- a/points2label.xml Mon Nov 13 22:11:46 2023 +0000
+++ b/points2label.xml Fri Sep 27 17:41:21 2024 +0000
@@ -1,41 +1,121 @@
-
+
+
+ creators.xml
+ tests.xml
+ 0.4
+ 0
+
+
+
+
operation_3443
galaxy_image_analysis
-
- numpy
- scikit-image
- pandas
- tifffile
-
-
-
-
-
-
-
-
+
+ scikit-image
+ numpy
+ pandas
+ tifffile
+ giatools
+
+
+
+
+
+
+
+
+
-
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- **What it does**
+
+ **Converts a tabular list of points to a label map by rasterizing the point coordinates.**
+
+ The created image is a single-channel image with 16 bits per pixel (unsigned integer). The points are
+ rasterized with unique labels, or the value 65535 (white) for binary image output. Pixels not corresponding to
+ any points in the tabular file are assigned the value 0 (black).
- This tool converts points to a label image.
+ The tabular list of points can either be header-less. In this case, the first and second columns are expected
+ to be the X and Y coordinates, respectively. Otherwise, if a header is present, it is searched for the
+ following column names:
+
+ - ``pos_x`` or ``POS_X``: This column corresponds to the X coordinates.
+ - ``pos_y`` or ``POS_Y``: This column corresponds to the Y coordinates.
+ - If a ``radius`` or ``RADIUS`` column is present, then the points will be rasterized as circles of the
+ corresponding radii.
+ - If a ``label`` or ``LABEL`` column is present, then the corresponding labels will be used for rasterization
+ (unless "Produce binary image" is activated). Different points are allowed to use the same label.
+
10.1016/j.jbiotec.2017.07.019
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/galaxyIcon_noText.tif
Binary file test-data/galaxyIcon_noText.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/input1.tsv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/input1.tsv Fri Sep 27 17:41:21 2024 +0000
@@ -0,0 +1,9 @@
+11.7555970149 10.4048507463
+15 14
+19 2
+5 4
+5 5
+5 6
+5 7
+5 8
+5 9
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/input2.tsv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/input2.tsv Fri Sep 27 17:41:21 2024 +0000
@@ -0,0 +1,39 @@
+frame pos_x pos_y scale radius intensity
+1 85 32 1.33 3.77 18807.73
+1 190 25 1.78 5.03 24581.44
+1 137 26 1.44 4.09 19037.59
+1 63 42 1.44 4.09 22390.80
+1 107 44 1.33 3.77 23429.96
+1 61 27 1.56 4.40 18052.18
+1 158 39 1.44 4.09 18377.02
+1 190 14 1.33 3.77 18548.86
+1 182 33 1.78 5.03 26467.79
+1 39 39 1.44 4.09 14782.43
+1 169 26 1.33 3.77 14203.41
+1 61 54 1.33 3.77 23248.06
+1 95 52 1.33 3.77 21480.71
+1 23 60 1.89 5.34 25203.43
+1 84 24 1.56 4.40 16630.57
+1 121 47 1.67 4.71 15459.11
+1 66 49 1.11 3.14 23858.07
+1 115 36 2.00 5.66 16389.10
+1 55 51 1.33 3.77 23548.90
+1 130 72 1.67 4.71 15769.02
+1 117 23 1.33 3.77 16763.14
+1 45 52 1.56 4.40 22877.61
+1 36 71 1.56 4.40 20780.96
+1 78 17 1.33 3.77 16844.51
+1 101 38 1.56 4.40 21376.59
+1 147 31 1.78 5.03 16597.14
+1 163 55 2.00 5.66 18301.54
+1 164 23 1.33 3.77 17073.82
+1 150 24 1.56 4.40 15440.02
+1 151 67 1.78 5.03 18419.96
+1 26 53 2.00 5.66 20586.01
+1 79 62 1.33 3.77 15232.88
+1 69 17 1.11 3.14 15601.83
+1 83 52 1.33 3.77 18315.00
+1 16 54 2.00 5.66 22140.66
+1 166 61 1.78 5.03 18488.78
+1 163 43 1.44 4.09 16925.49
+1 130 53 1.78 5.03 15101.96
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/input3.tsv
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test-data/input3.tsv Fri Sep 27 17:41:21 2024 +0000
@@ -0,0 +1,4 @@
+pos_x pos_y radius label
+20 20 20 1
+50 50 40 1
+150 50 40 2
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/out.tif
Binary file test-data/out.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/output1_binary.tif
Binary file test-data/output1_binary.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/output2.tif
Binary file test-data/output2.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/output2_binary.tif
Binary file test-data/output2_binary.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/output3.tif
Binary file test-data/output3.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/output3_binary.tif
Binary file test-data/output3_binary.tif has changed
diff -r 30ca5d5d03ec -r de611b3b5ae8 test-data/points.tsv
--- a/test-data/points.tsv Mon Nov 13 22:11:46 2023 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-5 3
-10 2
-8 4
-15 15
-8 5
-8 6
-8 7
diff -r 30ca5d5d03ec -r de611b3b5ae8 tests.xml
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests.xml Fri Sep 27 17:41:21 2024 +0000
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+