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

"""
Фильтры добавления шума к изображениям.

Автор: Юров П.И.
"""

import numpy as np
import random
from typing import Tuple
from random import sample
from .base import FilterBase
from scipy.signal import lfilter, butter, sosfilt

from .colored_noise import powerlaw_psd_gaussian, pink_noise_2d


[документация] class GaussianNoise(FilterBase): """ Фильтр аддитивного гауссовского шума. Добавляет нормально распределенный шум с заданным стандартным отклонением. Атрибуты -------- param : float Стандартное отклонение гауссовского шума. """
[документация] def __init__(self, param: float) -> None: """ Инициализация фильтра гауссовского шума. Параметры --------- param : float Стандартное отклонение шума (должно быть положительным). """ if param <= 0: raise ValueError("Стандартное отклонение должно быть положительным") super().__init__(param, 'noise') self.param = param
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|gaussiannoise_{self.param}"
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """ Применение гауссовского шума к изображению. Параметры --------- image : np.ndarray Входное изображение (любой тип, будет преобразовано в float32). Возвращает ---------- np.ndarray Зашумленное изображение (той же формы и типа, что и входное). """ noise = np.random.normal(0, self.param, image.shape).astype(np.float32) noisy = image.astype(np.float32) + noise return np.clip(noisy, 0.0, 255.0).astype(image.dtype)
[документация] class PoissonNoise(FilterBase): """ Фильтр пуассоновского шума (шума дробления). Имитирует шум подсчета фотонов с пуассоновской статистикой. Атрибуты -------- param : float Интенсивность шума (от 0.0 до 1.0). """
[документация] def __init__(self, param: float) -> None: """ Инициализация фильтра пуассоновского шума. Параметры --------- param : float Интенсивность шума (от 0.0 до 1.0). """ super().__init__(param, 'noise') self.param = param
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|poissonnoise_{self.param}"
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """ Применение пуассоновского шума к изображению. Параметры --------- image : np.ndarray Входное изображение (любой тип). Возвращает ---------- np.ndarray Зашумленное изображение (той же формы и типа, что и входное). """ noisy = image + np.sqrt(image) * np.random.normal(0, 1, image.shape) * self.param return np.clip(noisy, 0, 255).astype(image.dtype)
[документация] class SaltAndPepperNoise(FilterBase): """ Фильтр импульсного шума (типа "соль-перец"). Добавляет случайные белые (соль) и черные (перец) пиксели к изображению. Атрибуты -------- white_pixel : float Относительная интенсивность белых пикселей (соль). black_pixel : float Относительная интенсивность черных пикселей (перец). noise_amount : int Максимальное количество зашумляемых пикселей. """
[документация] def __init__(self, param: Tuple[float, float, float]) -> None: """ Инициализация фильтра шума "соль-перец". Параметры --------- param : Tuple[float, float, float] Кортеж, содержащий: - white_pixel: Относительное количество белых пикселей (>=0). - black_pixel: Относительное количество черных пикселей (>=0). - noise_amount: Максимальное число изменяемых пикселей (>=0). """ super().__init__(param, 'noise') self.white_pixel = param[0] self.black_pixel = param[1] self.noise_amount = param[2]
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|saltandpappernoise_{self.param}"
[документация] def filter(self, image: np.ndarray) -> np.ndarray: """ Применение шума "соль-перец" к входному изображению. Параметры --------- image : np.ndarray Входное изображение (в градациях серого или цветное). Возвращает ---------- np.ndarray Изображение с добавленным шумом "соль-перец". """ noisy = image.copy() h, w = image.shape[:2] total_pixels = h * w white_count = self.param[0] black_count = self.param[1] max_noise = self.param[2] if white_count + black_count <= 0 or max_noise <= 0: return noisy total = white_count + black_count white_prob = white_count / total black_prob = black_count / total num_pixels = min(max_noise, total_pixels) indices = sample(range(total_pixels), num_pixels) noise_values = [255, 0] for idx in indices: selected_value = random.choices(noise_values, weights=[white_prob, black_prob], k=1)[0] if len(image.shape) == 3: noisy[idx // w, idx % w, :] = [selected_value] * 3 else: noisy[idx // w, idx % w] = selected_value return noisy
[документация] class OldPhotoNoise(FilterBase): """ Коричневый шум для имитации старых фотографий. Атрибуты -------- strength : int Сила шума (0-255). f3dB : float Частота среза экспоненциального фильтра (0-0.5). fs : float Псевдо-частота дискретизации. apply_highpass : bool Применять ли high-pass фильтр. highpass_cutoff : float Cutoff частота для high-pass фильтра. """
[документация] def __init__(self, strength: int = 30, f3dB: float = 0.05, fs: float = 1.0, apply_highpass: bool = True, highpass_cutoff: float = 0.01) -> None: """ Инициализация. Параметры --------- strength : int Сила шума (0-255). f3dB : float Частота среза экспоненциального фильтра (0-0.5). fs : float Псевдо-частота дискретизации. apply_highpass : bool Применять ли high-pass фильтр. highpass_cutoff : float Cutoff частота для high-pass фильтра. """ self.strength = strength self.f3dB = f3dB self.fs = fs self.apply_highpass = apply_highpass self.highpass_cutoff = highpass_cutoff super().__init__(1, 'noise')
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|oldphotonoise_{self.strength}"
[документация] def find_alpha(self, Fs: float, f3dB: float) -> float: """Вычислить альфу для экспоненциального фильтра.""" return ( np.sqrt( np.power(np.cos(2 * np.pi * f3dB / Fs), 2) - 4 * np.cos(2 * np.pi * f3dB / Fs) + 3 ) + np.cos(2 * np.pi * f3dB / Fs) - 1 )
[документация] def generate_2d_brownian_noise(self, shape: Tuple[int, int], alpha: float) -> np.ndarray: """Создает 2D коричневый шум с фильтрацией по строкам и столбцам.""" x = np.random.normal(0, 1, shape) b = [alpha] a = [1, -(1 - alpha)] for i in range(shape[0]): x[i, :] = lfilter(b, a, x[i, :]) for j in range(shape[1]): x[:, j] = lfilter(b, a, x[:, j]) return x
[документация] def high_pass_filter_2d(self, img: np.ndarray, fs: float, cutoff: float = 0.01) -> np.ndarray: """Применяет 2D high-pass фильтр через Butterworth.""" sos = butter(2, cutoff, btype='highpass', fs=fs, output='sos') filtered = np.copy(img) for i in range(img.shape[0]): filtered[i, :] = sosfilt(sos, img[i, :]) for j in range(img.shape[1]): filtered[:, j] = sosfilt(sos, filtered[:, j]) return filtered
[документация] def filter(self, img): """Применяет 2D Brownian шум к изображению.""" img = np.array(img, dtype=np.float32) alpha = self.find_alpha(self.fs, self.f3dB) brown_noise = self.generate_2d_brownian_noise(img.shape, alpha) if self.apply_highpass: brown_noise = self.high_pass_filter_2d(brown_noise, self.fs, cutoff=self.highpass_cutoff) brown_noise -= brown_noise.min() brown_noise /= (brown_noise.max() - brown_noise.min()) brown_noise = 2 * brown_noise - 1 noisy_img = img + self.strength * brown_noise noisy_img = np.clip(noisy_img, 0, 255).astype(np.uint8) return noisy_img
[документация] class ColoredNoise(FilterBase): """ Цветной шум (1/f)^beta. Атрибуты -------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). beta : float Параметр спектрального наклона (1.0 = розовый шум, 2.0 = коричневый). """
[документация] def __init__(self, noise_level: float = 0.2, beta: float = 1.0) -> None: """ Инициализация. Параметры --------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). beta : float Параметр спектрального наклона (1.0 = розовый, 2.0 = коричневый). """ self.noise_level = noise_level self.beta = beta super().__init__(1, 'noise')
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|colorednoise_{self.beta}_{self.noise_level}"
[документация] def filter(self, img): """Применяет цветной шум к изображению.""" assert img.ndim == 2, "Ожидается двумерное изображение (grayscale)." h, w = img.shape pink_noise = powerlaw_psd_gaussian(exponent=self.beta, size=(h, w)) pink_noise = pink_noise / np.std(pink_noise) pink_noise = pink_noise * self.noise_level * 255.0 noisy_img = img.astype(np.float32) + pink_noise noisy_img = np.clip(noisy_img, 0, 255).astype(np.uint8) return noisy_img
[документация] class Pink_Noise(FilterBase): """ Розовый шум (1/f). Атрибуты -------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). """
[документация] def __init__(self, noise_level: float = 0.2) -> None: """ Инициализация. Параметры --------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). """ self.noise_level = noise_level super().__init__(1, 'noise')
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|pinknoise_{self.noise_level}"
[документация] def filter(self, img): """Применяет розовый шум к изображению.""" noise = pink_noise_2d(img.shape, 1) * self.noise_level res = np.clip(img + noise, 0.0, 255.0).astype(np.int16) return res
[документация] class Brown_Noise(FilterBase): """ Коричневый шум (1/f)^2. Атрибуты -------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). """
[документация] def __init__(self, noise_level: float = 0.2) -> None: """ Инициализация. Параметры --------- noise_level : float Уровень шума (0.0 - без шума, 1.0 - сильный шум). """ self.noise_level = noise_level super().__init__(1, 'noise')
[документация] def description(self) -> str: """Выдает название шума в файловой системе с параметром.""" return f"|brownnoise_{self.noise_level}"
[документация] def filter(self, img): """Применяет коричневый шум к изображению.""" noise = pink_noise_2d(img.shape, 2) * self.noise_level res = np.clip(img + noise, 0.0, 255.0).astype(np.int16) return res