|
1 | 1 | # @ImageJ ij
|
2 |
| -# @String(value="<HTML>This script applies the Frangi Vesselness filter to all TIFF images in a directory.<br>Only 2D or 3D grayscale images are supported. Processing log is shown in Console.", visibility="MESSAGE") msg |
| 2 | +# @String(value="<HTML>This script applies the Tubeness filter to all TIFF images in a directory.<br>Only 2D or 3D grayscale images are supported. Processing log is shown in Console.", visibility="MESSAGE") msg |
3 | 3 | # @File(label="Directory containing your images", style="directory") input_dir
|
4 | 4 | # @String(label="Consider only filenames containing",description="Clear field for no filtering",value="") name_filter
|
5 | 5 | # @boolean(label="Include subdirectories") recursive
|
6 |
| -# @float(label="Size of structures to be filtered (spatially calibrated units)",description="The filter kernel will be determined from the average voxel size") scale |
| 6 | +# @String(label="Size of structures to be filtered (multiple of avg. voxel spacing)",description="The filter kernel will be determined from the average voxel spacing") scales |
7 | 7 | # @LogService log
|
8 | 8 | # @StatusService status
|
9 | 9 | # @UIService uiservice
|
| 10 | +# @DatasetIOService dio |
| 11 | +# @IOService io |
| 12 | +# @Context context |
10 | 13 |
|
11 | 14 | """
|
12 |
| -file: Filter_Multiple_Images.py |
13 |
| -author: Tiago Ferreira, Cameron Arshadi |
14 |
| -version: 20190525 |
15 |
| -info: Bulk filtering of image files using Frangi Vesselness |
| 15 | +file: Filter_Multiple_Images.py |
| 16 | +author: Tiago Ferreira, Cameron Arshadi |
| 17 | +version: 20190525 |
| 18 | +info: Bulk filtering of image files using Frangi Vesselness |
16 | 19 | """
|
17 | 20 |
|
18 |
| -from net.imagej.axis import Axes |
19 |
| - |
20 | 21 | import os
|
| 22 | +from java.lang import Runtime |
| 23 | +from net.imglib2.type.numeric.real import FloatType |
| 24 | +from net.imglib2.img.display.imagej import ImageJFunctions |
| 25 | +from net.imagej.axis import Axes |
| 26 | +from sc.fiji.snt.filter import Tubeness |
| 27 | +from io.scif.img import ImgSaver |
21 | 28 |
|
22 | 29 |
|
23 | 30 | def get_image_files(directory, filtering_string, extension):
|
24 |
| - """Returns a list containing the paths of files in the specified |
25 |
| - directory. The list will only include files with the supplied |
26 |
| - extension whose filename contains the specified string.""" |
27 |
| - files = [] |
28 |
| - for (dirpath, dirnames, filenames) in os.walk(directory): |
29 |
| - for f in filenames: |
30 |
| - if os.path.basename(f).startswith('.'): |
31 |
| - continue |
32 |
| - if filtering_string in f and f.lower().endswith(extension): |
33 |
| - files.append(os.path.join(dirpath, f)) |
34 |
| - if not recursive: |
35 |
| - break # do not process subdirectories |
36 |
| - |
37 |
| - return files |
| 31 | + """Returns a list containing the paths of files in the specified |
| 32 | + directory. The list will only include files with the supplied |
| 33 | + extension whose filename contains the specified string.""" |
| 34 | + files = [] |
| 35 | + for (dirpath, dirnames, filenames) in os.walk(directory): |
| 36 | + for f in filenames: |
| 37 | + if os.path.basename(f).startswith('.'): |
| 38 | + continue |
| 39 | + if filtering_string in f and f.lower().endswith(extension): |
| 40 | + files.append(os.path.join(dirpath, f)) |
| 41 | + if not recursive: |
| 42 | + break # do not process subdirectories |
| 43 | + |
| 44 | + return files |
38 | 45 |
|
39 | 46 |
|
40 | 47 | def run():
|
41 |
| - # First check that scale parameter is > 0, exiting if not |
42 |
| - if scale <= 0: |
43 |
| - log.error('Please select a value > 0 for the scale parameter. Exiting...') |
44 |
| - return |
45 |
| - |
46 |
| - # Get all files with specified name filter and extension in the input directory |
47 |
| - d = str(input_dir) |
48 |
| - extension = ".tif" |
49 |
| - files = get_image_files(d, name_filter, extension) |
50 |
| - if not files or len(files) == 0: |
51 |
| - uiservice.showDialog("No files matched the specified criteria", "Error") |
52 |
| - return |
53 |
| - |
54 |
| - processed = 0 |
55 |
| - skipped = 0 |
56 |
| - for f in files: |
57 |
| - |
58 |
| - basename = os.path.basename(f) |
59 |
| - msg = 'Processing file %s: %s...' % (processed + skipped + 1, basename) |
60 |
| - status.showStatus(msg) |
61 |
| - print(msg) |
62 |
| - |
63 |
| - # Load the input image |
64 |
| - input_image = ij.io().open(f) |
65 |
| - |
66 |
| - # Verify that the image is 2D/3D and grayscale, skipping it if not |
67 |
| - num_dimensions = input_image.numDimensions() |
68 |
| - if num_dimensions > 3 or int(input_image.getChannels()) > 1: |
69 |
| - log.error('Could not process %s...Only 2D/3D grayscale images are supported' % basename) |
70 |
| - skipped += 1 |
71 |
| - continue |
72 |
| - |
73 |
| - # Convert input image to float, since we are dealing with derivatives |
74 |
| - float_input = ij.op().run("convert.float32", input_image) |
75 |
| - |
76 |
| - # Obtain spatial calibration of the image |
77 |
| - x_spacing = float_input.averageScale(0) |
78 |
| - y_spacing = float_input.averageScale(1) |
79 |
| - spacing = [x_spacing, y_spacing] |
80 |
| - |
81 |
| - if num_dimensions == 3 and float_input.axis(2).type() == Axes.Z: |
82 |
| - z_spacing = float_input.averageScale(2) |
83 |
| - spacing.append(z_spacing) |
84 |
| - |
85 |
| - # Create placeholder image for the output then run the Frangi Vesselness op |
86 |
| - output = ij.op().run("create.img", float_input) |
87 |
| - pixel_scale = scale / (sum(spacing) / len(spacing)) # average voxel size: convert scale to pixel units |
88 |
| - ij.op().run("frangiVesselness", output, float_input, spacing, pixel_scale) |
89 |
| - |
90 |
| - # Save the result using the same basename as the image, adding "[Frangi].tif" |
91 |
| - # For example, the output for "OP_1.tif" would be named "OP_1[Frangi].tif" |
92 |
| - l = len(f) |
93 |
| - el = len(extension) |
94 |
| - output_filepath = f[0:l - el] + "[Frangi].tif" |
95 |
| - ij.io().save(output, output_filepath) |
96 |
| - |
97 |
| - processed += 1 |
98 |
| - |
99 |
| - print('Done. %s file(s) processed. %s file(s) skipped...' % (processed, skipped)) |
| 48 | + # First check that scale parameters are > 0, exiting if not |
| 49 | + global scales |
| 50 | + scales = [float(s) for s in scales.split(',')] |
| 51 | + if not scales or any(s <= 0 for s in scales): |
| 52 | + log.error('Please select values > 0 for the scale parameter. Exiting...') |
| 53 | + return |
| 54 | + |
| 55 | + # Get all files with specified name filter and extension in the input directory |
| 56 | + d = str(input_dir) |
| 57 | + extension = ".tif" |
| 58 | + files = get_image_files(d, name_filter, extension) |
| 59 | + if not files or len(files) == 0: |
| 60 | + uiservice.showDialog("No files matched the specified criteria", "Error") |
| 61 | + return |
| 62 | + |
| 63 | + processed = 0 |
| 64 | + skipped = 0 |
| 65 | + for f in files: |
| 66 | + |
| 67 | + basename = os.path.basename(f) |
| 68 | + msg = 'Processing file %s: %s...' % (processed + skipped + 1, basename) |
| 69 | + status.showStatus(msg) |
| 70 | + |
| 71 | + # Load the input image |
| 72 | + input_image = dio.open(f) |
| 73 | + |
| 74 | + # Verify that the image is 2D/3D and grayscale, skipping it if not |
| 75 | + num_dimensions = input_image.numDimensions() |
| 76 | + if num_dimensions > 3 or input_image.getChannels() > 1: |
| 77 | + log.error('Could not process %s...Only 2D/3D grayscale images are supported' % basename) |
| 78 | + skipped += 1 |
| 79 | + continue |
| 80 | + |
| 81 | + # Obtain spatial calibration of the image |
| 82 | + x_spacing = input_image.averageScale(0) |
| 83 | + y_spacing = input_image.averageScale(1) |
| 84 | + spacing = [x_spacing, y_spacing] |
| 85 | + |
| 86 | + if num_dimensions == 3 and input_image.axis(2).type() == Axes.Z: |
| 87 | + z_spacing = input_image.averageScale(2) |
| 88 | + spacing.append(z_spacing) |
| 89 | + |
| 90 | + avgsep = sum(spacing) / len(spacing) |
| 91 | + scales = [s * avgsep for s in scales] |
| 92 | + # Create placeholder image for the output then run the Frangi Vesselness op |
| 93 | + output = ij.op().create().img(input_image, FloatType()) |
| 94 | + # Pass scales in physical units (e.g., um) |
| 95 | + op = Tubeness(scales, spacing, Runtime.getRuntime().availableProcessors()) |
| 96 | + op.compute(input_image, output) |
| 97 | + |
| 98 | + # Save the result using the same basename as the image, adding "[Frangi].tif" |
| 99 | + # For example, the output for "OP_1.tif" would be named "OP_1[Frangi].tif" |
| 100 | + l = len(f) |
| 101 | + el = len(extension) |
| 102 | + output_filepath = f[0:l - el] + "[Tubeness].tif" |
| 103 | + saver = ImgSaver(context) |
| 104 | + saver.saveImg(output_filepath, output) |
| 105 | + |
| 106 | + processed += 1 |
| 107 | + |
| 108 | + print('Done. %s file(s) processed. %s file(s) skipped...' % (processed, skipped)) |
100 | 109 |
|
101 | 110 |
|
102 | 111 | run()
|
0 commit comments