Advanced Indexing (Medoid)

username: Assefa_Seyoum
cooked: <p>I have a tensor of shape <code>torch.Size([400, 32, 32])</code></p>
<p>Each 32x32 patch contains only ones and zeros.</p>
<p>I want to return a geometric center / medoid of foreground pixels (1s).</p>
<p>The shape of the return value would be <code>[400, 2]</code>.</p>
<p>Example:</p>
<p><div class="lightbox-wrapper"><a class="lightbox" href="https://discuss.pytorch.org/uploads/default/original/3X/6/e/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad.png" data-download-href="https://discuss.pytorch.org/uploads/default/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad" title="image"><img src="https://discuss.pytorch.org/uploads/default/optimized/3X/6/e/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad_2_690x427.png" alt="image" data-base62-sha1="fHGwC2o2H3oFs3LS9YePf5b1lJH" width="690" height="427" srcset="https://discuss.pytorch.org/uploads/default/optimized/3X/6/e/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad_2_690x427.png, https://discuss.pytorch.org/uploads/default/original/3X/6/e/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad.png 1.5x, https://discuss.pytorch.org/uploads/default/original/3X/6/e/6e10ce0c2ebdcdf9fc46705d77d414948165f9ad.png 2x" data-dominant-color="F2F0F2"><div class="meta"><svg class="fa d-icon d-icon-far-image svg-icon" aria-hidden="true"><use href="#far-image"></use></svg><span class="filename">image</span><span class="informations">863×535 88.8 KB</span><svg class="fa d-icon d-icon-discourse-expand svg-icon" aria-hidden="true"><use href="#discourse-expand"></use></svg></div></a></div></p>
<p>The above [4, 4] patch returns [1,2] as the coordinate.</p>
<p>How to do that without a loop?</p>
<p>Thank you.</p>

username: KFrank
cooked: <p>Hi Assefa!</p>
<aside class="quote no-group" data-username="Assefa_Seyoum" data-post="1" data-topic="159611" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/assefa_seyoum/48/32226_2.png" class="avatar"> Assefa_Seyoum:</div>
<blockquote>
<p>I want to return a geometric center / medoid of foreground pixels (1s).</p>
</blockquote>
</aside>
<p>If the mean location of the foreground pixels would be adequate for your<br>
use case, you could do something like:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; import torch
&gt;&gt;&gt; torch.__version__
'1.12.0'
&gt;&gt;&gt; t = torch.tensor ([[0, 1, 1, 1], [0, 1, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1]])
&gt;&gt;&gt; t.nonzero().mean (dim = 0, dtype = torch.float).round().long()
tensor([1, 2])
</code></pre>
<aside class="quote no-group">
<blockquote>
<p>How to do that without a loop?</p>
</blockquote>
</aside>
<p>If the above doesn’t work for your use case, could you illustrate the specific<br>
computation you want with a self-contained, runnable script that uses loops,<br>
as necessary?</p>
<p>Best.</p>
<p>K. Frank</p>

username: Assefa_Seyoum
cooked: <p>Hello.</p>
<p>Your solution works for 2d data only.</p>
<p>My input’s shape is [N, 32, 32].</p>
<p>The solution with a loop would be something like,</p>
<pre><code class="lang-auto">t = torch.rand((400, 32, 32)) &gt; 0
mid = torch.stack([p.nonzero().mean (dim = 0, dtype = torch.float).round().long() for p in t])
mid.shape
</code></pre>
<p>Thank you.</p>

username: KFrank
cooked: <p>Hi Assefa!</p>
<aside class="quote no-group" data-username="Assefa_Seyoum" data-post="3" data-topic="159611" data-full="true">
<div class="title">
<div class="quote-controls"></div>
<img loading="lazy" alt="" width="24" height="24" src="https://discuss.pytorch.org/user_avatar/discuss.pytorch.org/assefa_seyoum/48/32226_2.png" class="avatar"> Assefa_Seyoum:</div>
<blockquote>
<p>The solution with a loop would be something like,</p>
<pre><code class="lang-auto">t = torch.rand((400, 32, 32)) &gt; 0
mid = torch.stack([p.nonzero().mean (dim = 0, dtype = torch.float).round().long() for p in t])
mid.shape
</code></pre>
</blockquote>
</aside>
<p>To do this on a “batch” basis, probably the simplest approach is to<br>
generate a tensor that hold the indices of all of the pixels in your 32x32<br>
slices and then compute the foreground-pixel weighted average of those<br>
indices:</p>
<pre><code class="lang-plaintext">&gt;&gt;&gt; import torch
&gt;&gt;&gt; print (torch.__version__)
1.12.0
&gt;&gt;&gt;
&gt;&gt;&gt; _ = torch.manual_seed (2022)
&gt;&gt;&gt;
&gt;&gt;&gt; t = torch.multinomial (torch.tensor ([249.0, 1.0]), 10 * 32 * 32, True).reshape (10, 32, 32)
&gt;&gt;&gt; t.shape
torch.Size([10, 32, 32])
&gt;&gt;&gt;
&gt;&gt;&gt; mid = torch.stack([p.nonzero().mean (dim = 0, dtype = torch.float).round().long() for p in t])
&gt;&gt;&gt;
&gt;&gt;&gt; indices = torch.stack ((torch.arange (32).expand (32, 32).T, torch.arange (32).expand (32, 32)), dim = 0)
&gt;&gt;&gt; midB = ((t.unsqueeze (1) * indices).sum (dim = (2, 3)) / t.sum (dim = (1, 2)).unsqueeze (1)).round().long()
&gt;&gt;&gt;
&gt;&gt;&gt; midB
tensor([[ 6, 18],
        [13, 17],
        [20, 18],
        [ 3,  6],
        [15, 15],
        [17, 11],
        [12, 24],
        [ 9, 20],
        [16, 17],
        [17, 17]])
&gt;&gt;&gt;
&gt;&gt;&gt; torch.equal (mid, midB)
True
</code></pre>
<p>Note, if a 32x32 slice happens to contain no foreground pixels, you will get<br>
<code>nan</code> (before converting to <code>long()</code>) for the mean foreground pixel location<br>
for that slice.  That’s probably as reasonable an “undefined” or  “special-case”<br>
value as any.</p>
<p>Best.</p>
<p>K. Frank</p>