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.
- model_3d
- camera_info
- bg_image
- scene_depth
- render
- silhouette
- depth
- occlusion
- depth_metric
Inputs (9)
| Name | Type | Default | Description |
|---|---|---|---|
| model_3d | FILE_3D_GLB,FILE_3D_GLTF,FILE_3D | 3D 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_info | LOAD3D_CAMERA | Camera 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_imageopt | IMAGE | Backdrop 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). | |
| shadingopt | COMBO | lambert (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_scaleopt | FLOAT | 1.000.25–4 | Multiplies 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). |
| antialiasopt | COMBO | 2x supersample | Anti-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_transformopt | STRING | Model 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_depthopt | NPARRAY | Optional 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_biasopt | FLOAT | 0.00-1000–1000 | Added 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)
| Name | Type | Description |
|---|---|---|
| render | IMAGE | Server-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. |
| silhouette | MASK | The 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... |
| depth | MASK | OBJECT-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... |
| occlusion | MASK | Where 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_metric | NPARRAY | The 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. |