7
$\begingroup$

Suppose I have a matrix:

sbt = RandomInteger[{0, 2^16 - 1}, {5202, 3465}];

It represents an image and I would like to do "pixel binning" on it, where I take non-overlapping n x n square submatrices and replace them with the total of each submatrix. A typical n is 8. When the image dimensions aren't divisible by n it's okay to discard the last few rows and columns.

Importantly, each resulting pixel is a total, not an average or a bilinear resampling.

My naive function to do this is:

imageBin[imagedata_, n_] := With[
  {dims = Reverse@Dimensions[imagedata]},
  Table[
   Total[Flatten[imagedata[[row ;; row + n - 1, col ;; col + n - 1]]]],
   {row, 1, dims[[2]] - n + 1, n},
   {col, 1, dims[[1]] - n + 1, n}
   ]
  ]

I have thousands of images to process this way, so I care about making the process fast.

Timing[Do[imageBin[sbt, 8];, {10}]]

gives about 3.4 seconds on my machine. Is there a way to make this faster?

$\endgroup$
  • $\begingroup$ Are you really processing a generated random image? You could directly compute a random result based on the expected value of n*RandomInteger... for each bin. $\endgroup$ – Ben Jackson Sep 12 '15 at 3:44
  • $\begingroup$ @BenJackson, I'm processing a genuine image. The random one is just an example. $\endgroup$ – ArgentoSapiens Sep 12 '15 at 15:09
6
$\begingroup$

You could compile the function. For example,

imageBinC = Compile[{{imagedata, _Integer, 2}, {n, _Integer}},
   With[{dims = Reverse@Dimensions[imagedata]},
    Table[
     Total[Flatten[
       imagedata[[row ;; row + n - 1, col ;; col + n - 1]]]], {row, 1,
       dims[[2]] - n + 1, n}, {col, 1, dims[[1]] - n + 1, n}
     ]
    ], CompilationTarget -> "C", Parallelization -> True, 
   RuntimeOptions -> "Speed"
   ];


In[11]:= AbsoluteTiming[Do[imageBinC[sbt, 8];, {10}]]

Out[11]= {2.01693, Null}

The uncompiled function takes 2.45955 seconds on my machine.

$\endgroup$
  • $\begingroup$ Well done! I also see a 20% speedup with your technique. $\endgroup$ – ArgentoSapiens Sep 11 '15 at 17:37
  • $\begingroup$ I think the Parallelization -> True is superfluous? $\endgroup$ – dr.blochwave Sep 11 '15 at 18:34
  • $\begingroup$ @blochwave, Yes, I don't think the Parallelization option adds anything. $\endgroup$ – Asim Sep 11 '15 at 20:38
5
$\begingroup$

you can define a function that downsamples in one dimension:

downsampleX = Total /@ Partition[#, 10] &;

Then call that once on your array and then on each row:

downsampleX /@ downsampleX[sbt];

Takes about 0.19 s on my PC

$\endgroup$
  • 1
    $\begingroup$ This seems like a great idea and I tried it myself, but it is slightly slower. Ten runs takes 3.5 s compared to the original 3.4 s. $\endgroup$ – ArgentoSapiens Sep 11 '15 at 17:34
  • 1
    $\begingroup$ Interesting, Partition can do two both dimensions at once, so you can do simply Map[Total@Flatten@# &, Partition[sbt, {8, 8}] , {2}]; However that turns out to be way slower than the other solutions. $\endgroup$ – george2079 Sep 11 '15 at 20:02
  • $\begingroup$ @george2079 Or Developer`PartitionMap[Total@*Flatten, sbt, {8,8}] (syntactically nicer, but still not fast) $\endgroup$ – C. E. Sep 12 '15 at 0:18
  • $\begingroup$ @george2079 I think it might be unpacking the array? $\endgroup$ – dr.blochwave Sep 12 '15 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.