Source code for cv2ext.metrics._ncc
# Copyright (c) 2024 Justin Davis (davisjustin302@gmail.com)
#
# MIT License
from __future__ import annotations
import contextlib
import cv2
import numpy as np
from cv2ext._jit import register_jit
@register_jit()
def _ncc_kernel(
image1: np.ndarray,
image2: np.ndarray,
) -> float:
image1 = image1.astype(np.float32)
image2 = image2.astype(np.float32)
img1std = np.std(image1)
img2std = np.std(image2)
if img1std == 0.0 or img2std == 0.0:
return 0.0
image1_numerator: np.ndarray = image1 - np.mean(image1) / img1std
image2_numerator: np.ndarray = image2 - np.mean(image2) / img2std
val = float(
np.sum(image1_numerator * image2_numerator)
/ (np.sqrt(np.sum(image1_numerator**2)) * np.sqrt(np.sum(image2_numerator**2))),
)
# clamp to [-1, 1] incase of floating point errors
return max(min(1.0, val), -1.0)
[docs]
def ncc(
image1: np.ndarray,
image2: np.ndarray,
size: tuple[int, int] | None = (112, 112),
*,
resize: bool | None = True,
) -> float:
"""
Compute the normalized cross-correlation between two images.
Parameters
----------
image1 : np.ndarray
The first image. Can be color or grayscale.
Converted to grayscale if color.
image2 : np.ndarray
The second image. Can be color or grayscale.
Converted to grayscale if color.
size : tuple[int, int], optional
The size to resize the images to, by default (112, 112).
If None, the images are not resized.
resize : bool, optional
If True, the images are resized to the given size before
computing the correlation, by default False.
Returns
-------
float
The normalized cross-correlation between the two images.
Raises
------
ValueError
If the images are not the same size and not resizing.
"""
if resize is None:
resize = False
if image1.shape != image2.shape and not resize:
err_msg = f"Images must be the same size if not resizing. Got {image1.shape} and {image2.shape}."
raise ValueError(err_msg)
colorchannels = 3
with contextlib.suppress(IndexError):
if image1.shape[2] == colorchannels: # type: ignore[misc]
image1 = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
with contextlib.suppress(IndexError):
if image2.shape[2] == colorchannels: # type: ignore[misc]
image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
if size is not None and resize:
image1 = cv2.resize(image1, size, interpolation=cv2.INTER_LINEAR)
image2 = cv2.resize(image2, size, interpolation=cv2.INTER_LINEAR)
return _ncc_kernel(image1, image2)