Nodes/ComfyUI CV/cv2.rapid.drawWireframe
ComfyUI Node

cv2.rapid.drawWireframe

Project your mesh into the frame and see if the pose is a lie

By bmad4ever·Created 3 months ago·Updated 14 days ago· 1
cv2.rapid.drawWireframe
  • img
  • pts2d
  • tris
  • nparray
◄color(0, 0, 0, 0)►
◄type►
◄cullBackface►

This is the honest test of any pose estimate: draw the model's wireframe at that pose over the image. If the lines sit on the object, you're fine. If they're rotated 180°, sitting inside the silhouette, or scaled to twice the object's size, you know instantly, and you know roughly which of those things it is. It's faster than any numeric check and it can't be fooled by a nearly-right residual.

What it takes

  • img - the frame to draw on. In the pack's RAPID playground workflow, this is the same chain that feeds the search stages, so the debug overlays accumulate over one image. Use the original frame if you want a clean comparison.
  • pts2d - the projected 2D points. These are per-vertex pixel coordinates, so they come from a projection step: cv2.projectPoints with your rvec/tvec/K, or rapid.extractControlPoints' ctl2d if you only want the silhouette samples. This is a data array, not an image, so shape matters - the playground passes it through CV Reshape Array before it gets here.
  • tris - the triangle index list (an M×3 integer array). For a mesh you'd build these with CV Mesh From 3D Model, which turns a loaded model into vertices and triangle indices. Note the playground wires this from CV Parse Matrix, i.e. a literal edited on the node, which is what you do when the triangles are known in advance.
  • color - a cv2 Scalar as a string literal in OpenCV's BGR order: "(0, 255, 0)" green, "(0, 0, 255)" red, "255" white. Saturated colours read better over photographic detail.

Optional: type (a literal widget - blank means the OpenCV default; this is the line type, i.e. anti-aliased or not) and cullBackface (also a literal widget, blank for default - turn back-face culling on and hidden triangles stop being drawn, which makes a solid object's wireframe legible instead of a cloud of internal edges).

Output, and the type trap

One output: nparray. This node is not type-preserving, so a preview node will not render it until you add CV Array → Image. Same for its two siblings, rapid.drawSearchLines and rapid.drawCorrespondencies - build a debug lane with the conversions in place rather than wiring the raw output to a preview and wondering why nothing appears.

How it's used in practice

The pack's RAPID workflows are the reference: 41_rapid_tracking_playground.json draws the wireframe at the incoming pose, then refines, accumulating search lines and correspondences on top; 42_rapid_model_tracking.json does it over a clip. In a production-ish graph, the wireframe is the thing you render when a track fails:

  • Wireframe sits on the object, but the refinement drifts. The pose is a fine starting point and the search is the problem - too-long search lines, or cluttered background edges. Look at rapid.drawSearchLines.
  • Wireframe is rotated ~180° about the depth axis. This is the normals trap: a cloud whose normals came from a plane fit instead of the mesh's winding. The pack's docs are specific about it - use CV Mesh Vertex Normals, which takes the sign from the triangle winding, rather than CV Point Cloud Normals, when the cloud came from a mesh. A wrong-signed cloud inverts the PPF/RAPID pose.
  • Wireframe is the right shape at the wrong size. Usually an intrinsics/scale mismatch: K built for a different resolution than the frame you're drawing on.

Install

From ComfyUI CV (bmad4ever). Manager, search comfyui_cv, or:

cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"

Restart. Python ≥ 3.12 and a modern V3-API ComfyUI. Nothing to download - your mesh is your asset, and a .glb loaded through core's Load 3D or the pack's mesh nodes is enough.

Common issues

Nothing renders. Convert the NPARRAY with CV Array → Image.

OpenCV errors on the triangle array. tris must be integer M×3 with indices into pts2d's rows. Float or 1-indexed arrays are the usual culprits.

"The whole mesh is drawn, I can't see the object." That's what cullBackface is for; also consider CV Mesh Split Long Edges, which subdivides a mesh to a target edge length - the pack describes it as denser control points for RAPID at roughly half the triangles of uniform subdivision, which makes both the draw and the tracking cheaper.

Missing rapid nodes. Contrib-only module; a non-contrib OpenCV wheel hides it. tools/repair_opencv_contrib.py --check, then --apply.

Categoryimage/CV/low-level/rapid

Inputs (6)

NameTypeDefaultDescription
imgNPARRAY,IMAGE,MASK - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
pts2dNPARRAY,IMAGE,MASK - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
trisNPARRAY,IMAGE,MASK - - - Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size.
colorSTRING(0, 0, 0, 0) - - - cv2 Scalar as a literal, e.g. "(0, 255, 0)" (BGR) or "(0, 255, 0, 64)" (BGRA). A bare number broadcasts to every component, so "255" means (255, 255, 255, 255). Components past the target's channel count are ignored by OpenCV.
typeoptSTRING - - - Optional - leave blank to use the OpenCV default. Accepts a Python literal, e.g. 3, 1.5, true, or (3, 3).
cullBackfaceoptSTRING - - - Optional - leave blank to use the OpenCV default. Accepts a Python literal, e.g. 3, 1.5, true, or (3, 3).

Outputs (1)

NameTypeDescription
nparrayNPARRAY—