Filter is doubling allocated memory (JAVA)

my tests are being done with a series that has approximately 118MB.
image

memory consumption of my software just by startup:
image

after loading the series:

this.reader.setFileNames(this.dicomFilesNames);
this.series = reader.execute();

image

finally, after running the final filter:

private void fixOrientation () {
        DICOMOrientImageFilter filter = new DICOMOrientImageFilter();
        switch (this.type) {
            case SCALAR -> filter.setDesiredCoordinateOrientation("RAI");
            case VECTOR -> filter.setDesiredCoordinateOrientation("LPI");
        }
        this.series = filter.execute(this.series);
    }

image

Can anyone explain how exactly simpleITK works in this situation and what can i do to solve the problem?

Hello @Salu_Ramos,

This looks like Java behavior not specific to SimpleITK. In the line:

this.series = filter.execute(this.series);

the variable this.series now points to a new memory location and the old one is still around waiting to be garbage collected (assuming no-one else is pointing at that memory location). You can request collection using System.gc(), but this is just a request and the memory may not be released till later on.

Possibly someone in the community with more Java expertise can comment more about Java memory management.

2 Likes

Hi @zivy,

even using System.gc() right after the filter, memory continues basically the same.

A question, was it expected that a series that occupies 118MB in disk would occupy 570MB in memory?
I mean, I don’t understand why it takes up almost 6 times more space.

Hi @Salu_Ramos,

As I said and as the documentation says, calling System.gc() is requesting/hinting to Java to invoke the garbage collector, it isn’t directly invoking it. I don’t know if there is a way to force immediate garbage collection which is why I asked for input from the community.

This is more of a Java issue, so possibly ask on in a forum dedicated to the language.

Also the SimpleITK library is a native library for JAVA, and that adds an additional layer of complexity to the memory management.

This article looks interesting:

How are you measuring memory allocation for the application? Just because the application has memory allocated does not mean that it is physically occupying the memory on the computer. This can happen when large libraries are mapped to memory and not physically loaded into memory ( SimpleITK is a large library ).

1 Like

i was using task manager, hehe.
Now i tried JProfiler with intellij idea.

The result of JProfiler are way too different from task manager and
resource monitor.
I think the JProfiler is not considering the jni lib heap memory (.dll).

task manager:

Hi @zivy, i understand that System.gc() does not guarantee the release of ram memory.

but this question still in my head.

To estimate the size of the image in memory, it is the number of pixels multiplied by the size of the pixel type. What are these values and your results?

1 Like

the series i’m using is a uint32 pixeltype.
the size is 512x512x549.

so, 4bytes * 512 * 512 * 549 = 575.668.224 bytes

which is ~575 MB, now that make’s sense to me.
i thought itk used some kind of compression system.

Anyway, that means I can save memory space by converting the image to an 8-bit pixel type, cool.

3 Likes

I found a method in ImageSeriesReader called setOutputPixelType that makes exactly what i need.

my code:

this.reader.setOutputPixelType(mainPixelID.getItkLightVersionID());

the method getItkLightVersionID is a enum method that returns de pixelID light version, basically map the higher bits version to the lower 8 bits version, keeping the sign (signed or unsigned) and type (scalar, vector or label).

example:
uint32 returns uint8
uint16 returns uint8
int16 returns int8
vectoruint64 returns vectoruint8

but my final result isn’t right.

result:

expected:

do you have any idea why is this happening?

Hello @Salu_Ramos,

The method you are using is simply coercing the values into uint8 so values that were originally negative and values that were greater than 255 are not represented correctly. You can read the image with the original intensity range, linearly rescale the intensities to [0,255] and then cast to uint8 and the image will look OK, but you will be loosing information that was contained in the original image.

There is no magic, you cannot directly fit the same amount of information into less memory (this isn’t a compressed representation of the values).

3 Likes

Thanks @zivy,

I managed to reproduce what you told me but it’s still not 100%, some layers are too white, see the layer scroll function and the jump in brightness. This doesn’t happen in other viewers even using default values ​​for wcww.

code:

this.reader.setFileNames(this.dicomFilesNames);
this.series = reader.execute();

RescaleIntensityImageFilter rescaleFilter = new RescaleIntensityImageFilter();
rescaleFilter.setOutputMinimum(0);
rescaleFilter.setOutputMaximum(255);
this.series = rescaleFilter.execute(this.series);

CastImageFilter castFilter = new CastImageFilter();
castFilter.setOutputPixelType(PixelIDValueEnum.sitkUInt8);
this.series = castFilter.execute(this.series);

Video unavailable
This video is private

I changed the video to public

If you do per-slice rescaling, instead of per-volume rescaling, you get those jumpy intensities from slice to slice.

i’m rescaling only once the entire series.

Maybe some slices are missing rescale slope/intercept tags? Or just a few slices have them?