documents:120206pyip_cooking:python_imagej_cookbook
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revisionNext revisionBoth sides next revision | ||
documents:120206pyip_cooking:python_imagej_cookbook [2021/06/24 07:10] – added 3D ImageJ Suite usages kota | documents:120206pyip_cooking:python_imagej_cookbook [2022/10/16 06:35] – Using Java8 Stream kota | ||
---|---|---|---|
Line 3: | Line 3: | ||
This page was last edited at: ~~LASTMOD~~ | This page was last edited at: ~~LASTMOD~~ | ||
- | For learning image processing using Fiji and Jython scripting, go to excellent tutorials written by Albert Cardona, such as [[https:// | + | For learning image processing using Fiji and Jython scripting, go to excellent tutorials written by Albert Cardona, such as [[https:// |
This page is like a cookbook: there are no details about how to do programming, | This page is like a cookbook: there are no details about how to do programming, | ||
Line 91: | Line 91: | ||
for j in range(imp.getHeight()): | for j in range(imp.getHeight()): | ||
+ | </ | ||
+ | |||
+ | ===== ImageJ2 / SciJava ===== | ||
+ | |||
+ | ==== Running a script from another script ==== | ||
+ | |||
+ | <code python> | ||
+ | #@ ScriptService scriptService | ||
+ | |||
+ | fullscript = "#@ String A_STRING\n" | ||
+ | "#@ Boolean (label=' | ||
+ | |||
+ | scriptmodule = scriptService.run(" | ||
+ | scriptInputmaps = scriptmodule.getInputs() | ||
+ | |||
+ | print scriptmodule.getOutputs() | ||
+ | print scriptInputmaps | ||
+ | # how can we know the dialog was Canceled? | ||
+ | print scriptmodule.isInputResolved(' | ||
</ | </ | ||
Line 222: | Line 241: | ||
print ff.getName() | print ff.getName() | ||
</ | </ | ||
+ | |||
+ | Fiji comes with ApacheIO library, and can be used quite conveniently for disintegrating file paths. See [[https:// | ||
+ | <code python linenums: | ||
+ | from org.apache.commons.io import FilenameUtils | ||
+ | |||
+ | srcpath = '/ | ||
+ | |||
+ | |||
+ | baseName = FilenameUtils.getBaseName(srcpath) | ||
+ | print " | ||
+ | |||
+ | ext = FilenameUtils.getExtension(srcpath) | ||
+ | print " | ||
+ | |||
+ | filename = FilenameUtils.getName(srcpath) | ||
+ | print "File name: ", filename | ||
+ | |||
+ | pathWOext = FilenameUtils.removeExtension(srcpath) | ||
+ | print " | ||
+ | |||
+ | # outputs | ||
+ | # | ||
+ | # Basename: | ||
+ | # Extension: | ||
+ | # File name: data.csv | ||
+ | # Fullpath without ext: / | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
==== Loading a textfile as String ==== | ==== Loading a textfile as String ==== | ||
Line 592: | Line 641: | ||
< | < | ||
{" | {" | ||
+ | </ | ||
+ | |||
+ | ==== Generating Time Stamp String ==== | ||
+ | |||
+ | <code python> | ||
+ | from java.util import Date | ||
+ | from java.text import SimpleDateFormat | ||
+ | |||
+ | timeStamp = SimpleDateFormat(" | ||
+ | print timeStamp | ||
</ | </ | ||
Line 728: | Line 787: | ||
zip command creates a list of tuples from elements of arrays a and b. | zip command creates a list of tuples from elements of arrays a and b. | ||
+ | ==== Using Java8 Stream ==== | ||
+ | |||
+ | Stream-related syntax introduced from Java8 is useful for writing clear codes, but cannot be directly used in Jython. Below is a way to use StreamAPI, by introducing Jython classes implementing Consumer interface. | ||
+ | |||
+ | <code python> | ||
+ | from java.util.Arrays import asList | ||
+ | from java.util.function import Predicate, Consumer, Function | ||
+ | from java.util.stream import Collectors | ||
+ | from java.util import Arrays | ||
+ | |||
+ | |||
+ | class jc(Consumer): | ||
+ | def __init__(self, | ||
+ | self.accept=fn | ||
+ | |||
+ | class jf(Function): | ||
+ | def __init__(self, | ||
+ | self.apply = fn | ||
+ | |||
+ | class jp(Predicate): | ||
+ | def __init__(self, | ||
+ | self.test = fn | ||
+ | | ||
+ | def jprint(x): | ||
+ | print x | ||
+ | |||
+ | tt = [" | ||
+ | c = Arrays.stream(tt).count() | ||
+ | print c | ||
+ | |||
+ | print " | ||
+ | Arrays.stream(tt).forEach(jc(lambda x: jprint(" | ||
+ | |||
+ | print " | ||
+ | Arrays.stream(tt).parallel().forEach(jc(lambda x: jprint(" | ||
+ | |||
+ | print "has b?", Arrays.stream(tt).anyMatch(jp(lambda x: x==" | ||
+ | print "has z?", Arrays.stream(tt).anyMatch(jp(lambda x: x==" | ||
+ | |||
+ | # convert to Java List<> | ||
+ | jtt = Arrays.asList(tt) | ||
+ | |||
+ | jtt.stream().forEach(jc(lambda x: jprint(" | ||
+ | </ | ||
===== Event Listener ===== | ===== Event Listener ===== | ||
Line 780: | Line 883: | ||
</ | </ | ||
+ | ===== GUI: wait for user ===== | ||
+ | |||
+ | To pause the script processing and wait for the user input (e.g. creating amanual ROI), use WaitForUserDialog class. | ||
+ | |||
+ | <code python> | ||
+ | from ij.gui import WaitForUserDialog | ||
+ | |||
+ | print(" | ||
+ | wud = WaitForUserDialog(" | ||
+ | print(" | ||
+ | wud.show() | ||
+ | print(" | ||
+ | </ | ||
===== HashMap ===== | ===== HashMap ===== | ||
Line 909: | Line 1025: | ||
outimp.show() | outimp.show() | ||
</ | </ | ||
+ | |||
+ | Another example with the map function | ||
+ | |||
+ | <code python> | ||
+ | # splits multichannel-zstack hyperstack and apply zprojection | ||
+ | from ij import IJ | ||
+ | from ij.plugin import ZProjector | ||
+ | from ij.plugin import ChannelSplitter | ||
+ | |||
+ | def sumzp( imp ): | ||
+ | zp = ZProjector(imp) | ||
+ | zp.setMethod(ZProjector.SUM_METHOD) | ||
+ | zp.doProjection() | ||
+ | zpimp = zp.getProjection() | ||
+ | return zpimp | ||
+ | |||
+ | imp = IJ.getImage() | ||
+ | imps = ChannelSplitter.split( imp ) | ||
+ | zpimps = map(sumzp, imps) | ||
+ | zpimps[0].show() | ||
+ | </ | ||
+ | |||
==== Threshold to create a mask (Binary) ==== | ==== Threshold to create a mask (Binary) ==== | ||
Line 942: | Line 1080: | ||
maskimp.show() | maskimp.show() | ||
</ | </ | ||
+ | |||
+ | === Stack to Mask by a threshold value === | ||
+ | |||
+ | Here is an example script to create a mask from an 8-bit stack using intensity thresholding. | ||
+ | The threshold value is derived by the Otsu algorithm using the full stack histogram. | ||
+ | |||
+ | < | ||
+ | from ij import IJ, ImagePlus, ImageStack | ||
+ | from ij.plugin import ChannelSplitter | ||
+ | from ij.process import StackStatistics | ||
+ | from fiji.threshold import Auto_Threshold | ||
+ | |||
+ | #imp = IJ.openImage(" | ||
+ | imp = IJ.getImage() | ||
+ | imps = ChannelSplitter.split( imp ) | ||
+ | imp1 = imps[0] | ||
+ | imp1bin = imp1.duplicate() | ||
+ | |||
+ | # get auto threshold value | ||
+ | stats = StackStatistics(imp1bin) | ||
+ | histdouble = stats.histogram() | ||
+ | |||
+ | # need this conversion from double to int | ||
+ | histint = map(lambda x:int(x), histdouble) | ||
+ | th = Auto_Threshold.Otsu(histint) | ||
+ | |||
+ | for i in range(imp1bin.getStackSize()): | ||
+ | ip = imp1bin.getStack().getProcessor( i + 1) | ||
+ | ip.threshold(th) | ||
+ | |||
+ | IJ.run(imp1bin, | ||
+ | imp1bin.show() | ||
+ | </ | ||
+ | |||
+ | To do this by accessing the pixel array of the stack, here is the way. It takes a longer time than above, so this is just to show the technique to process by pixel values using float processor pixel array object. | ||
+ | |||
+ | <code python> | ||
+ | |||
+ | from ij import IJ, ImagePlus, ImageStack | ||
+ | from ij.plugin import ChannelSplitter | ||
+ | from ij.process import StackStatistics | ||
+ | from ij.process import FloatProcessor | ||
+ | from fiji.threshold import Auto_Threshold | ||
+ | import jarray | ||
+ | |||
+ | imp = IJ.openImage(" | ||
+ | #imp = IJ.getImage() | ||
+ | imps = ChannelSplitter.split( imp ) | ||
+ | imp1 = imps[0] | ||
+ | ww = imp1.getWidth() | ||
+ | hh = imp1.getHeight() | ||
+ | binstack = ImageStack( ww, hh) | ||
+ | |||
+ | # get auto threshold value | ||
+ | stats = StackStatistics(imp1) | ||
+ | histdouble = stats.histogram() | ||
+ | histint = map(lambda x:int(x), histdouble) | ||
+ | th = Auto_Threshold.Otsu(histint) | ||
+ | |||
+ | for i in range(imp1.getStackSize()): | ||
+ | slicepixA = imp1.getStack().duplicate().convertToFloat().getPixels(i + 1) | ||
+ | # pixmin = reduce(min, slicepixA) | ||
+ | # pixmax = reduce(max, slicepixA) | ||
+ | # print " | ||
+ | slicepixA = map(lambda x: 0.0 if x<th else 255.0, slicepixA) | ||
+ | fp = FloatProcessor( ww, hh, slicepixA, None) | ||
+ | bp = fp.convertToByteProcessor() | ||
+ | binstack.addSlice(str(i+1), | ||
+ | |||
+ | binimp = ImagePlus(" | ||
+ | binimp.show() | ||
+ | </ | ||
+ | |||
==== ROI manager ==== | ==== ROI manager ==== | ||
Line 988: | Line 1199: | ||
istats = ip.getStatistics() | istats = ip.getStatistics() | ||
print istats.mean | print istats.mean | ||
+ | </ | ||
+ | |||
+ | === Saving All ROIs as a single zip file === | ||
+ | |||
+ | <code py> | ||
+ | import os, jarray | ||
+ | from ij.plugins.frame.RoiManager | ||
+ | |||
+ | outdir = "/ | ||
+ | roizipname = " | ||
+ | roizippath = os.path.join(outdir, | ||
+ | rm = RoiManager.getInstance() | ||
+ | if rm.getCount() > 0: | ||
+ | roiindex = jarray.array(range(0, | ||
+ | rm.setSelectedIndexes(roiindex) | ||
+ | rm.runCommand(' | ||
</ | </ | ||
Line 1335: | Line 1562: | ||
For more explanation about this processing, see [[http:// | For more explanation about this processing, see [[http:// | ||
+ | |||
+ | ==== Background Subtraction (Rolling Ball) ==== | ||
+ | |||
+ | Menu item [Process > Subtract Background] | ||
+ | |||
+ | <code python> | ||
+ | from ij import IJ | ||
+ | from ij.plugin.filter import BackgroundSubtracter | ||
+ | |||
+ | imp = IJ.getImage() | ||
+ | |||
+ | ip = imp.getProcessor() | ||
+ | radius = 50.0 | ||
+ | createBackground = False | ||
+ | lightBackground = False | ||
+ | useParaboloid = False | ||
+ | doPresmooth = False | ||
+ | correctCorners = False | ||
+ | |||
+ | bs = BackgroundSubtracter() | ||
+ | bs.rollingBallBackground(ip, | ||
+ | imp.updateAndDraw() | ||
+ | </ | ||
+ | |||
+ | If the image is in RGB, then use a different method (rollingBallBrightnessBackground). | ||
+ | |||
+ | For more explanation about this processing, see [[https:// | ||
==== ImageCalculator ==== | ==== ImageCalculator ==== | ||
Line 1695: | Line 1949: | ||
fullimps[0].show() | fullimps[0].show() | ||
</ | </ | ||
+ | |||
+ | If you do not want to open all images (called sereies) in multi-image CZI files, replace | ||
+ | < | ||
+ | open.setOpenAllSeries(True) | ||
+ | </ | ||
+ | with | ||
+ | < | ||
+ | options.setSeriesOn(seriesIndex, | ||
+ | </ | ||
+ | with " | ||
See here for more on metadata parsing and so on: [[https:// | See here for more on metadata parsing and so on: [[https:// | ||
Line 1856: | Line 2120: | ||
===== Plugin: Advanced Weka Segmentation ===== | ===== Plugin: Advanced Weka Segmentation ===== | ||
+ | |||
+ | ==== Adding Eamples from ROI ==== | ||
+ | |||
Training and get Probability image using ROI set as examples (labels). | Training and get Probability image using ROI set as examples (labels). | ||
Line 1887: | Line 2154: | ||
classifiedImage = weka.getClassifiedImage() | classifiedImage = weka.getClassifiedImage() | ||
classifiedImage.show() | classifiedImage.show() | ||
+ | </ | ||
+ | |||
+ | ==== Merging ARFF data files ==== | ||
+ | |||
+ | <code python> | ||
+ | import weka.core.Version | ||
+ | from weka.core import Instances | ||
+ | from weka.core.converters import ConverterUtils | ||
+ | |||
+ | print weka.core.Version().toString() | ||
+ | pp = '/ | ||
+ | data1path = pp+ ' | ||
+ | data2path = pp+ ' | ||
+ | |||
+ | data1 = ConverterUtils.DataSource.read(data1path) | ||
+ | data2 = ConverterUtils.DataSource.read(data2path) | ||
+ | print " | ||
+ | print " | ||
+ | |||
+ | for i in range(data2.size()): | ||
+ | data1.add(data2.get(i)) | ||
+ | |||
+ | dataMerged = data1 | ||
+ | print " | ||
+ | |||
+ | # if data1 and data2 have the same size, the line below can be used. | ||
+ | #dataMerged = Instances.mergeInstances(data1, | ||
+ | |||
+ | dataMargedPath = pp + " | ||
+ | |||
+ | ConverterUtils.DataSink.write(dataMargedPath, | ||
+ | </ | ||
+ | |||
+ | ==== Merging ARFF data files in another way, train and apply classifier ==== | ||
+ | |||
+ | <code python> | ||
+ | from trainableSegmentation import WekaSegmentation | ||
+ | from ij import IJ | ||
+ | |||
+ | # == merging | ||
+ | |||
+ | pp = '/ | ||
+ | data1path = pp+ ' | ||
+ | data2path = pp+ ' | ||
+ | |||
+ | ws = WekaSegmentation() | ||
+ | data1 = ws.readDataFromARFF(data1path) | ||
+ | data2 = ws.readDataFromARFF(data2path) | ||
+ | ws.mergeDataInPlace(data1, | ||
+ | dataBalanced = WekaSegmentation.balanceTrainingData(data1) #optional: can balance the classes | ||
+ | ws.setLoadedTrainingData(dataBalanced) | ||
+ | |||
+ | dataOutpath = pp+ ' | ||
+ | ws.writeDataToARFF(dataBalanced, | ||
+ | |||
+ | # == now train and apply a classifier to an image | ||
+ | |||
+ | if ws.trainClassifier(): | ||
+ | imp = IJ.getImage() | ||
+ | classifiedImage = ws.applyClassifier(imp, | ||
+ | classifiedImage.show() | ||
+ | |||
</ | </ | ||
Line 2052: | Line 2381: | ||
===== Plugin: MorphoLibJ ===== | ===== Plugin: MorphoLibJ ===== | ||
+ | Javadoc: [[http:// | ||
==== Distance Transform Watershed 3D ==== | ==== Distance Transform Watershed 3D ==== | ||
Line 2093: | Line 2423: | ||
outimp.setTitle(imp.getShortTitle() + " | outimp.setTitle(imp.getShortTitle() + " | ||
outimp.show() | outimp.show() | ||
+ | </ | ||
+ | |||
+ | ==== Kill Border Objects, 2D Binary image ==== | ||
+ | |||
+ | <code py> | ||
+ | from ij import IJ, ImagePlus | ||
+ | from inra.ijpb.morphology import Reconstruction | ||
+ | |||
+ | imp = IJ.getImage() #binary image | ||
+ | newip = Reconstruction.killBorders(imp.getProcessor()) | ||
+ | newimp = ImagePlus(" | ||
+ | newimp.show() | ||
</ | </ | ||
Line 2101: | Line 2443: | ||
The plugin webpage is here: [[https:// | The plugin webpage is here: [[https:// | ||
+ | |||
+ | The real power of 3D Suite is using it as a library from scripts and plugins. Many useful classes are implemented for processing, segmenting, and most notably measuring 3D objects. | ||
+ | |||
+ | Source codes are located under framagit: | ||
+ | |||
+ | * [[https:// | ||
+ | * [[https:// | ||
+ | |||
+ | The Javadoc I created (Core and Plugins are merged): | ||
+ | |||
+ | * [[https:// | ||
==== Measureing Spherical 3D ROI positions ==== | ==== Measureing Spherical 3D ROI positions ==== | ||
Line 2236: | Line 2589: | ||
See explanation in [[http:// | See explanation in [[http:// | ||
+ | |||
+ | ==== Shell Command ==== | ||
+ | |||
+ | To run Shell command in OSX, here is an example. | ||
+ | |||
+ | <code python> | ||
+ | from java.lang import Runtime | ||
+ | from java.util import Scanner | ||
+ | |||
+ | def runShellCommand( cmd ): | ||
+ | proc = Runtime.getRuntime().exec(cmd) | ||
+ | istream = proc.getInputStream() | ||
+ | scan = Scanner(istream).useDelimiter(" | ||
+ | for ss in scan: | ||
+ | print ss | ||
+ | |||
+ | |||
+ | runShellCommand( " | ||
+ | runShellCommand( "ls -la" ) | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Is docker daemon running? ==== | ||
+ | |||
+ | Check if the docker daemon is running (): | ||
+ | |||
+ | <code python linenums: | ||
+ | from java.lang import Runtime | ||
+ | from java.util import Scanner | ||
+ | |||
+ | |||
+ | def checkDockerRunning( cmd ): | ||
+ | proc = Runtime.getRuntime().exec(cmd) | ||
+ | inputst = proc.getInputStream() | ||
+ | s = Scanner(inputst).useDelimiter(" | ||
+ | if not s.hasNext(): | ||
+ | print " | ||
+ | return False | ||
+ | for ss in s: | ||
+ | print ss | ||
+ | if ss.startswith(" | ||
+ | return True | ||
+ | else: | ||
+ | print " | ||
+ | return False | ||
+ | |||
+ | # below is for OSX. WIth win, just " | ||
+ | cmd = "/ | ||
+ | if checkDockerRunning( cmd ): | ||
+ | print " | ||
+ | else: | ||
+ | print " | ||
+ | </ | ||
===== JAVADOCS ===== | ===== JAVADOCS ===== | ||
- | * 3D ImageJ Suite [[http:// | + | * 3D ImageJ Suite |
+ | * [[http:// | ||
+ | * [[https:// | ||
* [[https:// | * [[https:// | ||
* [[https:// | * [[https:// |
documents/120206pyip_cooking/python_imagej_cookbook.txt · Last modified: 2022/10/16 07:12 by kota