Using ImageJ for visceral fat in CT imaging

imagej

#1

Hi There!
New to ImageJ…I am trying to use ImageJ to obtain visceral fat and subcutaneous fat areas in CT imaging. I have been looking through the web and cannot find a protocol for this, I have been playing around but I am not sure if what I am doing is correct

Anyone have some insight/would like to help newbie?

Thank you in advance!


Visceral fat área and subcutaneous fat in ct
Threshold values
#2

Good day,

interesting, but could you show a typical raw image in PNG- or TIF-format and explain in detail what you like to achieve.

[…] to obtain visceral fat and subcutaneous fat areas in CT imaging.

Computers don’t accept such formulations yet.

What do you mean by “obtain”?

Image Segmentation?
Area calculation?

Regards

Herbie


#3

Hi Herbie,

Thank you for your response! I have cross sectional CT imaging at the level of the umbilicus (see below png).

What I am trying to do is perform an image segmentation to get visceral fat and subcutaneous areas from this one CT section. This has been performed before in another article and the picture below shows what they did (https://www.ncbi.nlm.nih.gov/pubmed/?term=Association+between+high+visceral+fat+area+and+postoperative+complications+in+patients+with+Crohn’s+disease+following+primary+surgery)

This is what I want to do, but I am not sure the actual steps in using ImageJ to achieve this result.

Thank you in advance! I appreciate your time and assistance.


#4

Well this helps a bit but these are not original raw images in uncompressed format …

What you need to do is to study the ImageJ user guide
https://imagej.nih.gov/ij/docs/guide/index.html
and especially the section about thresholding.

The demo images from the literature shows an ideal case and I doubt that the raw images allow a comparably easy analysis. That said, you will need some preprocessing and we need to see typical, i.e. not the best, raw images.

Regards

Herbie


#5

I will take a look at the ImageJ user guide once again. I just want to make sure that I am using the software correctly.

I took a screenshot of one of the raw images as a PNG file.

Thank you!


#6

@suni87

What you need to do is ‘Segment’ those areas… so here are few helpful links to get you started (these protocols will be applicable to your images - so don’t worry):

Attaching an ORIGINAL image here (in the original file format) is the best way for us to help you develop a specific workflow though… so go ahead and do that when you have time.

eta


#7

Thank you Eta!

I will take a look at the segmentation links that you shared. I tried to attach the original but it would not allow me to (it is a DICOM file format)

Thank you so much!


#8

@suni87

You can always share it via Dropbox or so… and just give us the link to download the image(s).

eta


#9

Here is the file:

let me know if you have any issues opening.


#10

Good day,

The sample image arrived savely and can be opened by ImageJ.

Just to give some more hints apart from the threshold-topic in the user guide.

  1. If you open DICOM-images in ImageJ, you can read the Meta-Information (DICOM-header) via “Image >> Show Info…”
  2. Be aware that ImageJ does not per se show Hounsfield units, although you can calibrate images to your liking which however isn’t really necessary for your project.
  3. Have a look at the ImageJ supported image formats. You will recognize that 8bit images have a range of gray values going from 0 to 255 which isn’t enough to represent the common ±1000 HU. Therefore, ImageJ opens your Image as 16bit image which provides a much larger range of gray values.
  4. The large range (0 to 65535) of gray values of 16bit images can’t be displayed on computer screens, therefore this range is mapped to the 8bit range for display purposes only! The original data isn’t touched.
  5. You can use the “Image >> Adjust >> Brightness/Contrast…”-tool to define and alter the mapping.
  6. This tool also shows you the histogram of your image, i.e. the distribution of the gray values in your image. (See “Analyze >> Histogram” as well.)

The histogram of your sample image shows the presence of gray values in the range from 0 to 8191 which makes it a de facto 13bit image. It also shows that the sample image is over-exposed, i.e. there are regions in your sample image that have the maximum gray level (white) without any gray value differentiation (clipping). This image is not suited for analysis if these regions are of interest. The image is defective.

Please try to understand the above hints and study the relevant sections of the user guide.

Regards

Herbie

ADDITION:
Here is a first result from an attempt to get what is shown in figure 1b of the above publication:

Although thresholds can be set by hand, it is subjective and should be avoided for scientific reasons. Therefore, you should look for a suitable automatic threshold setting that suits your needs. In the above case “Intermodes” gives a reasonable result and you should test it with a variety of images. (If a decision has been made for one of the automatic schemes, you should use it for all of your images!)
For your sample image a hand-set lower threshold slightly above zero (e.g. 30) is acceptable, but you should also test if it generalizes.


#11

Here is an ImageJ-macro that returns a pretty good estimate of the total fat area (at least for the provided sample image):

requires("1.51w");
setBatchMode(true);
run("Median...", "radius=1");
run("Unsharp Mask...", "radius=3 mask=0.6");
setAutoThreshold("Intermodes");
getThreshold(lowT, upT);
setThreshold(40, upT);
setOption("BlackBackground", false);
run("Convert to Mask");
run("Median...", "radius=1");
doWand(0, getHeight()*0.5);
run("Duplicate...", "title=cropped");
run("Make Inverse");
run("Set...", "value=0");
run("Select None");
run("Invert LUT");
getStatistics( ar, mn );
totFatArea = ar * mn / 255;
close();
run("Revert");
print( "Total Fat Area: "+d2s(totFatArea,0)+"mm^2" );
setBatchMode(false);
exit();

Paste the above macro code to an empty macro window (Plugins >> New >> Macro) and run it.

The result I get is:
Total Fat Area: 22125mm^2

To automatically estimate the subcutaneous fat area is more involved because of this gap in the inner contour:
Gap

Although there are automatic ways to close such discontinuities, most of the “cheap” approaches change the area to be determined as well. Better approaches are costly. Therefore, closing such gaps by hand (line drawing) is an economic procedure …

HTH

Herbie


#12

Here is a macro version that copes with gaps in the inner contour, i.e. it reports both, the Total Fat Area and the Visceral Fat Area:

requires("1.51w");
setBatchMode(true);
run("Median...", "radius=1");
run("Unsharp Mask...", "radius=3 mask=0.6");
setAutoThreshold("Intermodes");
getThreshold(lowT, upT);
setThreshold(40, upT);
setOption("BlackBackground", false);
run("Convert to Mask");
run("Median...", "radius=1");
doWand(0, getHeight()*0.5);
run("Duplicate...", "title=cropped");
run("Make Inverse");
run("Set...", "value=0");
run("Select None");
run("Invert LUT");
getStatistics( ar, mn );
totFatArea = ar * mn / 255;
run("Select None");
run("Analyze Particles...", "size=15000-Infinity pixel show=[Outlines]");
run("Make Binary");
run("Fill Holes");
run("Invert LUT");
doWand(getWidth()*0.5, getHeight()*0.5);
close();
run("Restore Selection");
getStatistics( ar, mn );
vscFatArea = ar * mn / 255;
close();
run("Revert");
print( "Total Fat Area: "+d2s(totFatArea,0)+"mm^2" );
print( "Visceral Fat Area: "+d2s(vscFatArea,0)+"mm^2" );
setBatchMode(false);
exit();

Total Fat Area: 22125mm^2
Visceral Fat Area: 6420mm^2

It works for the provided sample image but may need some tuning for other images, but this is left to you and your coding skills.

Regards

Herbie


#13

Dear Suni,

I don’t know if it can have interest for you but I made fat segmentation using Weka machine learning.
I think with the threshold you will have problem of bone marrow inclusion especially in sacrum.

I used the weka classifier as it reckognize the fat tissue more specifically than thresholding but is also need much more calculation time.

If needed I can share with you my Weka classifier if you want to try

Best regards,

Salim


#14

Herbie,

Thank you so much for the macros! They work wonderfully well. I am going to try to use it for the other images I have, but my coding skills are not that great so I may not be able to use them for all the images in my dataset. Thank you again for all of your time and effort in helping me.

Suni


#15

Hi Salim,

Yes, I am interested. I will see how it compares to other methods. I think the issue I am running into is that the bowel can be mistaken for fat as it is hypotense on CT imaging.

Thank you in advance,
Suni


#16

Dear Suny,

download this jar (https://github.com/salimkanoun/CT_Segmentation/releases/download/0.2/CT_Segmentation.jar) put it in your plugin folder of Fiji and restart Fiji.

Then follow this guide https://github.com/salimkanoun/CT_Segmentation/blob/master/Segmentation%20Manual2.pdf

On your side you don’t have to care with the PET/CT viewer part, you will get a masked CT and then you will able to do anything you want in Fiji with it.

Don’t hesitate if you have questions,

Best regards,

Salim


#17

Hi Salim,

Thank you for the plugin. Unfortunately, it did not work. When I go to generate masked image, I get a black screen for every type of tissue.

Not sure what is happening

Suni


#18

I tried again and got this console error:

Exception in thread “AWT-EventQueue-0” java.lang.NullPointerException
at ij.plugin.Duplicator.crop(Duplicator.java:234)
at ij.plugin.Duplicator.run(Duplicator.java:184)
at ij.ImagePlus.duplicate(ImagePlus.java:2039)
at org.petctviewer.scintigraphy.CTSegmentation.CT_Segmentation.makeMaskedImage(CT_Segmentation.java:45)
at org.petctviewer.scintigraphy.CTSegmentation.CT_Segmentation_GUI$4.actionPerformed(CT_Segmentation_GUI.java:163)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2022)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2348)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:402)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:259)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:252)
at java.awt.Component.processMouseEvent(Component.java:6535)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3324)
at java.awt.Component.processEvent(Component.java:6300)
at java.awt.Container.processEvent(Container.java:2236)
at java.awt.Component.dispatchEventImpl(Component.java:4891)
at java.awt.Container.dispatchEventImpl(Container.java:2294)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4525)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
at java.awt.Container.dispatchEventImpl(Container.java:2280)
at java.awt.Window.dispatchEventImpl(Window.java:2750)
at java.awt.Component.dispatchEvent(Component.java:4713)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
at java.awt.EventQueue.access$500(EventQueue.java:97)
at java.awt.EventQueue$3.run(EventQueue.java:709)
at java.awt.EventQueue$3.run(EventQueue.java:703)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
at java.awt.EventQueue$4.run(EventQueue.java:731)
at java.awt.EventQueue$4.run(EventQueue.java:729)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)


#19

Dear Suni,

Are you using ImageJ or Fiji ?

My jar is made for Fiji as it use many dependencies that would require additional file in ImageJ alone.

Salim


#20

Hi Salim,

I am using Fiji and added the jar to it as you had instructed. I am going to try to restart my computer and see if it helps. The file format is DICOM and I am running Mac OS Sierra if that helps.

Thanks
Suni