Nodes/comfyui_cv/CV Preview 3D (Calibrated Camera)
ComfyUI Node

CV Preview 3D (Calibrated Camera)

A self-contained Three.js viewer (web/cv_preview3d.js) that renders a 3D model through the FULL OpenCV camera model from 'CV Camera Pose To 3D View': the projection matrix is built directly from fx/fy/cx/cy (off-axis principal point and anamorphic pixels included), the pose keeps the camera roll the core OrbitControls viewers drop, and lens distortion (_dist_coeffs: k1, k2, p1, p2, k3) is replicated with an overscanned render + per-pixel Brown-Conrady warp shader - something no 4x4 projection matrix can express. Wire bg_image with the calibration photo and the render composites over it in exact register (the AR overlay check). Toolbar: reset to the calibrated pose, toggle distortion / background / the board-plane grid; drag to orbit (orbiting drops roll until reset). GLB/GLTF only (Three.js GLTFLoader); the canvas is letterboxed to the calibration image's aspect so the overlay lines up. Without _intrinsics in camera_info (the bridge's degenerate-K fallback) it degrades to a plain fov/aspect perspective view, distortion off. The 'render' IMAGE output is the same composite produced server-side (software rasterizer + cv2.undistortPoints lens warp, no grid), so the calibrated view is also available as data for further processing.

By bmad4ever·Created 3 months ago·Updated 2 days ago· 0
CV Preview 3D (Calibrated Camera)
  • model_3d
  • camera_info
  • bg_image
  • scene_depth
  • render
  • silhouette
  • depth
  • occlusion
  • depth_metric
shadinglambert (headlight)
resolution_scale1.00
antialias2x supersample
object_transform
scene_depth_bias0.00
Categoryimage/CV/low-level

Inputs (9)

NameTypeDefaultDescription
model_3dFILE_3D_GLB,FILE_3D_GLTF,FILE_3D3D model from a 3D loader node (e.g. 'Load 3D (Advanced)'). GLB is the reliable format: a .gltf with external buffers/textures will not resolve them from the temp folder.
camera_infoLOAD3D_CAMERACamera dict from 'CV Camera Pose To 3D View'. The underscore extras (_intrinsics/_dist_coeffs/_image_size) unlock the faithful projection and the distortion pass; a plain LOAD3D_CAMERA dict still works as a simple perspective view.
bg_imageoptIMAGEBackdrop composited behind the render - use the very photo the pose was solved from to verify the virtual camera matches the real one (the model should sit on the photographed board).
shadingoptCOMBOlambert (headlight)Lighting of the server-side render. 'lambert (headlight)' shades faces by their angle to a camera-side light (shows the 3D form); 'unlit (raw colors)' outputs the texture/base colors untouched - no lighting baked into the pixels, best when the render is data for further compositing. The mask output is unaffected.
resolution_scaleoptFLOAT1.000.25–4Multiplies the OUTPUT resolution of render + mask (camera matrix scales with it, so the framing is identical - 2.0 renders the same view at twice the calibration image's width/height).
antialiasoptCOMBO2x supersampleAnti-aliasing of the server-side render: the scene is rasterized and lens-warped at 2x/4x the output size, then box-filtered down - smooths polygon and silhouette edges (the mask edge gets fractional values). 'off' renders 1:1 (fastest, hard edges).
object_transformoptSTRINGModel transform in Three.js world space, as JSON: {"position": [x, y, z], "quaternion": [x, y, z, w], "scale": [x, y, z]} (scale may be a single number; missing keys default to identity; empty = identity). The viewer's move/rotate/scale GIZMO writes this widget as you drag, so the next run renders the model where you left it - and the viewer's 'reset' button puts the model back on whatever this widget says, so a mis-drag costs one click once the text is right. Convert to an input to drive the placement from upstream instead (the gizmo then only previews): 'CV 3D Object Transform' writes this string from readable position/rotation/scale widgets - including a 'model_up' dropdown that stands a Y-up model on the target plane instead of you finding the quaternion - and emits the matching 4x4, so the mesh lane moves with the render. 'CV Parse Object Transform' reads a gizmo-authored string back into the graph.
scene_depthoptNPARRAYOptional HxW float32 depth map of the PHOTOGRAPHED scene - metric camera-space Z, in the SAME units as the pose (metres if the calibration is). Wherever it is nearer than the model, the model is cut away, so a real car in front of the virtual one hides it instead of the overlay floating on top. Build it from stereo with cv2.reprojectImageTo3D + 'cv2 extractChannel' (coi=2), or from a metric monocular depth net. Non-finite or <= 0 means 'no measurement here' and never occludes, so the holes a matcher leaves are safe. Resolution is free - it is nearest-resampled to the render.
scene_depth_biasoptFLOAT0.00-1000–1000Added to scene_depth before the comparison, in scene units. Stereo depth is noisy and the surface the model stands ON is the one most likely to eat it: a small positive bias (0.1-0.5 m on a driving scene) pushes the scene back so the model wins ties. Negative biases it the other way.

Outputs (5)

NameTypeDescription
renderIMAGEServer-side software render (opencv_nodes/render3d.py) of the model over bg_image, through the SAME camera model as the live widget: overscanned pinhole rasterization warped into the lens with cv2.undistortPoints. No grid/axes - model and photo only, ready for compositing or diffing against the original photo. GLB triangles only.
silhouetteMASKThe rendered model's coverage as a MASK (1 = model, 0 = background), same resolution as the render, lens distortion applied. With antialiasing on, edge pixels carry fractional coverage. Feeds compositing, Overlay Masks, bbox extraction...
depthMASKOBJECT-relative depth as a MASK: 1 (white) at the model's point nearest the camera, 0 (black) at its farthest point - normalized over the model's own camera-space extent (ALL vertices, occluded ones included), never an arbitrary near/far range, so the visible minimum need not reach 0 when the far side is hidden. Background is 0; same resolution/distortion as the render. Feeds depth ControlNets, DoF blurs, fog compositing...
occlusionMASKWhere scene_depth HID the model: 1 on the pixels the model would have covered but the photographed scene is in front of, 0 everywhere else. All zeros when scene_depth is not wired. Use it to check the cut is landing on the right object, or to feather the contact edge.
depth_metricNPARRAYThe rasterizer's own HxW float32 z-buffer: camera-space Z in SCENE UNITS - the same quantity 'CV Rasterize Mesh' calls depth, and NOT the 'depth' output above (which is normalized to [0, 1] for viewing). This is what the scene_depth input of 'CV Project Points (Sequence)' / the 'CV Annotate Model' blueprint wants, so anchors on the far side of the model get hidden without rasterizing it a second time - and unlike 'CV Rasterize Mesh' it carries the lens distortion, so it stays registered with the render. +inf where nothing was drawn, which reads as 'no surface': a point projecting off the silhouette stays visible. With scene_depth wired this is the COMPOSITE surface (the photographed scene wherever it won the depth test), so an anchor a real object hides is hidden too.