Trainable Weka Error (large images)

fiji
weka
imagej
trainable-weka

#1

Hello everyone,

I tried to do some segmentation via the Trainable WEKA Segmentation plugin in Fiji. Therefore I loaded a classifier, clicked on “Apply classifier” and chose an image. I recorded this with the macro recorder:

selectWindow("203-001 - 2018-01-09 14.31.57_x20_z0_1.jpg");
run("Trainable Weka Segmentation");
selectWindow("Trainable Weka Segmentation v3.2.24");
call("trainableSegmentation.Weka_Segmentation.loadClassifier", "W:\\classifier003.model");
call("trainableSegmentation.Weka_Segmentation.applyClassifier", "W:\\Schnitte jpeg_gedreht - kleiner Stack", "203-001 - 2018-01-09 14.31.57_x20_z0_1.jpg", "showResults=true", "storeResults=false", "probabilityMaps=false", "");

The image I want the weka plugin to use on (actually there are several of them but I tried to do it one by one) are quite large (approximately 20k x 18,5k pixels). However, the segmentation ends after quite a while with an error on the log:

WARNING: core mtj jar files are not available as resources to this classloader (sun.misc.Launcher$AppClassLoader@764c12b6)
Exception in thread "Thread-14" java.lang.OutOfMemoryError: Java heap space
	weka.core.DenseInstance.copy(DenseInstance.java:145)
	weka.core.Instances.add(Instances.java:322)
	weka.core.Instances.copyInstances(Instances.java:2175)
	weka.core.Instances.<init>(Instances.java:237)
	trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5991)
	trainableSegmentation.WekaSegmentation$1ApplyClassifierThread.run(WekaSegmentation.java:4996)

	at weka.core.DenseInstance.copy(DenseInstance.java:145)
	at weka.core.Instances.add(Instances.java:322)
	at weka.core.Instances.copyInstances(Instances.java:2175)
	at weka.core.Instances.<init>(Instances.java:237)
	at trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5991)
	at trainableSegmentation.WekaSegmentation$1ApplyClassifierThread.run(WekaSegmentation.java:4996)
Exception in thread "Thread-13" java.lang.NullPointerException
	trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5054)
	trainableSegmentation.Weka_Segmentation$1ImageProcessingThread.run(Weka_Segmentation.java:1817)

	at trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5054)
	at trainableSegmentation.Weka_Segmentation$1ImageProcessingThread.run(Weka_Segmentation.java:1817)
Exception in thread "Thread-17" java.lang.OutOfMemoryError: Java heap space
	trainableSegmentation.FeatureStack.createInstance(FeatureStack.java:3155)
	trainableSegmentation.FeatureStack.createInstances(FeatureStack.java:2184)
	trainableSegmentation.WekaSegmentation$1ApplyClassifierThread.run(WekaSegmentation.java:4992)

	at trainableSegmentation.FeatureStack.createInstance(FeatureStack.java:3155)
	at trainableSegmentation.FeatureStack.createInstances(FeatureStack.java:2184)
	at trainableSegmentation.WekaSegmentation$1ApplyClassifierThread.run(WekaSegmentation.java:4992)
Exception in thread "Thread-16" java.lang.NullPointerException
	trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5054)
	trainableSegmentation.Weka_Segmentation$1ImageProcessingThread.run(Weka_Segmentation.java:1817)

	at trainableSegmentation.WekaSegmentation.applyClassifier(WekaSegmentation.java:5054)
	at trainableSegmentation.Weka_Segmentation$1ImageProcessingThread.run(Weka_Segmentation.java:1817)

To me it sounds like my memory is not large enough (I work on a cluster with 512 GB RAM(!)). Is there any way to get this working?

Many thanks in advance!

Max


#2

Dear @MaxAC,

It definitely is a memory issue. Are you sure you are running Fiji with full access to your 512GB of RAM? Have a look at this page in the wiki to see how to increase that number.

Another option consists on dividing your large image into tiles of “affordable” size, classify each tile separately and put them back together.


#3

Dear @iarganda,

thanks for your reply! I looked it up and Fiji did use ‘only’ 300 something GB. I will try it again now. In case it does not work out again, do you have a idea how I can automatically divide the picture in tiles and put it back together afterwards?

Thanks in advance!

Max

EDIT.: it worked out, thanks a lot!


#4

Dear @MaxAC,
I was trying to write a macro to do the image division and I ended up creating a new method in the plugin library to perform classification by tiling the input image. Please, update the plugin to the latest release and try the following Beanshell script:

// @File(label="Input directory", description="Select the directory with input images", style="directory") inputDir
// @File(label="Output directory", description="Select the output directory", style="directory") outputDir
// @File(label="Weka model", description="Select the Weka model to apply") modelPath
// @String(label="Result mode",choices={"Labels","Probabilities"}) resultMode
// @Integer(label="Number of tiles in X:", description="Number of image subdivision in the X direction", value=3) xTiles
// @Integer(label="Number of tiles in Y:", description="Number of image subdivision in the Y direction", value=3) yTiles
// @Integer(label="Number of tiles in Z (if 3D):", description="Number of image subdivision in the Z direction (ignored when using 2D images)", value=3) zTiles
 
import trainableSegmentation.WekaSegmentation;
import trainableSegmentation.utils.Utils;
import ij.io.FileSaver;
import ij.IJ;
import ij.ImagePlus;
  
// starting time
startTime = System.currentTimeMillis();
  
// caculate probabilities?
getProbs = resultMode.equals( "Probabilities" );
 
// create segmentator
segmentator = new WekaSegmentation( zTiles > 0 );
// load classifier
segmentator.loadClassifier( modelPath.getCanonicalPath() );
  
// get list of input images
listOfFiles = inputDir.listFiles();
for ( i = 0; i < listOfFiles.length; i++ )
{
    // process only files (do not go into sub-folders)
    if( listOfFiles[ i ].isFile() )
    {
        // try to read file as image
        image = IJ.openImage( listOfFiles[i].getCanonicalPath() );
        if( image != null )
        {
        	tilesPerDim = new int[ 2 ];
        	if( image.getNSlices() > 1 )
        	{
        		tilesPerDim = new int[ 3 ];
        		tilesPerDim[ 2 ] = zTiles;
        	}
        	tilesPerDim[ 0 ] = xTiles;
        	tilesPerDim[ 1 ] = yTiles;
        	
            // apply classifier and get results (0 indicates number of threads is auto-detected)
            result = segmentator.applyClassifier( image, tilesPerDim, 0, getProbs );

			if( !getProbs )
            	// assign same LUT as in GUI
            	result.setLut( Utils.getGoldenAngleLUT() );
             
            // save result as TIFF in output folder
            outputFileName = listOfFiles[ i ].getName().replaceFirst("[.][^.]+$", "") + ".tif";
            new FileSaver( result ).saveAsTiff( outputDir.getPath() + File.separator + outputFileName );
  
            // force garbage collection (important for large images)
            result = null; 
            image = null;
            System.gc();
        }
    }
}
// print elapsed time
estimatedTime = System.currentTimeMillis() - startTime;
IJ.log( "** Finished processing folder in " + estimatedTime + " ms **" );
System.gc();

You can define how many subdivisions (or tiles) to use per image dimension. Only the memory needed to process a tile will be use at each time, so it should be a bit slower than the regular method but much less memory consuming while obtaining basically the same result. Let me know if this works for you!


#5

Dear @iarganda,

When I try the tiling macro it is unfortunately unsuccessful for me, however it would certainly be very useful. I am using Trainable Weka Segmentation 3D version 3.2.28.

Below is the macro script I would usually use (and have previously run successfully), the macro script uses a previously made classifier.model and data.arff file (in the tiling script you have created there is no option to load a data.arff file and wonder whether that may be where the problem lies?).

run(“Raw…”, “open=[F:/Synchrotron Data Test/102938/Raw data/102938_1700_1700_100_8bit_nlm.raw] image=8-bit width=1700 height=1700 number=100”);
run(“Trainable Weka Segmentation 3D”);
selectWindow(“Trainable Weka Segmentation v3.2.28”);
call(“trainableSegmentation.Weka_Segmentation.loadData”, “F:\Synchrotron Data Test\102938\Raw data\data.arff”);
call(“trainableSegmentation.Weka_Segmentation.loadClassifier”, “F:\Synchrotron Data Test\102938\Raw data\5 um\classifier.model”);
call(“trainableSegmentation.Weka_Segmentation.trainClassifier”);
call(“trainableSegmentation.Weka_Segmentation.getResult”);
selectWindow(“Classified image”);
close();

The script works for a 1700x1700x100 volume, however I need to segment the full 1700x1700x1700 volume (which would be equivalent to 17 Z tiles).

Many thanks for your time, I look forward to hearing from you,

Ben C


#6

Dear @benc6,

The script of the previous post is used when we want to apply an already trained classifier to new images. In your case, you are training the classifier from the loaded data so no tiling is done.

One option would be to write a script (not a macro) that trains the classifier from the data only (not using the loaded image) and then applying it using the tiling strategy. Does it make sense?


#7

Dear @iarganda,

Thank you for your reply,

Perhaps I had confused the issue by outlining the code I used to train my original classifier. However I do have an already trained classifier (A classifier.model and data.arff file) that I created using a 1700x1700x100 volume, that I now want to apply to the full 1700x1700x1700 volume using the tiling strategy.

Applying the classifier on the 1700x1700x100 volume uses 150GB system memory on a 200GB system, therefore tiling is the only strategy that I can feasibly use for the full 1700x1700x1700 volume (Z tiling x17).

However I do not know how to amend the code outlining the tiling strategy in the earlier post to apply it to my dataset, as when I tried to use this code I was not successful (which may be due to the fact that I also need to apply the data.arff file, however I can’t be certain why I could not get the tiling strategy code to work).

Any advice or guidance that you may be able to provide would be greatly received. I have commonly used macro language but am an inexperienced coder.

Kind regards,

Ben C


#8

Dear @benc6

If your classifier is already trained, you only need to run the script from the Script Editor and select the input folder with you images to be segmented, the output folder where you want the results to be saved, the path to the trained classifier (Weka model), the results mode (labels or probabilities) and the number of tiles to use.

If your problem is how to run the script, it is easy:

  1. Go to “File > New > Script…”. That will open the Script Editor.
  2. Copy/paste the script in editor.
  3. In the editor window, select “Language > BeanShell”. That should highlight the code.
  4. Click on the “Run” button.

#9

Dear @iarganda

Thank you for your time and patience with this. The problem I have is that after loading the input folder containing the .raw file, the output folder and the classifier.model, I get no output.

In order for you to get a better understanding of my problem, I have uploaded a zip file containing: An input folder containing a 100x100x100 8 bit image, an output folder containing no files, your BeanShell Script file and predefined classifier.model and data.arff files. When I run the script with this simple setup and try to run the script with 4 X-Y and Z-tiles, I get no output.

Whilst this is a much smaller volume than for my actual problem, I believe this may better help to address my problem.

Many thanks for your time,

Ben C

Weka_Tiling_Test.zip (515.5 KB)


#10

@benc6 got it! The problem is a .raw file cannot be open directly by ImageJ without specifying the size and bit depth. I saved the file as TIFF and the script worked smoothly. Please, give it a try and let me know how it goes for you!


#11

Dear @iarganda

I had suspected conversion from .raw to a Tiff stack may have provided a solution, but tried this yesterday without success, as whilst the script runs and a binarised image is produced, the end result is highly inaccurate.

Assuming the .raw file has been converted into a stack of Tiff files and saved in the input folder, the script only works when the number of Z tiles is 0. The script works when you select for example X,Y tiles = 1or2or4 and Z tiles = 0, however the segmented result is completely wrong unfortunately.

From the new attached zip file you can much better understand what I mean. Within the zip folder there are three folders: In ‘folder 1’ is the Tiff stack (converted from the .raw file), in ‘folder 2’ is the inaccurate segmented result from selecting X,Y tiles = 4 and Z tiles = 0 using the tiling strategy, and finally ‘folder 3’ is an accurate segmented result from loading the classifier.model file and data.arff file into the 3D weka plugin (whilst this method is accurate, I cannot apply this method to my 1700x1700x1700 volume due to computer system memory limitations, and why I am trying to get the tiling strategy method to work).

I appreciate your time and I look forward to hearing any possible solutions you may have. I also hope I have explained myself well enough.

Kind regards,

Ben C

New weka zip file.zip (810.8 KB)


#12

@benc6 OK, I see the problem now. A few clarifications:

  1. If you select Z tiles = 0, the script will call TWS in 2D, not 3D. That’s why the result does not make sense.
  2. In the input folder, you need to store your image as a single 3D TIFF image (a stack) not a sequence of 2D images.

Try this and let me know how it goes. It works on my side.


#13

@iarganda,

Thank you for your time, now that I have converted the .raw file into a single 3D TIFF image (which in honesty I had not realised was a type of file format until now) it works well.

I will confirm on this thread that it has also worked for the 1700x1700x1700 volume once I have re-run the script for the larger volume.

Many thanks,

Ben C


#14

@benc6 I’m glad to hear that! Please don’t be shy on using a high number of tiles. In my experience, you might save a lot of RAM memory while preserving the computation time and basically the same classifier performance.


#15

Hi @iarganda,

I can confirm that having converted the .raw file into a 3D TIFF file, the tiling strategy worked successfully.

However, an important note to future members of the community that encounter the same issue, if you make a classifier.model using a .raw file, it does not work correctly if applied to a 3D TIFF file. Therefore you have to create a new classifier.model using the 3D Tiff file before using the tiling method, and apply the new classifier.model.

Thank you for your time,

Ben C


#16

What do you mean? The image format is not important, apart from being 2D or 3D. In that case, yes, because you have to save the model either using TWS or TWS 3D.


#17

Hi @iarganda

When I applied my classifier.model file previously created using a .raw file to the 3D TIFF file, the segmentation result was not accurate (i.e. the 2 phases were not classified correctly). When I created a new 3D weka classifier using the 3D Tiff, then it worked fine. I cannot explain why this was the case, but this is what I found.


#18

That’s strange. I used the .model file you sent me with the 3D Tiff and worked as I expected. In any case, as I said, the file format shouldn’t be an issue as long as you trained and test with the same data format (i.e. 2D grayscale -> 2D grayscale, 2D RGB -> 2D RGB, 3D grayscale -> 3D grayscale, 3D RGB -> 3D RGB).