ROI standard deviation.

We are trying to streamline our Catphan analysis by moving from ImageJ to pylinac.

During comparisons with our previous method (specifically for the 515 section (low contrast inserts) of the cbct analysis module) we found that in some cases the standard deviation of ROIs are lower on the pylinac compared to ImageJ or Eclipse measured ones (same ROI size/position to a reasonable extent).

Differences are on the order of 5.5 vs 7.5 HU for our acquisition settings. While this may seem small, it makes quite the difference in CNR calcs. We have tried ROIs on both inserts and background with similar results. Median values are the same which reassures us that, at least ROIs placed on the insert, are in roughly the same position. Surprisingly when comparing ROIs encompassing the whole phantom (on pylinac by manipulating the ROI radius and “dist2rois_mm”) the standard deviation is in full agreement between ImageJ, Eclipse, and pylinac.

A first thought is that some sort of smoothing might be happenning behind the scenes but that wouldn’t explain the similar sd for large ROIs. Any ideas?

Hi there,

Pylinac may average neighbouring slices so that at the end you will get a bit lower std. You can check this with:

print(mycbct.ctp515.combine_method, mycbct.ctp515.num_slices)

The first output will be the method used for averaging, and the second the number of slices below or above the CTP515 main slice that will be used for averaging. In my case I get “mean” and 1. I am using slice thickness of 2 mm.


Hi Dennis, three slice averaging (“mean” and “1” as well) was the culprit indeed , the std values now match our imageJ ones. Many thanks!

As a side note, what would be the easiest way to force the num_slices for each module when calling the analysis?

Στις Πέμπτη, 15 Οκτωβρίου 2020 στις 11:50:42 π.μ. UTC+1, ο χρήστης Denis έγραψε:

You could do something like this:

from pylinac import CatPhan604
from pylinac.ct import CTP515

class ChangeSliceAveragingCTP515(CatPhan604):
CTP515.num_slices = 1

mycbct = CatPhan604.from_zip(“”)
stds = [roi.std for roi in mycbct.ctp515.rois.values()]

print(mycbct.ctp515.combine_method, mycbct.ctp515.num_slices)

Sorry, the line before analyze() should be:

mycbct = ChangeSliceAveragingCTP515.from_zip("")

Amazing, thanks again!

Στις Σάββατο, 17 Οκτωβρίου 2020 στις 10:32:34 π.μ. UTC+1, ο χρήστης Denis έγραψε: