Test 0.1 dMLC dosimetry

For the Varian RapidArc QA test procedures, does pylinac support performing Test 0.1 dMLC dosimetry? I can’t see this in the documentation, would analysis have to be constructed using the core modules?

Many thanks,


I haven’t used that test clinically, so yes it would have to be built by the user.

Thanks James, I’ll have a go at creating a test for it with the core modules, it should be fairly simple I think.

Regarding the core modules, I have found some unexpected behavior, for example I have a dicom image (dosim0.dmc) that is 1024 x 768 in size:

import matplotlib
from pylinac import core

import numpy

phantom_center = core.image.DicomImage(‘dosim0.dcm’).cax
print(phantom center)


Point(x=512.00, y=384.00, z=0.00)

So far so good. Then

img = core.image.load(‘dosim0.dcm’)


(768, 1024)

Again makes sense as shape returns the dimensions as y, x.

roi = core.roi.RectangleROI(array=img, width=2, height=2, angle=0, dist_from_center=0, phantom_center=phantom_center)

print('bl_x: ’ + str(roi.bl_corner.x))
print('bl_y: ’ + str(roi.bl_corner.y) + ‘\n’)

print('tr_x: ’ + str(roi.tr_corner.x))
print('tr_y: ’ + str(roi.tr_corner.y) + ‘\n’)


bl_x: 511
bl_y: 383

tr_x: 513
tr_y: 385

Again these make sense. However, roi.pixel_array seems to return the wrong roi when looking at the source:

@property def pixel_array(self): “”“The pixel array within the ROI.”“” return self._array[self.bl_corner.x:self.tr_corner.x, self.bl_corner.y:self.tr_corner.y]

Is this not slicing in this example as:

[511:513, 383:385]

Which is the wrong way round? As the array should be sliced [y, x] not [x,y]?

Sorry I may have misunderstood something, but I wanted to walk you through my logic.

Many thanks,


What is it you’re trying to do with all this? The underlying array in a RectangleROI is a numpy array, so it will have row, col indexing. You could also plot the rectangle with the plot2axes() method to see if you’re sampling what you want.

Hi James,

The goal is simply to extract a ROI (as an array) of the same size and location in four images, find the mean value of each ROI, then calculate the mean of of the 4 ROIs and compare each ROI mean to the overall mean value and calculate the percentage difference.

The pylinac.core.roi.RectangleROI has the width and height parameters which I take to mean x and y dimensions of the ROI, is this correct? Phantom center I pass from phantom_center = core.image.DicomImage(‘dosim0.dcm’).cax which also returns x and y coords.

I understand the ROI returned by RectangleROI will be a numpy array, so as you say will have row and col indexing, which is why I would expect when it extracts the ROI from the image (which is also loaded as an array) the pixel_array method would look like this:

self._array[self.bl_corner.y:self.tr_corner.y, self.bl_corner.x:self.tr_corner.x]

As the y coords will be acting on the rows and x coords on the columns. But instead it’s the opposite way round in the source:

self._array[self.bl_corner.x:self.tr_corner.x, self.bl_corner.y:self.tr_corner.y]

With the x coords being used to extract the rows of the array and the y coords being used to extract the columns, which I don’t think is correct? I overcome this issue at the moment by rotating the image 90 degrees after determining the phantom center and before passing it to pylinac.core.roi.RectangleROI. Which then gives me the correct extracted ROI (which I check with the mean pixel value which I know the answer to by manually performing the test in ImageJ).

Does that clarify at all?



I gotcha now. Probably human error there :wink: You could probably swap the x,y in the source code instead of rotating the image, but whatever works. I will file an issue for it. Thanks!

Thanks James!

I have another little query, this time about the picket fence class, specifically the dist2cax method. Now this may be me being pedantic but the method states it calculates “the distance from the CAX to the picket in mm”. In my mind that means if the CAX was at x=0 and the picket at x=+10, the distance would be reported as +10 mm (i.e. I need to add 10 mm to the CAX x coord to arrive at the picket x coord). Instead the method produces the opposite result i.e. -10 mm:

return (getattr(self.image.center, axis) - getattr(p1, axis)) * self.settings.mmpd

To me this is in fact the distance from the picket to CAX, it’s just a question of the sign. Would you agree or am I understanding this incorrectly?

I came across this when trying to match up the offsets calculated by pylinac compared to the dicom picket positions and everything was the opposite sign from what I was expecting (as there is a slight asymmetry in the planned Varian picket fence positions which highlighted the issue).

This wouldn’t surprise me. While the end results for all my test images are fine, there are likely some of these Easter eggs floating around. Would it be more helpful to adjust the docstring, or the return value?

I don’t really mind to be honest, changing the return value makes it easier to compare to the expected image in my mind, but I would have thought changing the docstring would be easier! I leave it to whatever you feel is best.