Skip to content

Instantly share code, notes, and snippets.

@lacan
Last active February 15, 2023 06:20
Show Gist options
  • Save lacan/cb2eaba47ad001c49b7c232dc6313de9 to your computer and use it in GitHub Desktop.
Save lacan/cb2eaba47ad001c49b7c232dc6313de9 to your computer and use it in GitHub Desktop.
[Operetta Export Contiguous Fields] Uses Flood filling on the fields to find which ones are direct neighbors and exports each as a separate ImagePlus. Example #Operetta #Fiji #Groovy #BIOP
// This script was created for Anna Hamacher, as per an internal discussion on the ImageSC forum
// that stemmed from https://forum.image.sc/t/viewing-slide-scans-from-operetta-perkin-elmer-cls-as-overview-montage-images/52942/13?u=oburri
// Code by Olivier Burri, EPFL - SV - PTECH - BIOP
// Edited by Anna Hamacher
// Last edition: July 2022
#@ File dir (label="Images Folder", style="directory")
#@Integer downsample (label="Downsample Factor", value=1)
#@String z_projection_method (label = "Projection Type", choices = {"No Projection", "Average Intensity", "Max Intensity", "Min Intensity", "Sum Slices", "Standard Deviation", "Median"} )
#@File outputDir (label="Output directory", style="directory")
def id = new File( dir, "Index.idx.xml" )
// Minimal example, export everything
opm = new OperettaManager.Builder()
.setId( id )
.setSaveFolder(outputDir)
.setProjectionMethod()
.setProjectionMethod( z_projection_method )
.build();
def allWells = opm.getAvailableWells( )
allWells.each{ well ->
def clusters = getContigiousFields(well, 0.1)
def fieldsClusters = clusters.collect{ it.collect{ it.field } }
// Get XY coordinates of top left field from each cluster
//def coordClusters = clusters.collect{ [x:it.min{it.roi.x}.roi.x, y:it.min{it.roi.y}.roi.y ] }
//[fieldsClusters, coordClusters].transpose().each{ f, coord -> println f; println coord }
//return
fieldsClusters.eachWithIndex() {f, i ->
println f.size()
imp = opm.getWellImage( well, f, downsample, opm.getRange(), null )
def name = opm.getFinalWellImageName( well ) + "_region" + i
IJ.saveAsTiff(imp, new File( outputDir, name + ".tif" ).getAbsolutePath() )
}
}
// Basic Flood Fill for finding fields that are next to each other
// overlapRatio should be around 0.1 to find the right fields, especially if they do not overlap much
def getContigiousFields( def well, def overlapRatio ) {
def fields = opm.getAvailableFields( well )
def id = 0
def topleft = opm.getTopLeftCoordinates( fields )
// Build the rois
def rois = fields.collect{ field ->
def sample_id = field.getIndex( ).getValue( )
def x = ( opm.getUncalibratedPositionX( field ) - topleft.getLongPosition( 0 ) ) / downsample
def y = ( opm.getUncalibratedPositionY( field ) - topleft.getLongPosition( 1 ) ) / downsample
def w = ( opm.metadata.getPixelsSizeX( sample_id ).getValue( ) ) / downsample
def h = ( opm.metadata.getPixelsSizeY( sample_id ).getValue( ) ) / downsample
def extraW = w * overlapRatio
def extraH = h * overlapRatio
x -= extraW/2
y -= extraH/2
w += extraW
h += extraH
def roi = new Roi( x, y, w, h )
id++
return [id:id, field:field, roi:roi]
}
// Basic clustering algorithm using flood fill
// Define candidates that are available
def candidates = rois.collect()
// Define a list that will contain the discovered clusters
def clusters = []
// While there are available candidates
while (!candidates.isEmpty() ) {
// Take the last candidate as seed.
seed = candidates.pop()
// Prepare a queue which will contain all the fields to explore
def Q = [] as Queue
// Initialize this queue with the seed point
Q.offer(seed)
// Initialize this cluster, which contains the seed. This will contain the final list of fields that are part of this cluster
def cluster = [seed]
// Explore around the seedpoint, adding to the queue
while( !Q.isEmpty() ) {
// Get the first element of the queue, as we will insert new points to visit at the end
seed = Q.poll()
// Find all rois that overlap with the current one
def neighbors = candidates.findAll{ opm.isOverlapping( seed.roi, it.roi ) }
// If neighbors were found, add them to the queue, so they can be visited as well, remove them from the candidates and add them to the growing cluster list
if (neighbors.size() > 0) {
println( "Found ${neighbors.size()} neighbors" )
Q.addAll( neighbors )
candidates.removeAll( neighbors )
cluster.addAll( neighbors )
}
}
// When the queue is empty, we have a cluster ready, this will loop if there are candidates left
clusters.add( cluster )
}
return clusters
}
import ch.epfl.biop.operetta.OperettaManager
import ij.gui.Roi
import java.awt.Color
import ij.IJ
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment