ImageToSketch

# opensource# software# tools
ImageToSketchMatthew Armstrong

Trace a photo, scan or drawing of a part into a clean, extrudable FreeCAD sketch: straight

Work in progress (v0.2.0-wip, built 2026-10-03 13:10). The tracing library is tested against
simulated images with known geometry. The FreeCAD side has not yet been run inside real
FreeCAD
- only against stand-in modules and the OpenCascade kernel. See
What has and has not been tested.

Trace a photo, scan or drawing of a part into a clean, extrudable FreeCAD sketch: straight
lines, arcs, circles and splines, with holes, at true scale.

before and after

Left: a simulated "bad photo" (shadow, uneven light, dust, scratches, pen marks, blur, JPEG).
Right: the verdicts. Green = traced as real geometry, amber = borderline (added as construction
geometry so you can accept or delete it).

What it does

  1. Normalises the picture: picks the colour channel where the part stands out most, works out whether the part is the dark or the light tone, and divides out uneven lighting.
  2. Proposes closed outlines from thresholds at several levels between the part's tone and the background's tone.
  3. Verifies every outline against the real edge: each point is probed across the outline for a sharp step. Smooth ramps (shadow edges, lighting falloff) are subtracted, so they neither count as evidence nor pull the position. The same probe gives the edge position to a few hundredths of a pixel.
  4. Scores and discards: confidence = share of the outline that sits on a sharp step. High = real geometry, middling = borderline, low = dropped. Dust-sized shapes, debris beside the part and ragged patches of glare inside it can only ever be borderline.
  5. Fits the fewest lines, arcs, circles and splines that stay within tolerance, bridges small bumps (dirt, burrs) on edges, and joins neighbours at exact intersections or tangent points, so corners are sharp again and outlines are closed.
  6. Builds the sketch in FreeCAD, with the picture underneath at true size so anything the tracer missed can be drawn over it with the normal Sketcher tools.

Scale comes from a printable marker sheet (also fixes perspective), from a known picture
width, from a known mm-per-pixel value, or from a plain square of known size in the picture.

marker sheet

Quick start (FreeCAD)

python3 install.py
Enter fullscreen mode Exit fullscreen mode

That copies the add-on into FreeCAD's user Mod folder (it finds FreeCAD 0.21, 1.0 and 1.1
folders on Linux, macOS and Windows) and installs OpenCV into the add-on's own _deps folder.
Restart FreeCAD, choose the ImageToSketch workbench, press Image to sketch....

  • If you skip the installer, copy this folder into Mod as ImageToSketch. The first time you open the dialog it offers to install OpenCV for you.
  • Linux with FreeCAD from apt: sudo apt install python3-opencv then python3 install.py --no-opencv also works.
  • python3 install.py --uninstall removes it again.

dialog

Using it

  1. Browse to the picture. A preview appears.
  2. Choose where scale comes from. For the marker sheet: Save printable marker sheet, print at 100 %, lay the part in the middle, photograph from straight above with all four markers visible. Measure the sheet's 100 mm line with a ruler; if your printer scaled the page, type the real length.
  3. Check the preview, adjust if needed, Create sketch, then Pad it.

Settings worth knowing:

Setting What it changes
Part tone Force "dark part" or "light part" if the automatic guess is wrong.
If two edges compete A hard-edged shadow (or a bevel) gives two sharp outlines. Keep the inner edge ignores the shadow; keep the outer edge is right for bevelled parts. The loser is still added as construction geometry.
Smallest real feature Holes smaller than this become construction geometry; bumps shorter than this on an edge are bridged. Automatic is about 0.7 % of the picture diagonal.
Fit tolerance How closely primitives must follow the edge. Automatic is 0.5 px, more if the picture is blurred.

Without FreeCAD

pip install opencv-python-headless numpy
python3 -m imagetosketch photo.jpg --markers --svg part.svg --dxf part.dxf --overlay check.png
python3 -m imagetosketch --make-sheet sheet.pdf --paper A4
python3 -m imagetosketch --help
Enter fullscreen mode Exit fullscreen mode

SVG and DXF are in millimetres and import into FreeCAD, Fusion 360, Inkscape and LibreCAD.
From Python: from imagetosketch import trace, Options.

Accuracy and cost (simulated images)

60 simulated images (12 kinds of damage x 5 random seeds), 420 true outlines, 1600 x 1200 px:

Found False Mean error Radius error Time
v0.1 macro (one threshold) 370 / 420 436 0.60 px 0.45 px 6 ms
Full detector bank with voting 411 / 420 364 0.59 px 0.45 px 206 ms
v0.2 (this) 419 / 420 9 0.05 px 0.03 px 130 ms

Times are one core of a 2.1 GHz Xeon. A 12-megapixel colour JPEG takes about 1.2 s and 160 MB of
memory (it is reduced to 2400 px first). Why this design won, and the full table, is in
docs/BENCHMARK.md.

On the simulated marker-sheet photo above (tilted camera, uneven light, noise) hole diameters
came out within 0.005 mm, fillet radii within 0.01 mm and straight-edge lengths within about
0.03 mm of the true sizes.

What has and has not been tested

Tested (44 automated tests, python3 -m pytest):

  • Tracing accuracy on simulated images whose true geometry is known exactly.
  • Marker-sheet scale and perspective on simulated tilted photos; the PDF sheet's physical size.
  • Sketch building and the dialog against stand-in FreeCAD modules (tests/fake_freecad), with the resulting geometry rebuilt in OpenCascade (FreeCAD's own geometry kernel), checked to be closed, and padded into a solid whose volume matches the true part to 0.005 % (clean image) and 0.06 % (the "bad photo" above).
  • OpenCV 4.6 (the version Ubuntu / Mint ship), 4.13 and 5.0; NumPy 1.26 and 2.x. Linux only.

Not tested:

  • Running inside real FreeCAD (any version). The FreeCAD calls are written from the documented API; expect to find and fix small things on first run.
  • Real photographs of real parts. Four stock photos were looked at by eye only.
  • A 7th-generation Core i3. Nothing here needs a GPU and memory use is far below 2 GB, but the timings above are from a faster machine.
  • Windows and macOS (installer paths come from documentation, not from a test).

Limits

  • A shadow nearly as dark as the part cannot be told from the part. Such outlines come out as borderline. Light from above, or diffuse light, avoids it.
  • Embossed, glossy or heavily textured surfaces produce clutter inside the part (mostly borderline). Matte parts on plain paper of a contrasting colour work best.
  • The marker sheet corrects scale and perspective in the plane of the paper. The top face of a thick part is nearer the camera and reads large by about height / camera distance (a 10 mm part from 300 mm: about 3 %). Shoot from further away, or scale the sketch afterwards.
  • Lens distortion is not corrected.
  • A part touching the edge of the picture is not traced.
  • A dark blob on a light part is indistinguishable from a hole.

How it relates to other projects

  • SimpleCAD (FreeCAD workbench): separate add-on; SimpleCAD can open this dialog with from imagetosketch import fc_gui; fc_gui.show().
  • STL2STEP: its trace / extrude path needs the same "outline to lines, arcs, splines" step. imagetosketch.fit.fit_loop has no FreeCAD or image dependency and can be reused there.

Layout

imagetosketch/      the library: detect, fit, pattern, pipeline, export, cli + FreeCAD front end
InitGui.py, Init.py, package.xml   FreeCAD add-on entry points
ImageToSketch.FCMacro              macro that opens the dialog
install.py                         one-step installer
tests/                             test suite, simulated scenes, stand-in FreeCAD modules
bench/                             the design comparison, re-runnable
docs/                              benchmark write-up, images, printable marker sheets
Enter fullscreen mode Exit fullscreen mode

The usual src/main.py layout is not used because FreeCAD needs InitGui.py at the top of the
add-on folder. Heavy lifting is OpenCV (C++); the Python is glue. The dialog is Qt because it
runs inside FreeCAD.