Last active
November 11, 2023 13:51
-
-
Save lacan/6f70f60e98fa3e44fa23f1d85c303c82 to your computer and use it in GitHub Desktop.
[Parallel Histrogram-Based Normalization] This performs Histogram Based Normalization on all slices of an open stack based on the selected reference slice #fiji #imageJ #histogram #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
#@ ImagePlus image | |
#@ Integer ( label = "Channel to correct (in case of multichannel images)", value = 1 ) ch_correct | |
#@ Integer ( label = "Reference Slice", value = 1 ) ref_slice | |
#@ Boolean ( label = "Use Manual Normalization Parameters Below", value = false ) is_normalize | |
#@ Integer ( Label = "Minimum" ) the_min | |
#@ Integer ( Label = "Maximum" ) the_max | |
// This variable will help us at the end to add the normalized channel back | |
def all_images = [] | |
// We process only one channel | |
if( image.getNChannels() > 1 ) { | |
all_images = ChannelSplitter.split( image ) | |
image = all_images[ch_correct-1] | |
} | |
// Perform Normalization before starting, if requested | |
// We only normalize the reference slice of the stack. This is also the reference histogram to match | |
println( "Using Slice " + ref_slice + " as reference" ) | |
def ref_ip = image.getStack().getProcessor(ref_slice).convertToFloat().duplicate() | |
if ( is_normalize ) { | |
def stats = ref_ip.getStatistics() | |
ref_ip.subtract(stats.min) | |
ref_ip.multiply(1/(stats.max-stats.min)) | |
ref_ip.multiply(the_max-the_min) | |
ref_ip.add(the_min) | |
// Convert to shorts as, we cannot match histograms for float images | |
ref_ip = ref_ip.convertToShort(false) | |
} | |
// Pick up the reference histogram | |
ref_hist = ref_ip.getHistogram() | |
// Do the histogram matching | |
def matchedImage= matchSliceHistograms(ref_hist, image) | |
def newImage = matchedImage | |
// If there are multtiple channels, re-merge them into a single image with the new corrected channel | |
if(image.getNChannels() > 1) { | |
all_images[ch_correct-1] = matchedImage | |
newImage = RGBStackMerge.mergeChannels(all_images, false) | |
newImage.setDisplayMode(image.getDisplayMode()) | |
newImage.setLuts(image.getLuts()) | |
} | |
// This function performs the histotram matching in parallel | |
def matchSliceHistograms( def refhist, def imp ) { | |
// Get the stack | |
def stack = imp.getStack() | |
// Produce the final stack which will hold the normalized image | |
def final_imp = IJ.createHyperStack( "Normalized-" + imp.getTitle(), imp.getWidth(), imp.getHeight(), imp.getNChannels(), imp.getNSlices(), imp.getNFrames(), imp.getBitDepth() ) | |
// Create the Histogram Matcher | |
def matcher = new HistogramMatcher() | |
// Get all cores minus one for the process | |
def cores = Runtime.getRuntime().availableProcessors() > 1 ? Runtime.getRuntime().availableProcessors() - 1 : 1 | |
GParsExecutorsPool.withPool( cores ) { | |
(1..stack.getSize()).eachParallel{ slice -> | |
println( "Processing Slice "+slice ) | |
def hist_old = stack.getProcessor( slice ).getHistogram() | |
// Finally match the histograms with this great little class | |
def newHist = matcher.matchHistograms( hist_old, refhist ) | |
// We need to duplicate it, otherwise it will not work. there must a reference issue | |
def ip = stack.getProcessor( slice ).duplicate() | |
// This applies the matched to the current processor | |
ip.applyTable( newHist ) | |
stack.setProcessor( ip, slice ) | |
} | |
} | |
final_imp.setStack(stack) | |
final_imp.show() | |
} | |
// Imports | |
import histogram2.HistogramMatcher | |
import ij.plugin.ChannelSplitter | |
import ij.plugin.RGBStackMerge | |
import ij.IJ | |
import groovyx.gpars.GParsExecutorsPool |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment