CV DType
The one-string node that kills 'wrong dtype' errors
- nparray
- dtype
What it's for
OpenCV is strict about dtypes in a way ComfyUI never trained you for. cv2.accumulate wants float32. A cv2.LUT wants uint8. Half the ximgproc filters will raise a type complaint at you in a wall of C++ that mentions -215 and stops being helpful around character forty.
So you cast. And then you have a hard-coded float32 in a CV Cast Array node that was correct for the one input you tested with and is wrong for every other image the workflow touches.
CV DType is the fix for that specific class of bug: it reads the dtype off any ndarray and returns its numpy dtype name as a string - 'float32', 'uint8', whatever it is - ready to wire into the dtype input of CV Cast Array. Round trip: take the input's dtype, do your work, cast back to what you started with. The graph adapts instead of you remembering.
How it works
It's about as thin as a node gets, and that's fine. One input, nparray (any ndarray), one output, dtype (a STRING). The mechanism is ndarray.dtype.name.
What makes it more than trivia is the pattern it belongs to: pulling a value out of a widget and onto a wire so one place in the graph decides for all of them. That's the same move as the seed-primitive and the value node in ComfyUI's core plumbing - set it once, fan it out, stop typing it in five places. Here the value being fanned out is the dtype your pipeline entered with, and the win is that a workflow built with images and a workflow built with float masks can share one branch.
The pack uses this trick all over: CV Array Size and CV Array Shape do it for dimensions, CV DCT Flags and its relatives do it for flag bitmasks, CV Camera Matrix does it for intrinsics. This is the dtype version, and it's the smallest of them.
Where to put it
The canonical wiring is: read the dtype off the source you're about to process, keep the output as a STRING feeding CV Cast Array's dtype input, and cast back to it at the end of the branch. Same trick is handy for CV Inspect side-checks and for anything that wants a name rather than a code.
Install
ComfyUI Manager, search ComfyUI CV. Or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
Restart. The pack needs Python ≥ 3.12, a recent ComfyUI (V3 node API) and the contrib wheel:
pip install "opencv-contrib-python-headless~=5.0.0.93"
Where people get burned
There's no dtype on an IMAGE. The input is NPARRAY, full stop. ComfyUI's IMAGE is a torch tensor of float32, not an ndarray - run it through CV Image → CV first if you want to inspect it as an array. Wiring an IMAGE socket here just won't connect.
dtype is not range. The single most expensive confusion in this corner of the graph. uint8 images carry 0..255; float32 in ComfyUI's IMAGE carries 0..1. Changing the container doesn't change what the numbers mean. The pack's bridge nodes do the rescaling when data crosses the IMAGE boundary - a bare cast does not, so casting a 0..255 array to float32 and then handing it to something expecting 0..1 gives you a very bright, very clipped picture. Read the dtype, but when you cross the bridge, let the bridge do the scaling.
It's a numpy name, not an OpenCV code. 'float32', not CV_32F, and not CV_8UC3. The three-channel-ness lives in the shape, which is CV Array Shape's job.
Cast late, not early. Converting to float32 "just in case" at the top of a branch doubles your memory for the rest of the run, and most of these raw wrappers were fine with the input they got. Read the dtype; only cast where a node actually complains.
Inputs (1)
| Name | Type | Default | Description |
|---|---|---|---|
| nparray | NPARRAY | Any ndarray whose dtype to inspect. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| dtype | STRING | The numpy dtype name (e.g. 'float32', 'uint8'). |