Mercurial > repos > perssond > basic_illumination
diff imagej_basic_ashlar_filepattern.py @ 0:cad3339b566b draft default tip
"planemo upload for repository https://github.com/ohsu-comp-bio/basic-illumination commit d06e0682d1847fae0d5a464d7aa9e47e40d31fe7-dirty"
author | perssond |
---|---|
date | Tue, 08 Dec 2020 20:57:09 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/imagej_basic_ashlar_filepattern.py Tue Dec 08 20:57:09 2020 +0000 @@ -0,0 +1,135 @@ +# @String(label="Enter a filename pattern describing the TIFFs to process") pattern +# @File(label="Select the output location", style="directory") output_dir +# @String(label="Experiment name (base name for output files)") experiment_name +# @Float(label="Flat field smoothing parameter (0 for automatic)", value=0.1) lambda_flat +# @Float(label="Dark field smoothing parameter (0 for automatic)", value=0.01) lambda_dark + +import sys +import os +import re +import collections +from ij import IJ, WindowManager, ImagePlus, ImageStack +from ij.io import Opener +from ij.macro import Interpreter +import BaSiC_ as Basic + + +def enumerate_filenames(pattern): + """Return filenames matching pattern (a str.format pattern containing + {channel} and {tile} placeholders). + + Returns a list of lists, where the top level is indexed by channel number + and the bottom level is sorted filenames for that channel. + + """ + (base, pattern) = os.path.split(pattern) + regex = re.sub(r'{([^:}]+)(?:[^}]*)}', r'(?P<\1>.*?)', + pattern.replace('.', '\.')) + tiles = set() + channels = set() + num_images = 0 + # Dict[channel: int, List[filename: str]] + filenames = collections.defaultdict(list) + for f in os.listdir(base): + match = re.match(regex, f) + if match: + gd = match.groupdict() + tile = int(gd['tile']) + channel = int(gd['channel']) + tiles.add(tile) + channels.add(channel) + filenames[channel].append(os.path.join(base, f)) + num_images += 1 + if len(tiles) * len(channels) != num_images: + raise Exception("Missing some image files") + filenames = [ + sorted(filenames[channel]) + for channel in sorted(filenames.keys()) + ] + return filenames + + +def main(): + + Interpreter.batchMode = True + + if (lambda_flat == 0) ^ (lambda_dark == 0): + print ("ERROR: Both of lambda_flat and lambda_dark must be zero," + " or both non-zero.") + return + lambda_estimate = "Automatic" if lambda_flat == 0 else "Manual" + + #import pdb; pdb.set_trace() + print "Loading images..." + filenames = enumerate_filenames(pattern) + num_channels = len(filenames) + num_images = len(filenames[0]) + image = Opener().openImage(filenames[0][0]) + width = image.width + height = image.height + image.close() + + # The internal initialization of the BaSiC code fails when we invoke it via + # scripting, unless we explicitly set a the private 'noOfSlices' field. + # Since it's private, we need to use Java reflection to access it. + Basic_noOfSlices = Basic.getDeclaredField('noOfSlices') + Basic_noOfSlices.setAccessible(True) + basic = Basic() + Basic_noOfSlices.setInt(basic, num_images) + + # Pre-allocate the output profile images, since we have all the dimensions. + ff_image = IJ.createImage("Flat-field", width, height, num_channels, 32); + df_image = IJ.createImage("Dark-field", width, height, num_channels, 32); + + print("\n\n") + + # BaSiC works on one channel at a time, so we only read the images from one + # channel at a time to limit memory usage. + for channel in range(num_channels): + print "Processing channel %d/%d..." % (channel + 1, num_channels) + print "===========================" + + stack = ImageStack(width, height, num_images) + opener = Opener() + for i, filename in enumerate(filenames[channel]): + print "Loading image %d/%d" % (i + 1, num_images) + image = opener.openImage(filename) + stack.setProcessor(image.getProcessor(), i + 1) + input_image = ImagePlus("input", stack) + + # BaSiC seems to require the input image is actually the ImageJ + # "current" image, otherwise it prints an error and aborts. + WindowManager.setTempCurrentImage(input_image) + basic.exec( + input_image, None, None, + "Estimate shading profiles", "Estimate both flat-field and dark-field", + lambda_estimate, lambda_flat, lambda_dark, + "Ignore", "Compute shading only" + ) + input_image.close() + + # Copy the pixels from the BaSiC-generated profile images to the + # corresponding channel of our output images. + ff_channel = WindowManager.getImage("Flat-field:%s" % input_image.title) + ff_image.slice = channel + 1 + ff_image.getProcessor().insert(ff_channel.getProcessor(), 0, 0) + ff_channel.close() + df_channel = WindowManager.getImage("Dark-field:%s" % input_image.title) + df_image.slice = channel + 1 + df_image.getProcessor().insert(df_channel.getProcessor(), 0, 0) + df_channel.close() + + print("\n\n") + + template = '%s/%s-%%s.tif' % (output_dir, experiment_name) + ff_filename = template % 'ffp' + IJ.saveAsTiff(ff_image, ff_filename) + ff_image.close() + df_filename = template % 'dfp' + IJ.saveAsTiff(df_image, df_filename) + df_image.close() + + print "Done!" + + +main()