3D visionHow-to

From 2D image slices to 3D reconstruction: the laser sweep method

Convert 2D image slices to a 3D reconstruction of a turned part: set mm per pixel, add each frame's rotation angle, stack profiles into a point cloud.

LumaSpec Vision12 min read Share on LinkedIn

On this page 10 sections
  1. 01Every frame is one profile at one angle
  2. 02What you need on the bench
  3. 03Calibrate three numbers first
  4. 04Capture the slices and stack the profiles
  5. 05Worked example: a 40 mm steel ball at 5° steps
  6. 06Checks that prove the cloud is right
  7. 07Four mistakes that bend the result
  8. 08When an integrated profiler is the better tool
  9. 09Order checklist
  10. 10Questions

You have a folder of grayscale frames, each one showing where a line laser touched a rotating part, and you need a 3D model out of them. The frames will not get there by being stacked: each one is a single profile that needs a millimetre scale and its stage angle before it means anything in 3D. This guide takes you through the build: calibrating the pixel scale on the bench, finding the rotation axis in the image, extracting the laser curve from each frame, and assembling every profile into one measured point cloud. A 40 mm steel ball on a rotary stage serves as the worked example, with every calculation shown. The closing sections compare the DIY sweep against an integrated 3D profiler and end with an order checklist.

Every frame is one profile at one angle

A grayscale frame of a laser line is a picture of a curve, not a sample of a volume. It becomes geometry when you do two things: give every pixel on the curve a millimetre coordinate — radius from the rotation axis, height along it — and record the stage angle at which the frame was captured. Do both for every frame and the series assembles into a point cloud in cylindrical coordinates: one meridian curve per frame, swept around the axis.

Skip the calibration and concatenate the frames along a third axis, and you get an image stack: brightness values in a depth direction that no meshing or measuring tool can read as a surface. The stack plays back as flicker; the point cloud measures. The difference is two numbers from the bench and about thirty lines of code, and the rest of this guide gives you both. If your rig feeds a profiler rather than a turntable, the per-frame job is different — How to create a 2D image from laser triangulation covers that direction.

What you need on the bench

  • A mono camera with enough pixels across the part — the scale maths in the next section tells you how many. Mono suits a laser line: the light has no colour, so colour filters only cost signal. Start from the area scan cameras range.
  • A line laser module on a rigid bracket, its fan aligned so the plane of light contains the rotation axis.
  • A rotary stage with indexed steps. A 5.0° index, 72 steps per revolution, is a comfortable starting point; the worked example shows when to go finer.
  • A steel ruler or two gauge blocks for the scale shot, and a pin for the axis shot.
  • Python with NumPy, or any array language you already use.

For the capture camera, choose mono and give yourself pixel headroom: a 20 MP USB3 camera such as the MV-CS200-10UM lets you crop the field and still keep thousands of pixels across the part:

Area scan camerasHikrobot MV-CS200-10UM 20 MP Area Scan Camera, USB3.0, IMX183, 2nd Gen., MonoMV-CS200-10UM
Sensor format
1"
Lens mount
C-mount
Interface
USB3
Resolution
16-25 MP
$759.00View partBuild a system with it

If the part spins continuously instead of stepping, use a global shutter: it exposes every row at the same instant, so a moving line cannot skew. The 2.5 MP GigE MV-CU020-90GC is the class of camera for that job:

Area scan camerasHikrobot MV-CU020-90GC 2.5 MP Area Scan Camera, GigE, GMAX4002, ColorMV-CU020-90GC
Sensor format
1/1.7"
Lens mount
C-mount
Interface
GigE
Resolution
Up to 3 MP
$309.00View partBuild a system with it

Calibrate three numbers first

Three numbers turn pictures into millimetres, and all three come from two five-minute shots.

The scale s is millimetres per pixel at the laser plane. Lay a steel ruler in the plane the laser paints, shoot it, and count pixels between two graduations: a 20.0 mm span over 816 px gives s = 20.0 / 816 = 0.0245 mm per pixel. The equivalent route, field of view divided by sensor width (60 mm / 2448 px), must land on the same value within 1%. Measure rather than compute from lens tables: magnification scales with working distance, so a nominal 60 mm field that is really 63 mm makes a 40 mm ball read 38 mm — the same 5%, straight into your report.

How sensor width, working distance and field of view set the focal length Rays from the edges of the sensor pass through the lens and spread to the edges of the field of view. The ratio of sensor width to field of view is the magnification m.CameraField of view (FOV)Working distance (WD)≈ fsensor width slensm = s / FOVf = WD × m / (1 + m)
How sensor width, working distance and field of view set the focal length. Rays from the edges of the sensor pass through the lens and spread to the edges of the field of view. The ratio of sensor width to field of view is the magnification m.

The axis column u_axis and axis row v_axis locate the rotation axis in the image. Pull the part, plant a pointed pin on the axis, and shoot the stage at 0° and at 180°: the pin tip appears at columns u1 and u2, and u_axis = (u1 + u2) / 2. The same pair of shots gives v_axis from the pin tip's row. Two frames, one subtraction, and every later wobble diagnosis starts from a known good axis.

Work the pixel count backwards from the detail you owe someone. Four pixels across the smallest feature is a workable floor: for 0.1 mm features that means 0.025 mm per pixel, and across a 40 mm part it means 1,600 px — any current camera clears that. The calculator below runs the conversion from field of view and detail size to the pixel count you need:

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

Capture the slices and stack the profiles

  1. Square the rig. Align the laser plane so it contains the rotation axis, and point the camera square at that plane. The meridian curve then images without foreshortening, and one scale covers radius and height.
  2. Calibrate the three numbers: scale from the ruler shot, axis column and row from the pin-pair shot. Five minutes that decide whether the cloud comes out round.
  3. Step and settle: 5.0° per index with a 100 ms settle gives 72 frames per revolution, and the stage stands still during every exposure.
  4. Set exposure on the line, not the part: raise it until the line's peak sits near 80% of full scale, then back off a touch. A saturated line blooms several pixels wide and drags the centroid outward. Dim the room; a bandpass filter matched to the laser wavelength helps under shop lights.
  5. Extract the curve from every frame: threshold, then take the intensity-weighted centroid of the line in each column. When the line breaks or doubles, the laser-line extraction guide has the fixes.
  6. Convert and place: pixels to radius and height with the formulas below, radius and angle to x and y, one frame at a time. Save the assembled points as PLY or CSV.
  7. Rasterize if the downstream tool wants a grid: bin angle and height, write a cylindrical height map r(θ, z), or fill a voxel array. Empty bins between meridians are normal at a 5° step; interpolation or a finer step closes them.
formula
s = FOV / W_px                  mm per pixel at the laser plane
r = (u - u_axis) * s            radius from the rotation axis, mm
z = (v_axis - v) * s            height along the axis, mm
x = r * cos(theta_k)            frame k captured at stage angle theta_k
y = r * sin(theta_k)
gap = r * step_rad              spacing between neighbouring meridians, mm
N = 360 / step_deg              frames per full revolution
import numpy as np

# calibration inputs, measured on the rig
MM_PER_PIXEL = 0.0245   # float, mm per pixel: 60.0 mm FOV / 2448 px
AXIS_COLUMN = 1224      # int, rotation axis column from the pin-pair shot
AXIS_ROW = 1000         # int, rotation axis row in the image
STEP_DEG = 5.0          # float, stage angle between frames
N_FRAMES = 72           # int, frames per revolution: 360 / 5.0

def frame_to_points(us, vs, theta_deg):
    # us, vs: int arrays of pixel columns/rows on the extracted laser curve
    theta = np.deg2rad(theta_deg)
    r_mm = (us - AXIS_COLUMN) * MM_PER_PIXEL
    z_mm = (AXIS_ROW - vs) * MM_PER_PIXEL
    return np.column_stack((r_mm * np.cos(theta),
                            r_mm * np.sin(theta),
                            z_mm))

# test: a synthetic meridian of the 40 mm ball must land 20.0 mm from the axis
phi = np.linspace(0.0, np.pi, 181)
us = AXIS_COLUMN + (816.0 * np.sin(phi)).astype(int)   # 816 px x 0.0245 = 19.99 mm
vs = AXIS_ROW - (816.0 * np.cos(phi)).astype(int)
cloud = np.vstack([frame_to_points(us, vs, k * STEP_DEG) for k in range(N_FRAMES)])
radius = np.sqrt((cloud ** 2).sum(axis=1))
assert abs(radius.mean() - 20.0) < 0.05   # passes: mean radius is 19.99 mm

Run the test: the mean radius prints 19.99 mm, because 816 px × 0.0245 mm/px = 19.99 mm, and the assertion holds against the 20.0 mm caliper radius. That one check proves scale, axis and angle handling agree before a real part enters the story.

A continuous sweep works too, and the blur maths says how fast. At 1 rev/min, 6° per second, a 1 ms exposure lets the stage travel 0.006°, which is 0.0021 mm of arc at a 20 mm radius — 0.09 px at the 0.0245 mm scale. Rolling-shutter readout in the 10 ms class adds about 0.9 px of skew across the frame at that speed. Both grow in proportion to speed, and the scan-spacing arithmetic that keeps laser triangulation clouds unstretched is the same calculation applied to linear motion.

Worked example: a 40 mm steel ball at 5° steps

Rig numbers: 60 mm field of view across a 2448 px sensor, ruler-checked scale s = 60 / 2448 = 0.0245 mm per pixel, axis at pixel (1224, 1000), 72 frames at 5.0°. The 40 mm outline spans 40 / 0.0245 = 1633 px, which leaves about 400 px of margin on each side of the 2448 px frame.

One point, end to end. Frame 6 has the stage at 30.0°, and the extracted curve passes through pixel (1800, 422):

  • Radius: (1800 - 1224) × 0.0245 = 576 × 0.0245 = 14.11 mm
  • Height: (1000 - 422) × 0.0245 = 578 × 0.0245 = 14.16 mm
  • Place at angle: x = 14.11 × cos 30° = 12.2 mm, y = 14.11 × sin 30° = 7.1 mm, z = 14.2 mm
  • Sanity: √(12.2² + 7.1² + 14.2²) = √400.9 = 20.0 mm — the caliper radius, as it must be on a sphere.

Slice spacing decides whether the cloud answers questions. Neighbouring meridians at the equator sit r × Δθ apart: 20 mm × 0.0873 rad = 1.75 mm at a 5.0° step. That shows the shape and misses a 0.25 mm defect. For 0.25 mm gaps, step = 0.25 / 20 = 0.0125 rad = 0.72°, which is 360 / 0.72 = 500 frames — eight minutes at one frame per second, against under a minute and a half for the coarse pass. For how sampling choices shape a cloud, see 3D point cloud resolution settings.

Decision: prove the pipeline at 5.0°, then run the measurement pass at 0.72° only if the spec owes someone 0.25 mm.

Checks that prove the cloud is right

The synthetic test above is the first gate. Then put hands on it:

  • Diameter: fit a circle to the reconstructed equator and compare with calipers — it should land within 0.1 mm of 40.0 mm. Anything worse points at scale or axis.
  • Volume, if you mesh the cloud: a sphere of 20 mm radius holds 4/3 × π × 20³ = 33,510 mm³ = 33.5 cm³, and an integrated mesh volume within 1% (33.2 to 33.8 cm³) says the sweep is closed and scaled.
  • Banding: one dark band per revolution means the axis column is off; helical banding means angle jitter; a cloud that tilts as a whole usually means the camera is not square to the plane — the tilted point cloud calibration checks walk that diagnosis for profilers.

Once the cloud passes, measuring surface height difference from a 3D point cloud is where the numbers come from.

Four mistakes that bend the result

  1. Stacking frames before calibrating. The stack looks plausible in a viewer and measures as nothing. Run the two bench shots first.
  2. Scale from lens tables. A 5% working-distance error becomes a 5% size error — 40 mm reads 38 mm or 42 mm — and no post-processing recovers it.
  3. Axis off by a few pixels. 10 px at an 816 px radius wobbles the surface 10 × 0.0245 = 0.245 mm once per revolution: a banana the caliper will find.
  4. Angle bookkeeping. Stage backlash shows as a zigzag when steps alternate approach direction — 0.2° of backlash is 0.0035 rad × 20 mm = 0.07 mm of sawtooth. Approach every step from the same side and re-home each revolution.

When an integrated profiler is the better tool

The DIY sweep earns its keep for one-off shapes, oversized parts and rigs you already own. A production cell is a different trade: you would be rebuilding, frame by frame, what profiler vendors ship calibrated. An integrated 3D laser profile sensor such as the MIS-DP3850-01P outputs each frame as a finished height line in millimetres, factory-calibrated, with your motion stage supplying the second axis:

3D sensorsHikrobot MIS-DP3850-01P 3D laser profile sensorMIS-DP3850-01P
Type
Laser profiler
$12,379.00 · quotedView and quote
Approach What it outputs You calibrate Fits when
Calibrated sweep (this guide) Point cloud in mm, radius and angle per point Scale, axis, angles One-off parts, big diameters, existing stages
Integrated 3D laser profiler Factory-calibrated height line per frame Nothing but mounting Production repeatability, higher line rates
Raw grayscale stack Brightness values, no units Not possible Nothing — it is not 3D

For mounting geometry and field placement, the 3D camera working distance guide transfers directly to profilers, and surface defect detection with a 3D laser line profiler shows what finished height lines are used for. Compare the range in 3D sensors or the camera finder.

Order checklist

  • Field of view at the laser plane at least 1.5× the part diameter — 60 mm for the 40 mm ball — so the curve keeps margin.
  • Scale shot with a ruler in the plane, not lens tables: working distance drifts, and 5% is 2 mm on this part.
  • Pin-pair axis shot at 0° and 180°: the axis column is the top source of banded clouds.
  • Step angle from the gap you owe: step = gap / radius at the largest radius that matters.
  • Exposure peaking near 80% of full scale: saturated lines bias the centroid outward.
  • Mono sensor: the line carries no colour, so colour filters only cost signal.
  • A self-test before trust: reconstructed equator within 0.1 mm of the calipers, mesh volume within 1% for a closed solid.

Size the camera and lens around your part's numbers in the camera configurator, or send the rig sketch through a quote request and we will spec the set. The 3D topic page gathers the rest of the reconstruction guides.

Questions

How many frames does a full 360° sweep need?

Use frames = 360 / step, with the step set by the gap you accept at the largest radius: step = gap / radius in radians. For the 20 mm ball and a 0.25 mm gap, step = 0.25 / 20 = 0.0125 rad = 0.72°, so 500 frames. At 5° steps you get 72 frames and 1.75 mm gaps at the equator. Meridians converge towards the poles, so the equator is the worst case — budget for the largest radius that matters.

Can the grayscale frames just be stacked without calibration?

No. Stacking raw grayscale frames produces a 3D array of brightness values: playback flickers through the slices, and meshing or measurement tools find no surface because nothing carries millimetre units or a camera position. Convert each frame to profile coordinates first — radius and height from the axis, in millimetres — and attach the stage angle; only then does stacking produce geometry.

Why does a reconstruction come out banded or banana-shaped?

Usually the axis column is wrong or the stage angle jitters. A 10 px axis error at an 816 px radius wobbles every profile by 10 × 0.0245 = 0.245 mm once per revolution, which shows up as one low band per turn. Angle jitter draws helical banding instead. Re-find the axis with the 180° pin-pair shot, and always approach each step from the same side to cancel stage backlash.

Does this method work on a shiny polished part?

Partly. On polished steel the beam reflects away from the camera, so expect gaps where the line disappears instead of curves. Keep the exposure low, take the centroid of the strongest return, and drop frames where the line breaks; average neighbouring meridians to fill small holes. For shiny production parts, a 3D laser profiler with per-frame exposure control is the dependable choice.