Last active
February 15, 2023 06:20
-
-
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 file contains 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
// 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