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$
2
  • $\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$ Commented Sep 12, 2015 at 3:44
  • $\begingroup$ @BenJackson, I'm processing a genuine image. The random one is just an example. $\endgroup$ Commented Sep 12, 2015 at 15:09

2 Answers 2

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$
3
  • $\begingroup$ Well done! I also see a 20% speedup with your technique. $\endgroup$ Commented Sep 11, 2015 at 17:37
  • $\begingroup$ I think the Parallelization -> True is superfluous? $\endgroup$ Commented Sep 11, 2015 at 18:34
  • $\begingroup$ @blochwave, Yes, I don't think the Parallelization option adds anything. $\endgroup$
    – Asim
    Commented Sep 11, 2015 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$
4
  • 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$ Commented Sep 11, 2015 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
    Commented Sep 11, 2015 at 20:02
  • $\begingroup$ @george2079 Or Developer`PartitionMap[Total@*Flatten, sbt, {8,8}] (syntactically nicer, but still not fast) $\endgroup$
    – C. E.
    Commented Sep 12, 2015 at 0:18
  • $\begingroup$ @george2079 I think it might be unpacking the array? $\endgroup$ Commented Sep 12, 2015 at 8:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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