Source code for QVideo.filters.gamma

'''Gamma intensity-correction filter and companion Qt widget.'''
from qtpy import QtCore, QtWidgets
from pyqtgraph import SpinBox
from QVideo.lib.QVideoFilter import VideoFilter, QVideoFilter
from QVideo.lib.videotypes import Image
import numpy as np
import cv2

__all__ = ['GammaFilter', 'QGammaFilter']


[docs] class GammaFilter(VideoFilter): '''Gamma intensity correction. Applies the power-law transform .. math:: \\text{output} = \\left(\\frac{\\text{input}}{255}\\right)^{\\gamma} \\times 255 to every pixel. *γ* < 1 brightens the image (lifts shadows); *γ* > 1 darkens it (deepens shadows); *γ* = 1 is the identity. The transform is implemented as a 256-entry look-up table built once when :attr:`gamma` changes, so per-frame cost is a single table lookup regardless of image size. The same LUT is applied to every channel, preserving color balance. Parameters ---------- gamma : float Power-law exponent. Must be ≥ 0.1. Default: ``1.0``. ''' def __init__(self, gamma: float = 1.0) -> None: super().__init__() self.gamma = gamma @property def gamma(self) -> float: '''Power-law exponent (≥ 0.1); LUT is rebuilt on assignment.''' return self._gamma @gamma.setter def gamma(self, value: float) -> None: self._gamma = max(0.1, float(value)) table = np.arange(256, dtype=np.float32) / 255.0 self._lut = np.clip( np.power(table, self._gamma) * 255.0, 0, 255).astype(np.uint8)
[docs] def get(self) -> Image | None: '''Return the gamma-corrected frame. Returns ------- Image or None Corrected uint8 image, or ``None`` if no frame has been added. ''' if self.data is None: return None return cv2.LUT(self.data, self._lut)
[docs] def to_code(self) -> 'FilterCode': from QVideo.lib.QVideoFilter import FilterCode return FilterCode( imports=frozenset({'import numpy as np'}), lines=[ f'image = np.clip(np.power(image.astype(np.float32) / 255.0,' f' {self._gamma}) * 255.0, 0, 255).astype(np.uint8)', ], comment=f'gamma correction, γ={self._gamma}', )
[docs] class QGammaFilter(QVideoFilter): '''Widget for :class:`GammaFilter` with a gamma spinbox. Parameters ---------- parent : QtWidgets.QWidget or None Parent widget. ''' display_name = 'Gamma Correction' display_category = 'Preprocessing' def __init__(self, parent: QtWidgets.QWidget | None = None) -> None: super().__init__(parent, 'Gamma Correction', GammaFilter()) def _setupUi(self) -> None: super()._setupUi() self._layout.addWidget(QtWidgets.QLabel('γ')) self._gammaBox = SpinBox(value=self.filter.gamma, bounds=(0.1, 10.0), step=0.1) self._layout.addWidget(self._gammaBox) def _connectSignals(self) -> None: super()._connectSignals() self._gammaBox.valueChanged.connect(self._setGamma) @QtCore.Slot(object) def _setGamma(self, value: float) -> None: self.filter.gamma = value
if __name__ == '__main__': # pragma: no cover QGammaFilter.example()