changeset 5:2436a8807ad1 draft

planemo upload for repository https://github.com/BMCV/galaxy-image-analysis/tree/master/tools/2d_feature_extraction/ commit c045f067a57e8308308cf6329060c7ccd3fc372f
author imgteam
date Thu, 04 Apr 2024 15:23:23 +0000
parents 0a53256b48c6
children 5bc8cdc17fd0
files 2d_feature_extraction.py 2d_feature_extraction.xml creators.xml test-data/input.tiff test-data/out.tsv
diffstat 5 files changed, 161 insertions(+), 133 deletions(-) [+]
line wrap: on
line diff
--- a/2d_feature_extraction.py	Fri Nov 10 14:23:12 2023 +0000
+++ b/2d_feature_extraction.py	Thu Apr 04 15:23:23 2024 +0000
@@ -8,119 +8,120 @@
 import skimage.morphology
 import skimage.segmentation
 
-# TODO make importable by python script
 
-parser = argparse.ArgumentParser(description='Extract Features 2D')
+if __name__ == '__main__':
+
+    parser = argparse.ArgumentParser(description='Extract image features')
 
-# TODO create factory for boilerplate code
-features = parser.add_argument_group('compute features')
-features.add_argument('--all', dest='all_features', action='store_true')
-features.add_argument('--label', dest='add_label', action='store_true')
-features.add_argument('--patches', dest='add_roi_patches', action='store_true')
-features.add_argument('--max_intensity', dest='max_intensity', action='store_true')
-features.add_argument('--mean_intensity', dest='mean_intensity', action='store_true')
-features.add_argument('--min_intensity', dest='min_intensity', action='store_true')
-features.add_argument('--moments_hu', dest='moments_hu', action='store_true')
-features.add_argument('--centroid', dest='centroid', action='store_true')
-features.add_argument('--bbox', dest='bbox', action='store_true')
-features.add_argument('--area', dest='area', action='store_true')
-features.add_argument('--filled_area', dest='filled_area', action='store_true')
-features.add_argument('--convex_area', dest='convex_area', action='store_true')
-features.add_argument('--perimeter', dest='perimeter', action='store_true')
-features.add_argument('--extent', dest='extent', action='store_true')
-features.add_argument('--eccentricity', dest='eccentricity', action='store_true')
-features.add_argument('--equivalent_diameter', dest='equivalent_diameter', action='store_true')
-features.add_argument('--euler_number', dest='euler_number', action='store_true')
-features.add_argument('--inertia_tensor_eigvals', dest='inertia_tensor_eigvals', action='store_true')
-features.add_argument('--major_axis_length', dest='major_axis_length', action='store_true')
-features.add_argument('--minor_axis_length', dest='minor_axis_length', action='store_true')
-features.add_argument('--orientation', dest='orientation', action='store_true')
-features.add_argument('--solidity', dest='solidity', action='store_true')
-features.add_argument('--moments', dest='moments', action='store_true')
-features.add_argument('--convexity', dest='convexity', action='store_true')
+    # TODO create factory for boilerplate code
+    features = parser.add_argument_group('compute features')
+    features.add_argument('--all', dest='all_features', action='store_true')
+    features.add_argument('--label', dest='add_label', action='store_true')
+    features.add_argument('--patches', dest='add_roi_patches', action='store_true')
+    features.add_argument('--max_intensity', dest='max_intensity', action='store_true')
+    features.add_argument('--mean_intensity', dest='mean_intensity', action='store_true')
+    features.add_argument('--min_intensity', dest='min_intensity', action='store_true')
+    features.add_argument('--moments_hu', dest='moments_hu', action='store_true')
+    features.add_argument('--centroid', dest='centroid', action='store_true')
+    features.add_argument('--bbox', dest='bbox', action='store_true')
+    features.add_argument('--area', dest='area', action='store_true')
+    features.add_argument('--filled_area', dest='filled_area', action='store_true')
+    features.add_argument('--convex_area', dest='convex_area', action='store_true')
+    features.add_argument('--perimeter', dest='perimeter', action='store_true')
+    features.add_argument('--extent', dest='extent', action='store_true')
+    features.add_argument('--eccentricity', dest='eccentricity', action='store_true')
+    features.add_argument('--equivalent_diameter', dest='equivalent_diameter', action='store_true')
+    features.add_argument('--euler_number', dest='euler_number', action='store_true')
+    features.add_argument('--inertia_tensor_eigvals', dest='inertia_tensor_eigvals', action='store_true')
+    features.add_argument('--major_axis_length', dest='major_axis_length', action='store_true')
+    features.add_argument('--minor_axis_length', dest='minor_axis_length', action='store_true')
+    features.add_argument('--orientation', dest='orientation', action='store_true')
+    features.add_argument('--solidity', dest='solidity', action='store_true')
+    features.add_argument('--moments', dest='moments', action='store_true')
+    features.add_argument('--convexity', dest='convexity', action='store_true')
 
-parser.add_argument('--label_file_binary', dest='label_file_binary', action='store_true')
+    parser.add_argument('--label_file_binary', dest='label_file_binary', action='store_true')
 
-parser.add_argument('--raw', dest='raw_file', type=argparse.FileType('r'),
-                    help='Original input file', required=False)
-parser.add_argument('label_file', type=argparse.FileType('r'),
-                    help='Label input file')
-parser.add_argument('output_file', type=argparse.FileType('w'),
-                    help='Tabular output file')
-args = parser.parse_args()
+    parser.add_argument('--raw', dest='raw_file', type=argparse.FileType('r'),
+                        help='Original input file', required=False)
+    parser.add_argument('label_file', type=argparse.FileType('r'),
+                        help='Label input file')
+    parser.add_argument('output_file', type=argparse.FileType('w'),
+                        help='Tabular output file')
+    args = parser.parse_args()
 
-label_file_binary = args.label_file_binary
-label_file = args.label_file.name
-out_file = args.output_file.name
-add_patch = args.add_roi_patches
+    label_file_binary = args.label_file_binary
+    label_file = args.label_file.name
+    out_file = args.output_file.name
+    add_patch = args.add_roi_patches
 
-raw_image = None
-if args.raw_file is not None:
-    raw_image = skimage.io.imread(args.raw_file.name)
+    raw_image = None
+    if args.raw_file is not None:
+        raw_image = skimage.io.imread(args.raw_file.name)
 
-raw_label_image = skimage.io.imread(label_file)
+    raw_label_image = skimage.io.imread(label_file)
 
-df = pd.DataFrame()
-if label_file_binary:
-    raw_label_image = skimage.measure.label(raw_label_image)
-regions = skimage.measure.regionprops(raw_label_image, intensity_image=raw_image)
+    df = pd.DataFrame()
+    if label_file_binary:
+        raw_label_image = skimage.measure.label(raw_label_image)
+    regions = skimage.measure.regionprops(raw_label_image, intensity_image=raw_image)
 
-df['it'] = np.arange(len(regions))
+    df['it'] = np.arange(len(regions))
 
-if add_patch:
-    df['image'] = df['it'].map(lambda ait: regions[ait].image.astype(np.float).tolist())
-    df['intensity_image'] = df['it'].map(lambda ait: regions[ait].intensity_image.astype(np.float).tolist())
+    if add_patch:
+        df['image'] = df['it'].map(lambda ait: regions[ait].image.astype(np.float).tolist())
+        df['intensity_image'] = df['it'].map(lambda ait: regions[ait].intensity_image.astype(np.float).tolist())
 
-# TODO no matrix features, but split in own rows?
-if args.add_label or args.all_features:
-    df['label'] = df['it'].map(lambda ait: regions[ait].label)
+    # TODO no matrix features, but split in own rows?
+    if args.add_label or args.all_features:
+        df['label'] = df['it'].map(lambda ait: regions[ait].label)
 
-if raw_image is not None:
-    if args.max_intensity or args.all_features:
-        df['max_intensity'] = df['it'].map(lambda ait: regions[ait].max_intensity)
-    if args.mean_intensity or args.all_features:
-        df['mean_intensity'] = df['it'].map(lambda ait: regions[ait].mean_intensity)
-    if args.min_intensity or args.all_features:
-        df['min_intensity'] = df['it'].map(lambda ait: regions[ait].min_intensity)
-    if args.moments_hu or args.all_features:
-        df['moments_hu'] = df['it'].map(lambda ait: regions[ait].moments_hu)
+    if raw_image is not None:
+        if args.max_intensity or args.all_features:
+            df['max_intensity'] = df['it'].map(lambda ait: regions[ait].max_intensity)
+        if args.mean_intensity or args.all_features:
+            df['mean_intensity'] = df['it'].map(lambda ait: regions[ait].mean_intensity)
+        if args.min_intensity or args.all_features:
+            df['min_intensity'] = df['it'].map(lambda ait: regions[ait].min_intensity)
+        if args.moments_hu or args.all_features:
+            df['moments_hu'] = df['it'].map(lambda ait: regions[ait].moments_hu)
 
-if args.centroid or args.all_features:
-    df['centroid'] = df['it'].map(lambda ait: regions[ait].centroid)
-if args.bbox or args.all_features:
-    df['bbox'] = df['it'].map(lambda ait: regions[ait].bbox)
-if args.area or args.all_features:
-    df['area'] = df['it'].map(lambda ait: regions[ait].area)
-if args.filled_area or args.all_features:
-    df['filled_area'] = df['it'].map(lambda ait: regions[ait].filled_area)
-if args.convex_area or args.all_features:
-    df['convex_area'] = df['it'].map(lambda ait: regions[ait].convex_area)
-if args.perimeter or args.all_features:
-    df['perimeter'] = df['it'].map(lambda ait: regions[ait].perimeter)
-if args.extent or args.all_features:
-    df['extent'] = df['it'].map(lambda ait: regions[ait].extent)
-if args.eccentricity or args.all_features:
-    df['eccentricity'] = df['it'].map(lambda ait: regions[ait].eccentricity)
-if args.equivalent_diameter or args.all_features:
-    df['equivalent_diameter'] = df['it'].map(lambda ait: regions[ait].equivalent_diameter)
-if args.euler_number or args.all_features:
-    df['euler_number'] = df['it'].map(lambda ait: regions[ait].euler_number)
-if args.inertia_tensor_eigvals or args.all_features:
-    df['inertia_tensor_eigvals'] = df['it'].map(lambda ait: regions[ait].inertia_tensor_eigvals)
-if args.major_axis_length or args.all_features:
-    df['major_axis_length'] = df['it'].map(lambda ait: regions[ait].major_axis_length)
-if args.minor_axis_length or args.all_features:
-    df['minor_axis_length'] = df['it'].map(lambda ait: regions[ait].minor_axis_length)
-if args.orientation or args.all_features:
-    df['orientation'] = df['it'].map(lambda ait: regions[ait].orientation)
-if args.solidity or args.all_features:
-    df['solidity'] = df['it'].map(lambda ait: regions[ait].solidity)
-if args.moments or args.all_features:
-    df['moments'] = df['it'].map(lambda ait: regions[ait].moments)
-if args.convexity or args.all_features:
-    perimeter = df['it'].map(lambda ait: regions[ait].perimeter)
-    area = df['it'].map(lambda ait: regions[ait].area)
-    df['convexity'] = area / (perimeter * perimeter)
+    if args.centroid or args.all_features:
+        df['centroid'] = df['it'].map(lambda ait: regions[ait].centroid)
+    if args.bbox or args.all_features:
+        df['bbox'] = df['it'].map(lambda ait: regions[ait].bbox)
+    if args.area or args.all_features:
+        df['area'] = df['it'].map(lambda ait: regions[ait].area)
+    if args.filled_area or args.all_features:
+        df['filled_area'] = df['it'].map(lambda ait: regions[ait].filled_area)
+    if args.convex_area or args.all_features:
+        df['convex_area'] = df['it'].map(lambda ait: regions[ait].convex_area)
+    if args.perimeter or args.all_features:
+        df['perimeter'] = df['it'].map(lambda ait: regions[ait].perimeter)
+    if args.extent or args.all_features:
+        df['extent'] = df['it'].map(lambda ait: regions[ait].extent)
+    if args.eccentricity or args.all_features:
+        df['eccentricity'] = df['it'].map(lambda ait: regions[ait].eccentricity)
+    if args.equivalent_diameter or args.all_features:
+        df['equivalent_diameter'] = df['it'].map(lambda ait: regions[ait].equivalent_diameter)
+    if args.euler_number or args.all_features:
+        df['euler_number'] = df['it'].map(lambda ait: regions[ait].euler_number)
+    if args.inertia_tensor_eigvals or args.all_features:
+        df['inertia_tensor_eigvals'] = df['it'].map(lambda ait: regions[ait].inertia_tensor_eigvals)
+    if args.major_axis_length or args.all_features:
+        df['major_axis_length'] = df['it'].map(lambda ait: regions[ait].major_axis_length)
+    if args.minor_axis_length or args.all_features:
+        df['minor_axis_length'] = df['it'].map(lambda ait: regions[ait].minor_axis_length)
+    if args.orientation or args.all_features:
+        df['orientation'] = df['it'].map(lambda ait: regions[ait].orientation)
+    if args.solidity or args.all_features:
+        df['solidity'] = df['it'].map(lambda ait: regions[ait].solidity)
+    if args.moments or args.all_features:
+        df['moments'] = df['it'].map(lambda ait: regions[ait].moments)
+    if args.convexity or args.all_features:
+        perimeter = df['it'].map(lambda ait: regions[ait].perimeter)
+        area = df['it'].map(lambda ait: regions[ait].area)
+        df['convexity'] = area / (perimeter * perimeter)
 
-del df['it']
-df.to_csv(out_file, sep='\t', line_terminator='\n', index=False)
+    del df['it']
+    df.to_csv(out_file, sep='\t', line_terminator='\n', index=False)
--- a/2d_feature_extraction.xml	Fri Nov 10 14:23:12 2023 +0000
+++ b/2d_feature_extraction.xml	Thu Apr 04 15:23:23 2024 +0000
@@ -1,5 +1,13 @@
-<tool id="ip_2d_feature_extraction" name="Extract 2D features" version="0.1.1-2">
+<tool id="ip_2d_feature_extraction" name="Extract image features" version="@TOOL_VERSION@+galaxy@VERSION_SUFFIX@" profile="20.05">
     <description>with scikit-image</description>
+    <macros>
+        <import>creators.xml</import>
+        <token name="@TOOL_VERSION@">0.14.2</token>
+        <token name="@VERSION_SUFFIX@">0</token>
+    </macros>
+    <creator>
+        <expand macro="creators/bmcv" />
+    </creator>
     <edam_operations>
         <edam_operation>operation_3443</edam_operation>
     </edam_operations>
@@ -8,8 +16,8 @@
         <xref type="biii">scikit-image</xref>
     </xrefs>
     <requirements> 
+        <requirement type="package" version="@TOOL_VERSION@">scikit-image</requirement>
         <requirement type="package" version="0.23.4">pandas</requirement>
-        <requirement type="package" version="0.14.2">scikit-image</requirement>
         <requirement type="package" version="1.15.4">numpy</requirement>
         <requirement type="package" version="0.15.1">tifffile</requirement>
     </requirements>
@@ -28,15 +36,15 @@
     ]]>
     </command>
     <inputs>
-        <param name="input_label" type="data" format="tiff" label="Label file" />
+        <param name="input_label" type="data" format="tiff,png" label="Label map" />
         <conditional name="use_raw_option">
-            <param label="Use original image to compute additional features" name="use_raw" type="select">
-                <option selected="True" value="no_original">No original image</option>
-                <option value="raw_image">Use original image</option>
+            <param label="Use the intensity image to compute additional features" name="use_raw" type="select">
+                <option selected="True" value="no_original">No intensity image</option>
+                <option value="raw_image">Use intensity image</option>
             </param>
             <when value="no_original"></when>
             <when value="raw_image">
-                <param name="input_raw" type="data" format="tiff" label="Original image file" />
+                <param name="input_raw" type="data" format="tiff,png" label="Intensity image" />
             </when>
         </conditional>
         <conditional name="feature_options">
@@ -47,25 +55,25 @@
             <when value="all"> </when>
             <when value="select">
                 <param name="selected_features" type="select" label="Available features" multiple="true" display="checkboxes">
-                    <option value="--label">Add label id of label image</option>
+                    <option value="--label">Label from the label map</option>
                     <option value="--patches">Patches (requires original image)</option>
                     <option value="--max_intensity">Max Intensity (requires original image)</option>
                     <option value="--mean_intensity">Mean Intensity (requires original image)</option>
                     <option value="--min_intensity">Minimum Intensity (requires original image)</option>
                     <option value="--moments_hu">Moments Hu</option>
                     <option value="--centroid">Centroid</option>
-                    <option value="--bbox">Bounding Box</option>
+                    <option value="--bbox">Bounding box</option>
                     <option value="--area">Area</option>
-                    <option value="--filled_area">Filled Area</option>
-                    <option value="--convex_area">Convex Area</option>
+                    <option value="--filled_area">Filled area</option>
+                    <option value="--convex_area">Convex area</option>
                     <option value="--perimeter">Perimeter</option>
                     <option value="--extent">Extent</option>
                     <option value="--eccentricity">Eccentricity</option>
-                    <option value="--equivalent_diameter">Equivalent Diameter</option>
-                    <option value="--euler_number">Euler Number</option>
-                    <option value="--inertia_tensor_eigvals">Inertia Tensor Eigenvalues</option>
-                    <option value="--major_axis_length">Major Axis Length</option>
-                    <option value="--minor_axis_length">Minor Axis Length</option>
+                    <option value="--equivalent_diameter">Equivalent diameter</option>
+                    <option value="--euler_number">Euler number</option>
+                    <option value="--inertia_tensor_eigvals">Inertia tensor eigenvalues</option>
+                    <option value="--major_axis_length">Major axis length</option>
+                    <option value="--minor_axis_length">Minor axis length</option>
                     <option value="--orientation">Orientation</option>
                     <option value="--solidity">Solidity</option>
                     <option value="--moments">Moments</option>
@@ -81,14 +89,19 @@
         <test>
             <param name="input_label" value="input.tiff"/>
             <param name="features" value="select"/>
-            <param name="selected_features" value="--area"/>
+            <param name="selected_features" value="--label,--area"/>
             <output name="output" ftype="tabular" value="out.tsv"/>
         </test>
     </tests>
     <help>
-    **What it does**
+
+        **Computes features of a label map.**
 
-    This tool computes several features of a 2D label image and optionally more features using the original image.
+        The computed features are computed based solely on the properties of the labels in the label map,
+        or, optionally, by also taking the intensities from a corresponding intensity image into account.
+
+        The label map must be a 2-D or 3-D single-channel image.
+    
     </help>
     <citations>
         <citation type="doi">10.1016/j.jbiotec.2017.07.019</citation>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/creators.xml	Thu Apr 04 15:23:23 2024 +0000
@@ -0,0 +1,23 @@
+<macros>
+
+    <xml name="creators/bmcv">
+        <organization name="Biomedical Computer Vision Group, Heidelberg Universtiy" alternateName="BMCV" url="http://www.bioquant.uni-heidelberg.de/research/groups/biomedical_computer_vision.html" />
+        <yield />
+    </xml>
+
+    <xml name="creators/alliecreason">
+        <person givenName="Allison" familyName="Creason"/>
+        <yield/>
+    </xml>
+
+    <xml name="creators/bugraoezdemir">
+        <person givenName="Bugra" familyName="Oezdemir"/>
+        <yield/>
+    </xml>
+
+    <xml name="creators/thawn">
+        <person givenName="Till" familyName="Korten"/>
+        <yield/>
+    </xml>
+    
+</macros>
Binary file test-data/input.tiff has changed
--- a/test-data/out.tsv	Fri Nov 10 14:23:12 2023 +0000
+++ b/test-data/out.tsv	Thu Apr 04 15:23:23 2024 +0000
@@ -1,12 +1,3 @@
-area
-612
-612
-375
-375
-729
-729
-399
-399
-3
-3
-434042
+label	area
+1	8
+2	9