Исходный код blinddeconv.filters.blur

"""
Фильтры размытия изображений.

Автор: Беззаборов А.А., Юров П.И.
"""

import cv2 as cv
import numpy as np
from typing import Callable, Optional, Tuple
from .base import FilterBase

from scipy.interpolate import BSpline, make_interp_spline
from mpl_toolkits.mplot3d import Axes3D


[документация] class DefocusBlur(FilterBase): """ Фильтр размытия вне фокуса, имитирующий эффект расфокусировки камеры. Создает 2D размытие в форме колокола с использованием настраиваемой PSF-функции. Атрибуты -------- psf : Callable Функция распределения точки (PSF), генерирующая ядро размытия. param : float Параметр интенсивности размытия. kernel_size : Optional[int] Фиксированный размер ядра размытия (None для автоопределения). """
[документация] def __init__(self, psf: Callable[[np.ndarray, float], np.ndarray], param: float = 5.0, kernel_size: Optional[int] = None) -> None: """ Инициализация фильтра размытия вне фокуса. Параметры --------- psf : Callable Функция, принимающая (radius, param) и возвращающая значения ядра. param : float Параметр контроля интенсивности размытия. kernel_size : Optional[int] Опциональный фиксированный размер ядра (должен быть нечетным). """ self.psf = psf self.param = param self.kernel_size = kernel_size super().__init__(param, 'blur')
[документация] def description(self) -> str: """Возвращает название способа смаза и его параметры в файловой системе.""" return f"|defocus_{self.psf.__name__}_{self.param}_{self.kernel_size}"
[документация] def generate_kernel(self) -> np.ndarray: """Генерация ядра размытия.""" size = self.kernel_size or self._calculate_kernel_size() y, x = np.ogrid[-size//2:size//2+1, -size//2:size//2+1] radius = np.sqrt(x**2 + y**2) kernel = self.psf(radius, self.param) kernel[kernel < np.finfo(float).eps] = 0 return kernel / kernel.sum()
def _calculate_kernel_size(self) -> int: """Вычисление оптимального размера ядра на основе параметра размытия.""" return int(6 * self.param) | 1
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """ Применение размытия вне фокуса к изображению. Параметры --------- image : np.ndarray Входное изображение для размытия. Возвращает ---------- np.ndarray Размытое изображение. """ return cv.filter2D(image, -1, self.generate_kernel())
[документация] class MotionBlur(FilterBase): """ Фильтр размытия в движении, имитирующий линейное движение камеры. Создает одномерное направленное размытие, которое можно повернуть на любой угол. Атрибуты -------- psf : Callable PSF-функция для одномерного движения. param : float Параметр контроля длины/интенсивности размытия. angle : float Направление движения в градусах (0 = горизонтальное). kernel_length : Optional[int] Фиксированная длина ядра размытия. """
[документация] def __init__(self, psf: Callable[[np.ndarray, float], np.ndarray], param: float = 1.0, angle: float = 0, kernel_length: Optional[int] = None) -> None: """ Инициализация фильтра размытия в движении. Параметры --------- psf : Callable Функция (x, param) -> значения ядра, где x - 1D массив координат. param : float Параметр для PSF-функции. angle : float Угол направления размытия (в градусах). kernel_length : Optional[int] Длина размытия (нечетное число). """ self.psf = psf self.param = param self.angle = angle self.kernel_length = kernel_length super().__init__(param, 'blur')
[документация] def description(self) -> str: """Возвращает название способа смаза и его параметры в файловой системе.""" return f"|motion_{self.psf.__name__}_{self.param}_{self.angle}_{self.kernel_length}"
[документация] def generate_kernel(self) -> np.ndarray: """Генерация ядра размытия в движении.""" length = self.kernel_length or self._calculate_kernel_length() if length % 2 == 0: raise ValueError("Длина ядра должна быть нечетной") x_coords = np.linspace(-length//2, length//2, length) psf_1d = self.psf(x_coords, self.param) psf_1d = psf_1d / psf_1d.sum() kernel = np.zeros((length, length)) center = length // 2 kernel[center, :] = psf_1d rotation_matrix = cv.getRotationMatrix2D((center, center), self.angle, 1) rotated_kernel = cv.warpAffine(kernel, rotation_matrix, (length, length)) rotated_kernel[rotated_kernel < np.finfo(float).eps] = 0 return rotated_kernel / rotated_kernel.sum()
def _calculate_kernel_length(self) -> int: """Вычисление длины ядра на основе параметра размытия.""" return max(int(4 * self.param) | 1, 3)
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """Применение размытия в движении к изображению.""" return cv.filter2D(image, -1, self.generate_kernel())
[документация] class BSpline_blur(FilterBase): """ Фильтр размытия в движении, имитирующий криволинейное неравномерное движение. Создает 2D B-spline для моделирования сложных траекторий. Атрибуты -------- shape_points : np.ndarray Точки, задающие форму B-spline. intensity_points : np.ndarray Точки, задающие интенсивность. output_size : Tuple[int, int] Размер выходной матрицы PSF. shape_degree : int Степень B-spline для формы. intensity_degree : int Степень B-spline для интенсивности. n_samples : int Количество точек для дискретизации кривой. """
[документация] def __init__(self, shape_points: np.ndarray, intensity_points: np.ndarray, output_size: Tuple[int, int] = (15, 15), shape_degree: int = 3, intensity_degree: int = 2, n_samples: int = 1000): """ Инициализация фильтра криволинейного движения. Параметры --------- shape_points : np.ndarray Точки, задающие форму кривой [x, y]. intensity_points : np.ndarray Точки, задающие интенсивность вдоль кривой. output_size : Tuple[int, int] (width, height) Размер выходной матрицы PSF. shape_degree : int Степень B-spline для формы. intensity_degree : int Степень B-spline для интенсивности. n_samples : int Количество точек для дискретизации кривой. """ super().__init__(1, 'blur') self.shape_points = shape_points self.intensity_points = intensity_points self.output_size = output_size self.shape_degree = shape_degree self.intensity_degree = intensity_degree self.n_samples = n_samples
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """Применение размытия к изображению.""" kernel = self.create_dual_bspline_psf() res = cv.filter2D(src=image, ddepth=-1, kernel=kernel) return res
[документация] def description(self) -> str: """Возвращает название способа смаза в файловой системе.""" return f"|Bspline_motion_"
[документация] def create_dual_bspline_psf(self) -> np.ndarray: """Создает PSF используя два B-spline: для формы и для интенсивности.""" shape_points = np.array(self.shape_points) intensity_points = np.array(self.intensity_points) t_shape = np.linspace(0, 1, len(shape_points)) shape_spline_x = make_interp_spline( t_shape, shape_points[:, 0], k=self.shape_degree) shape_spline_y = make_interp_spline( t_shape, shape_points[:, 1], k=self.shape_degree) t_intensity = np.linspace(0, 1, len(intensity_points)) intensity_spline = make_interp_spline( t_intensity, intensity_points[:, 1], k=self.intensity_degree) t_samples = np.linspace(0, 1, self.n_samples) x_samples = shape_spline_x(t_samples) y_samples = shape_spline_y(t_samples) intensity_samples = intensity_spline(t_samples) intensity_samples = np.maximum(intensity_samples, 0) width, height = self.output_size x_min, x_max = x_samples.min(), x_samples.max() y_min, y_max = y_samples.min(), y_samples.max() x_scaled = (x_samples - x_min) / (x_max - x_min) * (width - 1) y_scaled = (y_samples - y_min) / (y_max - y_min) * (height - 1) psf = np.zeros(self.output_size) for i in range(len(x_scaled)): x_idx = int(round(x_scaled[i])) y_idx = int(round(y_scaled[i])) if 0 <= x_idx < width and 0 <= y_idx < height: psf[y_idx, x_idx] += intensity_samples[i] psf = np.maximum(psf, 0) if np.sum(psf) > 0: psf = psf / np.sum(psf) return psf
[документация] class Kernel_convolution(FilterBase): """ Фильтр, значения матрицы которого сохранены в .npy файле. Загружает и применяет эту матрицу к изображению. Атрибуты -------- npy_file_path : str Путь до .npy файла с ядром. """
[документация] def __init__(self, npy_file_path: str) -> None: """ Инициализация сохраненного фильтра. Параметры --------- npy_file_path : str Путь до .npy файла с ядром. """ self.npy_file_path = npy_file_path super().__init__(1, 'custom_kernel')
[документация] def description(self) -> str: """Возвращает название способа смаза в файловой системе.""" return f"|custom_kernel_"
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """Применение фильтра к изображению.""" kernel = np.load(self.npy_file_path) blurred = cv.filter2D(image, -1, kernel) return blurred
[документация] class Identical_kernel(FilterBase): """ Фильтр единичной матрицы. Фильтр, используемый при копировании оригинального изображения в буфер. """
[документация] def __init__(self) -> None: """Инициализация идентичного фильтра.""" super().__init__(1, 'blur')
[документация] def description(self) -> str: """Возвращает название способа смаза в файловой системе.""" return f"|I_"
[документация] def filter(self, image: np.ndarray) -> np.ndarray: "Применение фильтра к изображению." return image