Skip to content
AI360Xpert
Beta

Voxels for 3D Shape Grids

Voxels put 3D shapes on a fixed grid so 3D convolutions can run, at the cost of memory that grows cubically.

Voxelization sorts nearby points into shared grid cells, so a 64-cubed grid holds 262,144 cells while a street scan fills under 2 percent of them.
Voxelization sorts nearby points into shared grid cells, so a 64-cubed grid holds 262,144 cells while a street scan fills under 2 percent of them.

Why Does This Exist?

3D sensors hand you the wrong data structure. A LiDAR sweep returns an unordered cloud of points, and a mesh is a tangle of vertices and faces, but a 3D convolution expects a plain array it can slide a kernel across. Voxels fix the mismatch by snapping space itself onto a fixed 3D grid: each cell records whether anything is there, and suddenly every 2D trick, convolution, pooling, downsampling, works in three dimensions.

The price is cubic growth. Halving the cell size multiplies the cell count by eight, so a grid fine enough to keep thin poles quickly outgrows GPU memory. This page covers voxelization, the sizing arithmetic, and the dense-versus-sparse decision that keeps voxels affordable. Raw unordered points without any grid belong to point cloud processing, and the detector that made sparse voxels famous lives under 3D object detection.

Think of It Like This

Ice cube trays for marbles

Pouring marbles into an ice cube tray sorts them into slots you can count row by row. Tiny slots keep every marble separate but you need a freezer full of trays; big slots save trays but jam two marbles into one slot and you lose track of individuals.

Voxel size is slot size and memory is freezer space. The analogy stops at emptiness: real ice trays come back full, while a voxel grid of a street comes back almost entirely empty air, which is why sparse storage matters.

How It Actually Works

From points to cells

Voxelization maps a point (x,y,z)(x, y, z) to integer indices (⌊x/v⌋,⌊y/v⌋,⌊z/v⌋)(\lfloor x/v \rfloor, \lfloor y/v \rfloor, \lfloor z/v \rfloor), where vv is the voxel size, the edge length of one cubic cell. Every point landing in the same cell pools into one feature, usually by averaging or max-pooling, so the cloud becomes a regular array of shape (L/v)3(L/v)^3 for a cubic scene of side LL. Dense memory follows M=(L/v)3×bM = (L/v)^3 \times b bytes with bb bytes per cell, and dense 3D convolution costs grow with the same cube.

Worked sizing example

Take a street patch with side L=3.2L = 3.2 m and cells of v=0.05v = 0.05 m. Cells per side are 3.2/0.05=643.2 / 0.05 = 64, so the grid holds 643=262,14464^3 = 262{,}144 cells. At b=4b = 4 bytes each, dense storage is 262,144×4=1,048,576262{,}144 \times 4 = 1{,}048{,}576 bytes, exactly 1.01.0 MiB. A real scan fills only about 5,0005{,}000 of those cells, so occupancy is 5,000/262,144≈1.95{,}000 / 262{,}144 \approx 1.9 percent. Halving vv to 0.0250.025 m would octuple the grid to 2,097,1522{,}097{,}152 cells and 8.08.0 MiB, which is the cubic tax made concrete.

Dense grids versus sparse lists

Below a few percent occupancy, storing the full cube wastes both memory and multiply-adds on empty air. Sparse pipelines store only occupied cells as coordinate lists and convolve only where data exists, which is exactly what detectors like SECOND exploit on LiDAR. The rule is simple: dense while occupancy is high (medical volumes, occupancy grids), sparse once air dominates (street scenes, rooms).

Code

Voxelize three points at v=0.05v = 0.05 m and count occupancy:

pts = [(0.12, 0.03, 1.52), (0.14, 0.04, 1.53), (2.87, 0.11, 0.52)]v = 0.05
vox = {}for p in pts:    key = (int(p[0] // v), int(p[1] // v), int(p[2] // v))    vox.setdefault(key, []).append(p)
occ = {k: len(c) for k, c in sorted(vox.items())}print(occ, "occupied:", len(vox))# -> {(2, 0, 30): 2, (57, 2, 10): 1} occupied: 2

The first two points pool into cell (2,0,30)(2, 0, 30), the third lands alone in (57,2,10)(57, 2, 10), and the scene that spans a 64364^3 index range occupies just 22 cells.

Watch Out For

Voxel size erasing thin structures

A 0.100.10 m voxel merges a 0.030.03 m pole with surrounding air and the pole vanishes from every later layer. The symptom is systematic misses on poles, limbs, and wires while bulky objects detect fine. Size vv from the thinnest must-keep structure first, then fit memory around that choice.

Convolving densely over empty air

Running dense 3D convolution on a grid that is 9898 percent empty spends nearly all its multiply-adds on zeros and exhausts memory past 1283128^3 on a single GPU. The symptom is out-of-memory errors on scenes that sparse baselines handle easily. Check occupancy first, and switch to sparse storage and sparse convolution whenever air dominates.

The Quick Version

  • Voxels snap 3D points onto a regular grid so 3D convolution can run unchanged.
  • A 64364^3 grid holds 262,144262{,}144 cells, exactly 1.01.0 MiB at 4 bytes per cell.
  • Street scans fill under 22 percent of cells, so sparse storage keeps the occupied few thousand.
  • Halving voxel size octuples memory, so size vv from the thinnest structure first.
  • Past 1283128^3 dense on one GPU, switch to sparse convolutions, points, or fields.