CV Slice Array
Numpy slicing as a node, one axis at a time
- nparray
- nparray
Every CV graph eventually needs a slice of an array: crop a padded result back to its original size, pull a column out of a detection table, take every third row, flip an axis. In pure numpy that's one line; in a graph it's a node.
CV Slice Array exposes numpy slicing with a widget per parameter, and the source comment says why it got built: the pack kept working around the lack of it, notably in a Draw Labels chain that needed Permute → Take By Index → Permute just to get a column out of an (N,4) table. Now it's start=2, stop=3.
The semantics that trip people up
It slices one axis per node. Need rows and columns? Two nodes chained. That's not a limitation being hidden - it's the design, and it's why each node stays comprehensible.
- axis - INT, -8 to 8, default 0.
0is rows (image height, detections in a table),1is columns,-1is the last axis (channels). Negative counts from the end. - start - first index kept. Negative counts from the end, so
-2is the second-to-last. - stop - one past the last index. And here's the genuinely useful quirk the tooltip calls out:
stop = 0means "to the end of the axis", because a real slice ending at 0 would be empty, and the common case in this pack is cropping a padded array back to a size that arrives as a link. If that computed size is 0, you get the whole axis rather than an empty result. Negativestopcounts from the end (-1 drops the last element), which follows numpy. - step (optional, advanced) - stride, -1024 to 1024.
2keeps every other index. Negative walks backwards, and thenstartis where it starts from: to reverse a whole axis you needstart=-1, stop=0, step=-1. With the defaultstart=0, a backwards walk stops immediately and hands you one element. That is the trap of the node, stated in its own tooltip, and it's easy to hit.step = 0is rejected, because numpy doesn't define it. - keep_axis (optional, advanced, default true) - when the slice keeps exactly one index, does the axis survive with length 1, or get dropped?
(N,4)→(N,1)versus(N,4)→(N,). On keeps a table a table; off is what a downstream node wanting a flat vector of scores expects.
One input, nparray - any shape, any dtype, values untouched. One output, nparray, same dtype.
The two behaviours worth knowing before you wire it
Out-of-range bounds clamp the numpy way instead of raising. An empty result is a valid outcome here, not a wiring mistake. That's consistent with the pack's style - plenty of its nodes return a found flag or an empty array instead of an exception - and it means a slice node in the middle of a batch graph won't kill the run when one frame is odd-sized. It also means silent emptiness is possible, so if something downstream is mysteriously blank, check the slice bounds rather than the downstream node.
You never get a view. The output is contiguous. That matters more than it sounds in a cv2 pipeline: a numpy view shares memory with its parent, so a later cv2 call that writes in place would corrupt the original array. The pack cuts that off deliberately.
What it's for
The canonical use is DNN letterboxing: you padded the input up to a multiple of 32 because the network demands it, so you slice the result back to the real size - axis 0 for rows, axis 1 for columns, two nodes, out comes the image you actually wanted. The other two: pulling a column out of a detection or score table (with keep_axis off if the consumer wants a flat vector), and subsampling a sequence with step.
Install
ComfyUI Manager → ComfyUI CV → install → restart, or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Python ≥ 3.12, recent ComfyUI on the V3 node API. No models. It's registered under image/CV/low-level, which is where the pack files its glue nodes - they ship alongside ~470 auto-generated cv2.* wrappers, so the category is deep.
Common issues
- Reversing an axis returned one element.
step=-1with the defaultstart=0. Setstart=-1andstop=0. - Axis out of range error. Real error, real message: it names the axis and the valid range for your array's dimensionality. A 2-D array has no
-3. - Shape mismatch downstream.
keep_axis. Turn it off for flat vectors, on for tables. - Empty array slipped through. Check bounds. The node won't tell you it clamped.
- Nodes gone from the menu after another pack installed. Non-contrib OpenCV wheel overwrote the contrib one in the shared
site-packages/cv2.python tools/repair_opencv_contrib.py --check, then--apply.
Inputs (6)
| Name | Type | Default | Description |
|---|---|---|---|
| nparray | NPARRAY | Array to slice. Any shape and dtype; values are not touched, only selected. | |
| axis | INT | 0-8–8 | Which axis to slice. 0 = rows (height of an image, detections of a table), 1 = columns (width), -1 = the last axis (channels). Negative counts from the end. |
| start | INT | 0-2147483647–2147483647 | First index to keep, counting from 0. Negative counts from the end (-2 = the second-to-last). |
| stop | INT | 0-2147483647–2147483647 | One PAST the last index to keep. 0 means 'to the end of the axis' - the common case when cropping a padded array back to a computed size, where the size arrives as a link. Negative counts from the end (-1 = drop the last element). |
| stepopt | INT | 1-1024–1024 | Stride between kept indices. 2 = every other one. A NEGATIVE step walks backwards, and then 'start' is where it starts FROM: reversing a whole axis is start=-1, stop=0, step=-1 - with the default start=0 a backwards walk stops immediately and returns a single element. 0 is rejected (numpy does not define it). |
| keep_axisopt | BOOLEAN | true | When the slice keeps exactly ONE index, whether the axis survives with length 1 (True, e.g. (N,4) -> (N,1)) or is dropped (False, (N,4) -> (N,)). Off is what a downstream node wanting a flat vector of scores expects; on is what keeps a table a table. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | The selected part of the input, same dtype. A view is never returned - the result is contiguous, so a later cv2 node cannot write into the original. |