Source code for cv2ext

# Copyright (c) 2024 Justin Davis (davisjustin302@gmail.com)
#
# MIT License
# ruff: noqa: E402, I001
"""
Package containing helpful tools for working with opencv.

Submodules
----------
:mod:`bboxes`
    Submodule containing tools for working with bounding boxes in images.
:mod:`cli`
    Submodule containing command line interface tools.
:mod:`detection`
    Submodule containing tools for performing simple types of detection.
:mod:`image`
    Submodule containing tools for working with images.
:mod:`io`
    Submodule containing tools for working with video and image io.
:mod:`metrics`
    Submodule containing tools for working with image metrics.
:mod:`research`
    Submodule containing implementations of research papers and methods.
:mod:`template`
    Submodule containing tools for working with templates in images.
:mod:`tracking`
    Submodule containing tools for tracking objects in videos.
:mod:`video`
    Submodule containing tools for working with videos.

Classes
-------
:class:`Display`
    A class for displaying images using a separate thread.
:class:`Fourcc`
    A class for handling the codecs for video writing.
:class:`IterableVideo`
    A class for iterating over frames in a video, optionally with threading.
:class:`VideoWriter`
    A class for writing videos.

Functions
---------
:func:`set_log_level`
    Set the log level for the cv2ext package.
:func:`enable_jit`
    Enable just-in-time compilation using Numba for some functions.
:func:`disable_jit`
    Disable just-in-time compilation using Numba for some functions.
:func:`register_jit`
    Register a function to be just-in-time compiled.

"""

from __future__ import annotations

# setup the logger before importing anything else
import logging
import os
import sys

# import the flags object
from ._flags import FLAGS


# Created from answer by Dennis at:
# https://stackoverflow.com/questions/7621897/python-logging-module-globally
def _setup_logger(level: str | None = None) -> None:
    if level is not None:
        level = level.upper()
    level_map: dict[str | None, int] = {
        "DEBUG": logging.DEBUG,
        "INFO": logging.INFO,
        "WARNING": logging.WARNING,
        "WARN": logging.WARNING,
        "ERROR": logging.ERROR,
        "CRITICAL": logging.CRITICAL,
        None: logging.WARNING,
    }
    try:
        log_level = level_map[level]
    except KeyError:
        log_level = logging.WARNING

    # create logger
    logger = logging.getLogger(__package__)
    logger.setLevel(log_level)

    if not FLAGS.SETUP_LOG_HANDLER:
        formatter = logging.Formatter(
            "%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        )
        stdout_handler = logging.StreamHandler(sys.stdout)
        stdout_handler.setLevel(log_level)
        stdout_handler.setFormatter(formatter)
        logger.addHandler(stdout_handler)

        FLAGS.SETUP_LOG_HANDLER = True


[docs] def set_log_level(level: str) -> None: """ Set the log level for the cv2ext package. Parameters ---------- level : str The log level to set. One of "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL". Raises ------ ValueError If the level is not one of the allowed values. """ if level.upper() not in ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]: err_msg = f"Invalid log level: {level}" raise ValueError(err_msg) _setup_logger(level)
level = os.getenv("CV2EXT_LOG_LEVEL") _setup_logger(level) _log = logging.getLogger(__name__) if level is not None and level.upper() not in [ "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL", ]: _log.warning(f"Invalid log level: {level}. Using default log level: WARNING") import contextlib from typing import TYPE_CHECKING import cv2 if not cv2.useOptimized(): cv2.setUseOptimized(True) if TYPE_CHECKING: from typing_extensions import Self class _DEL: def __init__(self: Self, log: logging.Logger) -> None: self._log = log self._started = False self._windows: list[str] = [] self._is_windows = os.name == "nt" self._osname = "Windows" if self._is_windows else "Unix" self._log.debug(f"cv2ext is running on {self._osname}.") def __del__(self: Self) -> None: self._log.debug("cv2ext is being deleted.") for windowname in self._windows: self._log.debug(f"Deleting window {windowname}.") with contextlib.suppress(cv2.error): cv2.destroyWindow(windowname) self._log.debug("Deleting all windows.") cv2.destroyAllWindows() if self._is_windows: cv2.waitKey(1) def logwindow(self: Self, windowname: str) -> None: """ Queue windowname for deletion. Parameters ---------- windowname : str The name of the window to delete. """ if not self._started: self._log.debug("Starting cv2 window thread.") cv2.startWindowThread() self._started = True self._windows.append(windowname) _WINDOW_MANAGER = _DEL(_log) from . import bboxes, detection, image, io, metrics, research, template, tracking, video from .io import Display, Fourcc, IterableVideo, VideoWriter from ._jit import JIT, enable_jit, disable_jit, register_jit __all__ = [ "FLAGS", "JIT", "_WINDOW_MANAGER", "Display", "Fourcc", "IterableVideo", "VideoWriter", "bboxes", "cli", "detection", "disable_jit", "enable_jit", "image", "io", "metrics", "register_jit", "research", "set_log_level", "template", "tracking", "video", ] __version__ = "0.1.2" _log.info(f"Initialized cv2ext with version {__version__}") from . import cli _log.info("cv2ext.cli initialized.") __all__ += ["cli"] # # automatically enable the JIT if numba is present # if FLAGS.FOUND_NUMBA: # enable_jit()