CV Chart Scatter
CV Chart Scatter for k-means centres and feature vectors
- points
- classes
- spec
What this is for
Point data shows up all over a CV workflow: k-means cluster centres, PCA-projected feature vectors, keypoint coordinates, per-tile scores from a grid. The pack has CV Draw Points, which stamps points onto the image they came from - perfect when the question is "where on this frame". CV Chart Scatter answers the other question: "how do these points relate to each other". Two clusters overlapping, an outlier sitting miles from everything, a sweep that turns out to be monotonic - that's a plot, not an annotated photo.
It's an interactive Apache ECharts scatter drawn inside the node. Hover reads a point's name and exact coordinates, box-zoom gets you into a cluster, and the toolbar saves a PNG.
How it works
Everything that decides what the plot says - series, legends, class colours, axis names, formatting - is assembled in Python as an ECharts option, then handed to web/cv_charts.js, which only instantiates the chart. The same option comes back out as the spec STRING, so the plot is inspectable as text and testable without a browser.
The inputs that matter
points is the required array: (N, 2) x/y coordinates, and (N, 1, 2) - the shape the pack's point nodes emit - is accepted as-is. An empty array is valid and draws an empty plot, which is a deliberate choice: a failed detector upstream shouldn't turn into a red node.
classes is the optional one that earns its place. Give it an integer label per point (k-means labels, classifier predictions, which method a score came from) and the points split into one coloured, legend-named series per distinct value. class_names names those values in ascending order - JSON list, or ;/newline/comma text. point_names gives each point a tooltip name, one per point in the same order, which is how you get from "that dot is odd" to "that dot is tile 37".
Then two that quietly change whether the plot is honest:
y_down- turn it on when your coordinates are pixel positions. Image y grows downwards, and a scatter that doesn't know that will show you a mirror image of your layout. Leave it off for feature space, where you'd only be flipping a plot for no reason.palette- the default is the colourblind-safe Okabe-Ito set, which is the right default for unordered classes. The sampled ramps are for ordered classes (a sweep, a rank), where the reader should be able to tell neighbouring classes apart by hue distance.
Presentation: point_size, x_axis_name/y_axis_name (worth filling in - an unlabelled axis is a guess), theme, decimals (trailing zeros dropped) and chart_height.
Output is spec, and the node is an output node, so the chart draws when you queue. Preview as Text on spec is the lazy way to read the plotted coordinates.
Install
Same pack, same steps:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
# restart ComfyUI
ComfyUI Manager users: search ComfyUI CV (publisher bmad4ever). Needs Python ≥ 3.12, a ComfyUI recent enough for the V3 node API, and opencv-contrib-python-headless~=5.0.0.93 (in requirements.txt). Nothing to download for this node.
Common issues
classeslength mismatch. It must have one label per point - that's the one place this node will complain instead of coping.- Blank chart area after install or update. Reload the page; the frontend half of this node is a JS module and a cached tab won't load it.
- Y axis is upside down. Check
y_downbefore you go debugging your detector. If the coordinates came out of pixel space, this is the setting. - A whole class of nodes vanished from the pack. Something overwrote
site-packages/cv2with a non-contrib OpenCV wheel. Diagnose and repair withtools/repair_opencv_contrib.py --check/--apply.
Inputs (13)
| Name | Type | Default | Description |
|---|---|---|---|
| points | NPARRAY | (N, 2) x/y coordinates; (N, 1, 2) - the shape the point nodes emit - is accepted as-is. Empty is valid and draws an empty plot. | |
| title | STRING | Heading drawn above the chart. Empty draws none. | |
| classesopt | NPARRAY | Optional integer label per point (k-means labels, classifier predictions): points are grouped into one colored, legend-named series per distinct value. Length must match the point count. | |
| class_namesopt | STRING | Names for the class values, in ascending value order, as a JSON list or ';' / newline / ',' separated text. Defaults to 'class 0', ... | |
| point_namesopt | STRING | Name per point (same order as points), shown in the tooltip - use the same label list that names the matching image tiles. | |
| y_downopt | BOOLEAN | false | Flip the y axis so it grows downwards, matching image/pixel coordinates. Leave off for ordinary feature-space data. |
| point_sizeopt | INT | 102–60 | Marker diameter in pixels. |
| x_axis_nameopt | STRING | Caption under the x axis (what x measures). | |
| y_axis_nameopt | STRING | Caption beside the y axis (what y measures). | |
| paletteopt | COMBO | colorblind-safe (Okabe-Ito) | Class colors. The sampled ramps suit ORDERED classes (a sweep, a rank); the fixed palettes suit unordered ones. |
| themeopt | COMBO | dark | Chart colors. 'light' is for a PNG that will be pasted into a document. |
| decimalsopt | INT | 30–8 | Decimals shown for the coordinates in the tooltip. Trailing zeros are dropped. |
| chart_heightopt | INT | 340160–2048 | Height of the chart area inside the node, in pixels. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| spec | STRING | The chart definition as JSON - the exact option the frontend renders. Wire into 'Preview as Text' to read the plotted coordinates. |