Last active
April 9, 2025 12:32
-
-
Save lacan/e0a33a6cf713947994a7bcde51dd8327 to your computer and use it in GitHub Desktop.
[Stitching with Flatfield Correction] Takes a folder as input and processed all multiseries files there using a provided flatfield file #Beanshell #Stitching #Fiji #ImageJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//@File(label="Multiposition Files Directory",style="directory") image_directory | |
//@String(value="Select a file for flat field correction or write none", visibility="MESSAGE") textFF | |
//@File(label="Flatfield Correction File", value="none") ff_file | |
//@int(label="Downsample Factor", style = "slider", min = "1", max = "16", stepSize = "1") downsample | |
//@Boolean(label="Compute Overlap") is_compute | |
//@String(label="Macro Mode",choices={"Fuse and display","Write to disk"}) output_type | |
//@LogService log | |
import ij.IJ; | |
import loci.formats.ImageReader; | |
import loci.formats.meta.IMetadata; | |
import loci.formats.meta.MetadataRetrieve; | |
import loci.formats.MetadataTools; | |
import loci.common.services.ServiceFactory; | |
import loci.formats.services.OMEXMLService; | |
import java.util.Arrays; | |
import java.util.Collections; | |
import org.apache.commons.lang.ArrayUtils; | |
import ij.plugin.ChannelSplitter; | |
import ij.plugin.RGBStackMerge; | |
import ij.ImagePlus; | |
import ij.plugin.ImageCalculator; | |
import loci.plugins.in.ImagePlusReader; | |
import loci.plugins.in.ImporterOptions; | |
import loci.plugins.in.ImportProcess; | |
import java.io.FilenameFilter; | |
/* | |
* This simple script will open a multiposition series and perform a flatfield correction of each image and each channel before | |
* returning a text file with the coordinates of each file to use qith Stephan Preibish's stitching plugins | |
* | |
* - works for lsm and lif files format | |
* - correction factor is applied to metadata of lif iles | |
* - 1 tilling per file (without the tilled from microscope) | |
* - Flat field correction file should have the same number of channel (but 1z) | |
* | |
*/ | |
ImagePlus divideImages(ImagePlus[] image, ImagePlus[] flatfield) { | |
if(image.length == flatfield.length) { | |
ic = new ImageCalculator(); | |
for(int c = 0; c < image.length; c++) { | |
image[c] = ic.run("Divide create 32-bit stack", image[c], flatfield[c]); | |
} | |
return RGBStackMerge.mergeChannels(image, false); | |
} | |
} | |
void exportAndStitch(File image_file, ImagePlus ff_image, int downsample) { | |
// Get the base directory for the multiposition file | |
base_dir = image_file.getParent(); | |
// Use it to create a save directory | |
File saveDir = new File(base_dir+File.separator+image_file.getName().replaceFirst("[.][^.]+$", "")+File.separator); | |
IJ.log(saveDir.getAbsolutePath()); | |
saveDir.mkdir(); | |
// Get the full path of the file. | |
String theFile = image_file.getPath(); | |
// lif files have their coordiante metadata stored in another unit... | |
// or at least bioformats has issues with them... | |
if(theFile.endsWith(".lif")) corr_factor= 1e6; | |
// All this code parses the metadata of the lif file so we can access the coordinates | |
ImageReader reader = new ImageReader(); | |
ServiceFactory factory = new ServiceFactory(); | |
OMEXMLService service = factory.getInstance( OMEXMLService.class ); | |
IMetadata meta = service.createOMEXMLMetadata(); | |
reader.setMetadataStore( meta ); | |
// Here we set the Bioformats reader to work on our file | |
reader.setId(theFile); | |
// And the retreive function will help us get all the metadata back | |
MetadataRetrieve retrieve = service.asRetrieve(reader.getMetadataStore()); | |
/* This is the reader for bioformats | |
* | |
*/ | |
opts = new ImporterOptions(); | |
opts.setId(theFile); | |
opts.setUngroupFiles(true); | |
//set up import process | |
process = new ImportProcess(opts); | |
process.execute(); | |
nseries = process.getSeriesCount(); | |
//reader belonging to the import process | |
i_reader = process.getReader(); | |
impReader = new ImagePlusReader(process); | |
double[] posX = new double[reader.getSeriesCount()]; | |
double[] posY = new double[reader.getSeriesCount()]; | |
String[] names = new String[reader.getSeriesCount()]; | |
vx = retrieve.getPixelsPhysicalSizeX(0).value(); | |
all_dims = null; | |
suffix = ""; | |
for (int i=0; i<nseries; i++) { | |
// for (int i=0; i<2; i++) { | |
if( retrieve.getPixelsSizeX(i). getNumberValue().intValue() < 2000) { | |
posX[i] = (retrieve.getPlanePositionX( i, 0 ).value()); | |
posY[i] = (retrieve.getPlanePositionY( i, 0 ).value()); | |
names[i] = retrieve.getImageName(i); | |
unit = retrieve.getPlanePositionY( i, 0 ).unit(); | |
opts.setSeriesOn(i,true); | |
i_reader.setSeries(i); | |
//read and process all images in series | |
imps = impReader.openImagePlus(); | |
//IJ.log("Length "+imps.length); | |
imp=imps[0]; | |
bd= imp.getBitDepth(); | |
ff_imp = imp; | |
// Perform the flatfield, if there is a file | |
if( ff_image != null ) { | |
ff_imp = divideImages(ChannelSplitter.split(imp),ChannelSplitter.split(ff_image)); | |
suffix = "_FF"; | |
} | |
small_ff_imp = ff_imp; | |
if (downsample>1) { | |
IJ.run(ff_imp, "Scale...", "x="+(1.0/downsample)+" y="+(1.0/downsample)+" z="+(1.0/downsample)+" create interpolation=Bilinear average"); | |
small_ff_imp = IJ.getImage(); | |
} | |
// Save image as TIFF | |
IJ.saveAs(small_ff_imp, "Tiff", saveDir.getAbsolutePath()+File.separator+names[i]+"_"+(i+1)+suffix+".tif"); | |
// Some cleanup | |
imp.changes=false; | |
imp.close(); | |
ff_imp.close(); | |
all_dims = small_ff_imp.getDimensions(); | |
small_ff_imp.close(); | |
opts.setSeriesOn(i, false); | |
} | |
} | |
reader.close(); | |
i_reader.close(); | |
// Get min in X and Y | |
List lX = Arrays.asList(ArrayUtils.toObject(posX)); | |
List lY = Arrays.asList(ArrayUtils.toObject(posY)); | |
minX = Collections.min(lX); | |
minY = Collections.min(lY); | |
dim=2; | |
z = ""; | |
if(all_dims[3] > 1) { | |
dim = 2; | |
z = ", 0.0"; | |
} | |
PrintWriter out = new PrintWriter(saveDir.getAbsolutePath()+File.separator+"positions.txt"); | |
out.println("#Define the number of dimensions we are working on:"); | |
out.println("dim = "+dim); | |
out.println("# Define the image coordinates"); | |
for (i=0; i<posX.length; i++) { | |
fposX = Math.round(((posX[i]-minX)*corr_factor/vx/downsample)); | |
fposY = Math.round(((posY[i]-minY)*corr_factor/vx/downsample)); | |
out.println(names[i]+"_"+(i+1)+suffix+".tif; ; ("+fposX+", "+fposY + z+")"); | |
} | |
out.close(); | |
compute = ""; | |
if(is_compute) { compute = "compute overlap "; } | |
// Define an Output directory if needed | |
output_dir = ""; | |
if (output_type != "Fuse and display") output_dir = "output_directory=["+saveDir.getAbsolutePath()+File.separator+"]"; | |
IJ.run("Grid/Collection stitching", "type=[Positions from file] "+ | |
"order=[Defined by TileConfiguration] "+ | |
"directory=["+saveDir.getAbsolutePath()+"] "+ | |
"layout_file=positions.txt "+ | |
"fusion_method=[Linear Blending] "+ | |
"regression_threshold=0.30 "+ | |
"max/avg_displacement_threshold=2.50 "+ | |
"absolute_displacement_threshold=3.50 "+ | |
compute+ | |
"computation_parameters=[Save memory (but be slower)] "+ | |
"image_output=["+output_type+"] "+ | |
output_dir); | |
// Save image in case output type was set to display | |
if (output_type.equals("Fuse and display")) { | |
final_image = IJ.getImage(); | |
IJ.saveAs(final_image, "Tiff", saveDir.getAbsolutePath()+File.separator+image_file.getName()+suffix+"-fused.tif"); | |
final_image.close(); | |
log.info("Fused image Saved"); | |
} | |
} | |
/* | |
* flatfield file opening and management | |
* We need to open the Flatfield image and split the channels. | |
*/ | |
is_not_flatfield = false; | |
ImagePlus ff_image = null; | |
ff_string = ff_file.getName(); | |
if (!ff_string.equals("none")) ff_image = IJ.openImage(ff_file.getPath()); | |
// Correction factor, needed for LIF files | |
corr_factor = 1.0; | |
File[] all_files = image_directory.listFiles(new FilenameFilter() { | |
public boolean accept(File dir, String name) { | |
return (name.toLowerCase().endsWith(".lsm") || (name.toLowerCase().endsWith(".lif"))); | |
} | |
}); | |
for(File f : all_files) { | |
exportAndStitch(f, ff_image, downsample); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment