Unble to understand some lines in dlwpt book

username: Sokhib_Tukhtaev
cooked: <p>I have been following the “Deep Learning with Pytorch” book to learn pytorch, in Chapter 10 of part 2, there is <em>dsets.py</em> file and it contains the following code:</p>
<pre><code class="lang-auto">    def __init__(self, series_uid):
        mhd_path = glob.glob(
            'data-unversioned/part2/luna/subset*/{}.mhd'.format(series_uid)
        )[0]

        ct_mhd = sitk.ReadImage(mhd_path)
        ct_a = np.array(sitk.GetArrayFromImage(ct_mhd), dtype=np.float32)

        ct_a.clip(-1000, 1000, ct_a)

        self.series_uid = series_uid
        self.hu_a = ct_a

        self.origin_xyz = XyzTuple(*ct_mhd.GetOrigin())
        self.vxSize_xyz = XyzTuple(*ct_mhd.GetSpacing())
        self.direction_a = np.array(ct_mhd.GetDirection()).reshape(3, 3)
</code></pre>
<p>I understood all but the last 3 lines where <code>self.origin_xyz</code>, <code>self.vxSize_xyz</code> and <code>self.direction_a</code> got assigned. <code>XyzTuple</code> is <code>XyzTuple = collections.namedtuple('XyzTuple', ['x', 'y', 'z'])</code> (in <em>util.py</em>)</p>
<p>I am having a difficulty to understand what those <code>GetOrigin()</code>, <code>GetSpacing()</code> and <code>GetDirection()</code> mean. Also what is the role of <code>*</code> in front of <code>ct_mhd</code>?</p>
<p>Hope one can explain or refer to some other resources.</p>

username: ptrblck
cooked: <p>Based on the code snippet it seems that CT images are loaded (<code>hu_a</code> should refer to Hounsfield units).<br>
<code>origin_xyz</code> would refer to the origin of the Cartesian coordinate system, <code>vxSize_xyz</code> to the voxel size in each direction, and <code>direction_a</code> should be an axis vector.</p>
<p>The asterisk (<code>*</code>) unpacks the values as described e.g. <a href="https://towardsdatascience.com/unpacking-operators-in-python-306ae44cd480">here</a>.</p>
<p>CC <a class="mention" href="/u/tom">@tom</a>, who is one of the authors of this book to correct me.</p>

username: tom
cooked: <p>Yes, as <a class="mention" href="/u/ptrblck">@ptrblck</a> said, these are the coordinate transformation, and the <code>*</code> is just Python to separate a tuple (returned by GetOrigin/GetSpacing) into separate arguments.</p>
<p>The easiest way to look at the transformation is irc2xyz (i.e. voxel indices to physical coordinates in the patient):<br>
The key here is</p>
<pre><code class="lang-auto">coords_xyz = (direction_a @ (cri_a * vxSize_a)) + origin_a
</code></pre>
<p>And so starting out with the pixel indices <code>cri_a</code>:</p>
<ul>
<li>The triple <code>vx_Size_a</code> is the size fo a single voxel, i.e. two voxels are exactly that many mm apart.<br>
So <code>cri_a * vxSize_a</code> has a coordinates of the right size.</li>
<li>The matrix <code>direction_a</code> is an affine transformation, typically a orthogonal one, it typically be the identity or a mirroring of the coordingates (-1 on the diagonal) or a rotation (e.g., but not necessarily by 90° to exchange some axes). After this, you have a coordinate system that the directions of figure 10.6.</li>
<li>The origin moves where 0 is. I’m never 100% sure where it should be, but the software of the ct scanner gives it to us and we need it to match against the physical coordinates in the pixels.<br>
Then you have patient coordinates. These are used e.g. for the annotations.</li>
</ul>
<p>The other direction is the algebraic inverse of this.<br>
This can be more than just a bit messy but so not all patients lie perfectly axis-parallel in the thing and so you have all sorts of weird effects. It is a wonder that we even get a common file format for all these machines across the world.<br>
(And I’ve personally sat next to Luca when we converted some even more arcane format in some other project, so the LUNA dataset is actually quite moderate.)</p>
<p>Best regards</p>
<p>Thomas</p>
<p>P.S.: As an anecdote: Actually you can get by with only two very simple matrices for the direction in the LUNA dataset and we had the transformations hardcoded at first, but then we thought it might be better to code this up in general to let you use it with other data if you want (also more maths less magic <img src="https://discuss.pytorch.org/images/emoji/apple/wink.png?v=9" title=":wink:" class="emoji" alt=":wink:"> ).</p>

username: step
cooked: <p><a class="mention" href="/u/tom">@tom</a> Hey guys,</p>
<p>Thank you very much for this wonderful book!</p>
<p>Could you please explain these lines in util.py:</p>
<pre><code class="lang-auto">def irc2xyz(coord_irc, origin_xyz, vxSize_xyz, direction_a):
    cri_a = np.array(coord_irc)[::-1]
</code></pre>
<p>cri_a should have dimensions c and i swapped compared to coord_irc, right? But  does [::-1] really swap dimensions? Or is it just reversing data along the first dimension?</p>

username: tom
cooked: <p>Hi, thank you!</p>
<p>The thing is that this is for a single tuple of irc coordinates cast to a 3-element array and then flipped to cri.<br>
In general, flipping would be the transformation between irc and cri, but if you had a batch, it would be more likely in the last dimension. (This is - in an abstract way - similar to BGR-&gt;RGB, but ignore this if it adds more confusion than it helps.)</p>
<p>If I had a tensor that was in IRC “data layout” and wanted to convert it to CRI data layout (similar to channel-first CHW vs. channel-last HWC layout for images), then I would need to use <code>.permute(2, 1, 0)</code>, but this is not the case here because we are talking about indices into tensors.</p>
<p>Best regards</p>
<p>Thomas</p>

username: step
cooked: <p>Hi Thomas,</p>
<p>Got it, this function is designed to work with a single point. So the first line just turns tuple of numbers (I,R,C) into 3-element array [C, R, I].</p>
<p>Thank you very much for quick response and detailed explanation!</p>