I need to repeatedly increment cuboidal chunks of a 3D image. The image is large - on the order of 1G elements.
The only way I know of doing it using built-in Image functions is:
accumulator = accumulator ~ ImageAdd ~ ImagePad[ImageConstant[1,size], padding]`
This of course constantly allocates the constant images and the padded outputs. Tens of thousands them, and at these sizes, it's slow.
So, instead, I can use a packed array to represent the image data, and add to that using Part
and AddTo
:
regions = { Splice[{1;;2, 3;;4, 5;;6}, Part], ... };
size = {1100, 1100, 1100};
accumulator = ConstantArray[0, size];
Assert[Developer`PackedArrayQ[accumulator]];
(* accumulator has a single reference here *)
Map[{region} |-> accumulator[[region]] += 1, regions];
result = Image3D[accumulator, "Bit16"];
Unfortunately, the AddTo
function doesn't seem to have a specialization that would not reallocate, in spite of the modified array having just one reference*. Judging by the system memory use, each AddTo
clones the image, modifies it, sets accumulator
to it, then deallocates accumulator
.
Is there some incantation that would do it without allocating memory?
* Bonus Question: Is there any way to inspect an object refcount in Mathematica?
The exact code I'm using is in a Module, and is:
gridCuboidImageData = {gcuboids} |->
Module[{gridSize, i, gcuboid, start, size, ex, ey, ez,
n = Length[gcuboids], imageData},
gridSize = Plus @@ gcuboids[[1]];
(* if compiled to C, replace with: imageData=PadLeft[{{{}}},
Reverse@gridSize] *)
imageData = ConstantArray[0, Reverse@gridSize];
Assert[Developer`PackedArrayQ[imageData]];
For[i = 1, i <= n, i++,
gcuboid = gcuboids[[i]];
size = gcuboid[[3]];
start = gcuboid[[1]];
start = {start[[1]], gridSize[[2]] - start[[2]] - size[[2]],
gridSize[[3]] - start[[3]] - size[[3]]};
ex = start[[1]] + size[[1]];
ey = start[[2]] + size[[2]];
ez = start[[3]] + size[[3]];
imageData[[1 + start[[3]] ;; ez, 1 + start[[2]] ;; ey,
1 + start[[1]] ;; ex]] += 1;
];
imageData
];
regions
? $\endgroup$Do[accumulator[[region]] + 1, {region, regions}]
the same problems? $\endgroup$regions
is a list of lists ofSpan
-s that get spliced into thePart[accumulator, <here>]
. I haven't tried theDo
form, but somehow doubt it will be different. TheMap
is not really changing anything. It is theaccumulator[[a;;b, c;;d, e;;f]] += 1
expression that isn't doing what I'd expect it to. $\endgroup$