cv2.accumulateProduct
The second-moment accumulator nobody explains
- src1
- src2
- dst
- mask
- nparray
cv2.accumulateProduct computes dst += src1 × src2, pixel by pixel. If that reads like a solution in search of a problem, that's because it mostly is one - until you're doing statistics, where it's exactly the missing half of a correlation.
It lives in the same family as cv2_accumulate, cv2_accumulateSquare and cv2_accumulateWeighted, and the family exists for one reason: these are the accumulation primitives OpenCV's own estimators (calcCovarMatrix, the Harris/RANSAC internals, template statistics) are built from. You get them because the registry is generated from the type stubs - including the parts you don't have a use for yet.
How it works
dst += src1 * src2, with the multiplication done and the sum stored in a float accumulator. Both inputs must be the same size and type, dst must be 32-bit or 64-bit float with the same channel count, and an optional mask restricts which elements update.
The interesting property is the same one accumulate has: no saturation into 8-bit. Products of two bright uint8 pixels blow past 255 immediately - 255 × 255 = 65025 - so a float accumulator is not a nicety here, it's the only thing that works. Sum 100 frames of that and you're at six and a half million. The container holds it; your eyes won't, so inspect the numbers rather than the preview.
Like its siblings, this wrapper hands OpenCV a private copy of dst, so nothing persists between runs, and the batch path starts each frame from the dst you supplied. It is one multiply-accumulate, not a running estimator.
Inputs and outputs
src1,src2(NPARRAY,IMAGE,MASK) - the pair. Same type, same size.dst(NPARRAY,IMAGE,MASK) - the float accumulator you build with CV Constant Like → CV Cast Array (float32).mask(optional) - update only where it's set.- nparray - the updated accumulator.
What it's actually for
Pair this with cv2_accumulate and you have a running cross-moment: Σxy next to Σx gives you covariance, and covariance over an image pair is how classic vision code gets orientation, anisotropic diffusion tensors and structure matrices. Pair it with cv2_accumulateSquare and you get variances.
Concretely, in ComfyUI terms:
- Covariance between two channels or two frames - accumulate the products, then reduce with CV Array Statistic or feed the matrix into the pack's PCA/covariance nodes.
- Averaging a product over a stack - sum
A·Bover N frames and divide; that's a mean product, which is the numerator of a correlation coefficient. Denoising by averaging assumes zero correlation; this is how you check whether that assumption holds. - Template statistics - the second moment of your template patch, computed ahead of
cv2_matchTemplate, is the classic normalisation term in NCC-style matching.
If your actual goal is "multiply two images and get a picture", this is the wrong node - use cv2_multiply, which saturates and returns something displayable. accumulateProduct returns numbers, and it expects you to know why you wanted them.
Install
ComfyUI Manager → comfyui_cv (ComfyUI CV), or:
cd ComfyUI/custom_nodes
git clone https://github.com/bmad4ever/comfyui_cv
pip install "opencv-contrib-python-headless~=5.0.0.93"
Restart afterwards. Requires Python ≥ 3.12 and a recent ComfyUI built on the V3 node API; behaviour is pinned against OpenCV 5.0.0.93. Keep the contrib wheel - installing a non-contrib opencv-python over it empties the contrib submodules and hides nodes.
Where people get burned
A uint8 dst. Same trap as the rest of the family: OpenCV wants 32F/64F. Build the accumulator properly or it errors out (or does something worse).
Float64 input against a float32 accumulator. Mismatched depths raise. Get both sides consistent with CV Cast Array.
Reading the thumbnail as if it were an image. Min-max normalisation of a Σxy map shows you structure, not values. Use Inspect CV Data / CV Array Statistic when the number matters, CV Array → Image when it doesn't.
Expecting it to keep accumulating across runs. It won't. One call, one add.
And the standing caveat: these raw wrappers are auto-generated and uncurated, the OpenCV docs are the real manual, and the author explicitly warns the pack isn't production-hardened.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| src1 | NPARRAY,IMAGE,MASK | First input image, 1- or 3-channel, 8-bit or 32-bit floating point. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| src2 | NPARRAY,IMAGE,MASK | Second input image of the same type and the same size as src1 . Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| dst | NPARRAY,IMAGE,MASK | %Accumulator image with the same number of channels as input images, 32-bit or 64-bit floating-point. The low-level cv2 function writes its result into this array in place, but this wrapper passes cv2 a private copy, so your input array is never modified. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. | |
| maskopt | NPARRAY,IMAGE,MASK | Optional operation mask. Accepts a ComfyUI IMAGE/MASK directly (frame 0 of a batch) or an NPARRAY. Arithmetic ops (add, multiply, etc.) process the full IMAGE batch when both inputs have the same batch size. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| nparray | NPARRAY | — |