# How can this resampling code be made faster?

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?

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

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.

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

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

• 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. Sep 11, 2015 at 17:34
• 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. Sep 11, 2015 at 20:02
• @george2079 Or DeveloperPartitionMap[Total@*Flatten, sbt, {8,8}]` (syntactically nicer, but still not fast) Sep 12, 2015 at 0:18
• @george2079 I think it might be unpacking the array? Sep 12, 2015 at 8:33