Utilities

Numpy-based cosine similarity optimized to generate same- and different-source scores

import numpy as np
from forensicface.utils import compute_ss_ds

X = np.random.rand(4, 128)  # 4 embeddings of dimension 128
x_id = np.array([0, 0, 1, 1])  # Identity labels
x_names = np.array(["0_a.jpg", "0_b.jpg", "1_a.jpg", "1_b.jpg"])

Z = np.random.rand(4, 128)  # 4 embeddings of dimension 128
z_id = np.array([0, 1, 1, 2])  # Identity labels
z_names = np.array(["0_c.jpg", "1_c.jpg", "1_d.jpg", "2_a.jpg"])

scores, y, names = compute_ss_ds(
    X=X, x_id=x_id, x_names=x_names, Z=Z, z_id=z_id, z_names=z_names
)
scores, y, names
(array([0.74897761, 0.75348665, 0.7746813 , 0.7476807 , 0.75427125,
        0.7612412 , 0.74093543, 0.69641632, 0.79051704, 0.75934837,
        0.75419815, 0.76641602, 0.78709193, 0.72118283, 0.75966526,
        0.7605714 ]),
 array([1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
 [(np.str_('0_a.jpg'), np.str_('0_c.jpg')),
  (np.str_('0_b.jpg'), np.str_('0_c.jpg')),
  (np.str_('1_a.jpg'), np.str_('1_c.jpg')),
  (np.str_('1_a.jpg'), np.str_('1_d.jpg')),
  (np.str_('1_b.jpg'), np.str_('1_c.jpg')),
  (np.str_('1_b.jpg'), np.str_('1_d.jpg')),
  (np.str_('0_a.jpg'), np.str_('1_c.jpg')),
  (np.str_('0_a.jpg'), np.str_('1_d.jpg')),
  (np.str_('0_a.jpg'), np.str_('2_a.jpg')),
  (np.str_('0_b.jpg'), np.str_('1_c.jpg')),
  (np.str_('0_b.jpg'), np.str_('1_d.jpg')),
  (np.str_('0_b.jpg'), np.str_('2_a.jpg')),
  (np.str_('1_a.jpg'), np.str_('0_c.jpg')),
  (np.str_('1_a.jpg'), np.str_('2_a.jpg')),
  (np.str_('1_b.jpg'), np.str_('0_c.jpg')),
  (np.str_('1_b.jpg'), np.str_('2_a.jpg'))])
scores, y, names = compute_ss_ds(X=X, x_id=x_id, Z=Z, z_id=z_id)
scores, y, names
(array([0.74897761, 0.75348665, 0.7746813 , 0.7476807 , 0.75427125,
        0.7612412 , 0.74093543, 0.69641632, 0.79051704, 0.75934837,
        0.75419815, 0.76641602, 0.78709193, 0.72118283, 0.75966526,
        0.7605714 ]),
 array([1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
 None)
scores, y, names = compute_ss_ds(X=X, x_id=x_id, x_names=x_names)
scores, y, names
(array([0.76977981, 0.74947804, 0.72975574, 0.7696751 , 0.77814901,
        0.7889754 ]),
 array([1., 1., 0., 0., 0., 0.]),
 [(np.str_('0_a.jpg'), np.str_('0_b.jpg')),
  (np.str_('1_a.jpg'), np.str_('1_b.jpg')),
  (np.str_('0_a.jpg'), np.str_('1_a.jpg')),
  (np.str_('0_a.jpg'), np.str_('1_b.jpg')),
  (np.str_('0_b.jpg'), np.str_('1_a.jpg')),
  (np.str_('0_b.jpg'), np.str_('1_b.jpg'))])
scores, y, names = compute_ss_ds(X=X, x_id=x_id)
scores, y, names
(array([0.76977981, 0.74947804, 0.72975574, 0.7696751 , 0.77814901,
        0.7889754 ]),
 array([1., 1., 0., 0., 0., 0.]),
 None)

Export the Python version and the packages names and versions in the active virtual environment

from forensicface.utils import freeze_env

freeze_env()
{'Python version': '3.13.13 (main, Apr 14 2026, 14:28:56) [Clang 22.1.3 ]',
 'annotated-types': '0.7.0',
 'anyio': '4.13.0',
 'argon2-cffi': '25.1.0',
 'argon2-cffi-bindings': '25.1.0',
 'arrow': '1.4.0',
 'asttokens': '3.0.1',
 'async-lru': '2.3.0',
 'attrs': '26.1.0',
 'babel': '2.18.0',
 'beartype': '0.22.9',
 'beautifulsoup4': '4.14.3',
 'black': '26.3.1',
 'bleach': '6.3.0',
 'certifi': '2026.4.22',
 'cffi': '2.0.0',
 'charset-normalizer': '3.4.7',
 'click': '8.3.3',
 'colorama': '0.4.6',
 'comm': '0.2.3',
 'contourpy': '1.3.3',
 'cycler': '0.12.1',
 'debugpy': '1.8.20',
 'decorator': '5.2.1',
 'defusedxml': '0.7.1',
 'executing': '2.2.1',
 'fastjsonschema': '2.21.2',
 'flatbuffers': '25.12.19',
 'fonttools': '4.62.1',
 'forensicface': '0.5.1',
 'fqdn': '1.5.1',
 'griffe': '2.0.2',
 'griffecli': '2.0.2',
 'griffelib': '2.0.2',
 'h11': '0.16.0',
 'httpcore': '1.0.9',
 'httpx': '0.28.1',
 'idna': '3.13',
 'ImageIO': '2.37.3',
 'importlib_metadata': '9.0.0',
 'importlib_resources': '7.1.0',
 'imutils': '0.5.4',
 'iniconfig': '2.3.0',
 'ipykernel': '7.2.0',
 'ipython': '9.13.0',
 'ipython_pygments_lexers': '1.1.1',
 'ipywidgets': '8.1.8',
 'isoduration': '20.11.0',
 'jedi': '0.20.0',
 'Jinja2': '3.1.6',
 'json5': '0.14.0',
 'jsonpointer': '3.1.1',
 'jsonschema': '4.26.0',
 'jsonschema-specifications': '2025.9.1',
 'jupyter': '1.1.1',
 'jupyter-console': '6.6.3',
 'jupyter-events': '0.12.1',
 'jupyter-lsp': '2.3.1',
 'jupyter_client': '8.8.0',
 'jupyter_core': '5.9.1',
 'jupyter_server': '2.17.0',
 'jupyter_server_terminals': '0.5.4',
 'jupyterlab': '4.5.7',
 'jupyterlab_pygments': '0.3.0',
 'jupyterlab_server': '2.28.0',
 'jupyterlab_widgets': '3.0.16',
 'kiwisolver': '1.5.0',
 'lark': '1.3.1',
 'lazy-loader': '0.5',
 'markdown-it-py': '4.0.0',
 'MarkupSafe': '3.0.3',
 'matplotlib': '3.10.9',
 'matplotlib-inline': '0.2.1',
 'mdurl': '0.1.2',
 'mistune': '3.2.0',
 'ml_dtypes': '0.5.4',
 'mypy_extensions': '1.1.0',
 'nbclient': '0.10.4',
 'nbconvert': '7.17.1',
 'nbformat': '5.10.4',
 'nest-asyncio': '1.6.0',
 'networkx': '3.6.1',
 'notebook': '7.5.6',
 'notebook_shim': '0.2.4',
 'numpy': '2.4.4',
 'nvidia-cublas-cu12': '12.9.2.10',
 'nvidia-cuda-nvrtc-cu12': '12.9.86',
 'nvidia-cuda-runtime-cu12': '12.9.79',
 'nvidia-cudnn-cu12': '9.21.1.3',
 'nvidia-cufft-cu12': '11.4.1.4',
 'nvidia-curand-cu12': '10.3.10.19',
 'nvidia-nvjitlink-cu12': '12.9.86',
 'onnx': '1.21.0',
 'onnxruntime-gpu': '1.25.0',
 'opencv-python-headless': '4.13.0.92',
 'packaging': '26.2',
 'pandas': '3.0.2',
 'pandocfilters': '1.5.1',
 'parso': '0.8.7',
 'pathspec': '1.1.1',
 'pexpect': '4.9.0',
 'pillow': '12.2.0',
 'platformdirs': '4.9.6',
 'pluggy': '1.6.0',
 'plum-dispatch': '2.9.0',
 'prometheus_client': '0.25.0',
 'prompt_toolkit': '3.0.52',
 'protobuf': '7.34.1',
 'psutil': '7.2.2',
 'ptyprocess': '0.7.0',
 'pure_eval': '0.2.3',
 'pycparser': '3.0',
 'pydantic': '2.13.3',
 'pydantic_core': '2.46.3',
 'Pygments': '2.20.0',
 'pyparsing': '3.3.2',
 'pytest': '9.0.3',
 'python-dateutil': '2.9.0.post0',
 'python-json-logger': '4.1.0',
 'pytokens': '0.4.1',
 'PyYAML': '6.0.3',
 'pyzmq': '27.1.0',
 'quartodoc': '0.11.1',
 'referencing': '0.37.0',
 'requests': '2.33.1',
 'rfc3339-validator': '0.1.4',
 'rfc3986-validator': '0.1.1',
 'rfc3987-syntax': '1.1.0',
 'rich': '15.0.0',
 'rpds-py': '0.30.0',
 'scikit-image': '0.26.0',
 'scipy': '1.17.1',
 'Send2Trash': '2.1.0',
 'setuptools': '82.0.1',
 'six': '1.17.0',
 'soupsieve': '2.8.3',
 'sphobjinv': '2.4',
 'stack-data': '0.6.3',
 'tabulate': '0.10.0',
 'terminado': '0.18.1',
 'tifffile': '2026.4.11',
 'tinycss2': '1.4.0',
 'tornado': '6.5.5',
 'tqdm': '4.67.3',
 'traitlets': '5.14.3',
 'typing-inspection': '0.4.2',
 'typing_extensions': '4.15.0',
 'tzdata': '2026.2',
 'uri-template': '1.3.0',
 'urllib3': '2.6.3',
 'watchdog': '6.0.0',
 'wcwidth': '0.7.0',
 'webcolors': '25.10.0',
 'webencodings': '0.5.1',
 'websocket-client': '1.9.0',
 'widgetsnbextension': '4.0.15',
 'zipp': '3.23.1'}