图像的退化方式及python实现

未完成

测试图片使用lena,下载地址:https://www.ece.rice.edu/~wakin/images/lena512color.tiff

辅助函数

起名为util.py,下面某些退化函数需要用到该文件。

# -*- coding: utf-8 -*-
import numpy as np


def float_to_uint8(image, scale=255.0):
    """
    Convert image from float type to uint8, meanwhile the clip between [0, 255]
    will be done.

    Parameters
    ----------
    image: numpy array image data of float type
    scale: a scale factor for image data

    Returns
    -------
    image_uint8: numpy array image data of uint8 type
    """
    image_uint8 = np.clip(np.round(image * scale), 0, 255).astype(np.uint8)
    return image_uint8


def get_noise_index(image, noise_num_ratio):
    """
    Get noise index for a certain ratio of noise number

    Parameters
    ----------
    image: numpy array image data
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]

    Returns
    -------
    row: row indexes
    col: column indexes
    """
    image_height, image_width = image.shape[0:2]
    noise_num = int(np.round(image_height * image_width * noise_num_ratio))
    row = np.random.randint(0, image_height, noise_num)
    col = np.random.randint(0, image_width, noise_num)
    return row, col


def get_noise_shape(image, speckle_size=1.0):
    """
    Get noise shape according to image shape.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    speckle_size: speckle size of noise

    Returns
    -------
    noise_shape: a tuple whose length is 3
        The shape of noise. Let height, width be the image height and width.
        If image.ndim is 2, output noise_shape will be (height, width, 1),
        else (height, width, 3)
    """
    if speckle_size > np.min(image.shape[:2]):
        raise ValueError("speckle_size can NOT be larger than the min of "
                         "(image_height, image_width)")

    if image.ndim == 2:
        noise_shape = np.array(image.shape)
    else:
        noise_shape = np.array([*image.shape[:2], 3])

    if speckle_size > 1.0:
        noise_shape[0] = int(round(noise_shape[0] / speckle_size))
        noise_shape[1] = int(round(noise_shape[1] / speckle_size))
    return noise_shape


def add_noise(image, noise, noise_num_ratio=1.0):
    """
    Add noise to image.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    noise: additive noise, same shape as 'image'
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]

    Returns
    -------
    noisy_image: image with noise
    """
    if not 0.0 <= noise_num_ratio <= 1.0:
        raise ValueError('noise_num_ratio must between [0, 1]')

    # preprocess noise
    if noise.ndim == 2:
        noise = np.expand_dims(noise, axis=2)
    channel = noise.shape[2]

    # initialize noisy_image
    noisy_image = image.copy().astype(np.float32)
    if noisy_image.ndim == 2:
        noisy_image = np.expand_dims(noisy_image, axis=2)

    # add noise to noisy_image
    if noise_num_ratio >= 1.0:
        noisy_image[:, :, :channel] += noise
    else:
        row, col = get_noise_index(image, noise_num_ratio)
        noisy_image[row, col, :channel] += noise[row, col, ...]

    # post processing for dtype and shape
    if image.dtype == np.uint8:
        noisy_image = float_to_uint8(noisy_image, scale=1.0)
    else:
        noisy_image = noisy_image.astype(image.dtype)
    noisy_image = np.squeeze(noisy_image)
    return noisy_image

噪音

噪音由拍摄过程中的随机因素引入,在算法上我们使用随机数进行模拟。

关于噪音一般需要考虑如下几个因素:

  1. 噪音强弱:通过控制随机数生成器的参数来决定,比如高斯随机数中的sigma(std),均匀随机数中的low,high等。
  2. 噪音颜色:灰度还是彩色。灰度噪音要求同一位置三个通道上的随机数相等,而彩色噪音则无此要求。
  3. 噪音颗粒大小:噪音颗粒平均占据的像素大小。通常教材或者一般代码中加噪音的做法是给图像逐像素加上(或乘以)一个随机数,此时噪音颗粒的大小可以认为是一个像素,但我们看实际的图片会发现很多噪音的颗粒往往不是单个像素大小,而是更大一些。对于这种情况,本文档的代码中通过resize来实现增大噪音颗粒,具体来讲就是生成的随机数的宽高比图片小,然后将其resize到图片大小,所得到的噪音颗粒就会大于一个像素。
  4. 噪声数量:不一定每个像素都要加噪音,可以设置一定的数量比例。
  5. 噪音是加性还是乘性。
    加性噪音一般由下面公式添加:
    n o i s y _ i m a g e = i m a g e + n o i s e noisy\_image = image + noise noisy_image=image+noise
    乘性噪音一般由下面公式添加:
    n o i s y _ i m a g e = i m a g e + i m a g e ∗ n o i s e noisy\_image = image + image * noise noisy_image=image+imagenoise

下面介绍4中图像处理中常用的噪音,分别是高斯噪音,均匀噪音,泊松噪音,椒盐噪音。其中高斯噪音和均匀噪音既可以设置成加性,也可以设置为乘性。

针对每个噪音类型,我会从两个维度(彩色图,带alpha通道的彩色图,灰度图) * (彩色噪音,灰度噪音)给出6个测试图片。从外观表现上来看,带alpha通道的彩图与普通彩图加上噪音后没什么区别,测这样的图只是为了看看程序的健壮性如何。

高斯噪音

自然界的过程大多是高斯的,比如元器件尺寸与标准尺寸的差异分布。再加上多个随机过程(即便非高斯过程)的叠加一般也是高斯分布,所以高斯噪音几乎是一定存在的。

下面图中彩色噪声的颗粒搞得比较大,夸张了点,实际上一般不会有这么大颗粒的噪音,这里只是为了测试。在灰度图中,即便参数设置为加彩噪,最后实际上也会退化成加灰度噪音。

在这里插入图片描述

加性高斯噪音

在这里插入图片描述

乘性高斯噪音

下面是加高斯噪音的代码,控制高斯噪音强弱的关键参数是sigma,是高斯随机数的标准差。

# -*- coding: utf-8 -*-
import cv2
import numpy as np
from util import get_noise_shape
from util import add_noise


def generate_gaussian_noise(image,
                            sigma,
                            is_gray=False,
                            speckle_size=1.0,
                            multiplicative=False):
    """
    Generate gaussian distributed noise.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    sigma: standard deviation of gaussian random numbers
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise
    multiplicative: whether to add additive noise or multiplicative noise

    Returns
    -------
    noise: gaussian noise
    """
    noise_shape = get_noise_shape(image, speckle_size)

    # generate noise
    if is_gray:
        noise = np.random.normal(0.0, sigma, size=noise_shape[0:2])
        if image.ndim == 3:
            noise = np.expand_dims(noise, axis=2).repeat(3, axis=2)
    else:
        noise = np.random.normal(0.0, sigma, size=noise_shape)

    # resize noise to keep same size as image
    if speckle_size > 1.0:
        interp_type = np.random.choice([cv2.INTER_CUBIC, cv2.INTER_LINEAR])
        noise = cv2.resize(noise, dsize=image.shape[:2], fx=None, fy=None,
                           interpolation=interp_type)

    noise = noise.astype(np.float32)

    # make multiplicative noise if needed
    if multiplicative:
        if noise.ndim == 3:
            channel = noise.shape[2]
            noise *= np.float32(image[..., :channel])
        else:
            noise *= np.float32(image)

    return noise


def add_gaussian_noise(image,
                       sigma,
                       is_gray=False,
                       speckle_size=1.0,
                       noise_num_ratio=1.0,
                       multiplicative=False):
    """
    Add gaussian noise to image.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    sigma: standard deviation of gaussian random numbers
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]
    multiplicative: whether to add additive noise or multiplicative noise

    Returns
    -------
    noisy_image: image after adding noise
    """
    if sigma < 0:
        raise ValueError('sigma must >= 0.0')

    noise = generate_gaussian_noise(image,
                                    sigma=sigma,
                                    is_gray=is_gray,
                                    speckle_size=speckle_size,
                                    multiplicative=multiplicative)

    noisy_image = add_noise(image,
                            noise=noise,
                            noise_num_ratio=noise_num_ratio)
    return noisy_image


if __name__ == '__main__':
    # choose one of the following parameter set
    MULTI = True
    SIGMA = 0.1

    # MULTI = False
    # SIGMA = 25

    # read original image / gray scale image / image with alpha channel
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)  # 4 channel

    # generate noisy image
    noisy_image = add_gaussian_noise(image, SIGMA, False, 10.0, 1.0, MULTI)
    cv2.imwrite('gaussian_noise_color_image_color_noise.png', noisy_image)

    noisy_image = add_gaussian_noise(image_gray, SIGMA, False, 10.0, 1.0,
                                     MULTI)
    cv2.imwrite('gaussian_noise_gray_image_color_noise.png', noisy_image)

    noisy_image = add_gaussian_noise(image_alpha, SIGMA, False, 10.0, 1.0,
                                     MULTI)
    cv2.imwrite('gaussian_noise_alpha_image_color_noise.png', noisy_image)

    noisy_image = add_gaussian_noise(image, SIGMA, True, 2.5, 1.0, MULTI)
    cv2.imwrite('gaussian_noise_color_image_gray_noise.png', noisy_image)

    noisy_image = add_gaussian_noise(image_gray, SIGMA, True, 2.5, 1.0, MULTI)
    cv2.imwrite('gaussian_noise_gray_image_gray_noise.png', noisy_image)

    noisy_image = add_gaussian_noise(image_alpha, SIGMA, True, 2.5, 1.0, MULTI)
    cv2.imwrite('gaussian_noise_alpha_image_gray_noise.png', noisy_image)


均匀噪音

我也不清楚图像中什么情况下会出现均匀随机噪音,且当作一种能增加点随机性的退化手段吧。这个搞法跟高斯那部分几乎一样,只是生成噪音的随机数不一样,另外控制噪音的参数也不一样,均匀噪音直接用上下限来控制强弱。

在这里插入图片描述

加性均匀噪音

乘性均匀噪音的图就略了,肉眼也看不出太大区别。

代码如下:

# -*- coding: utf-8 -*-
import cv2
import numpy as np
from util import get_noise_shape
from util import add_noise


def generate_uniform_noise(image,
                           limit,
                           is_gray=False,
                           speckle_size=1.0,
                           multiplicative=False):
    """
    Generate uniform distributed noise.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    limit: limit of uniform random number
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise
    multiplicative: whether to add additive noise or multiplicative noise

    Returns
    -------
    noise: uniform noise
    """
    noise_shape = get_noise_shape(image, speckle_size)

    # generate noise
    if is_gray:
        noise = np.random.uniform(-limit, limit, size=noise_shape[0:2])
        if image.ndim == 3:
            noise = np.expand_dims(noise, axis=2).repeat(3, axis=2)
    else:
        noise = np.random.uniform(-limit, limit, size=noise_shape)

    # resize noise to keep same size as image
    if speckle_size > 1.0:
        interp_type = np.random.choice([cv2.INTER_CUBIC, cv2.INTER_LINEAR])
        noise = cv2.resize(noise, dsize=image.shape[:2], fx=None, fy=None,
                           interpolation=interp_type)

    noise = noise.astype(np.float32)

    # make multiplicative noise if needed
    if multiplicative:
        if noise.ndim == 3:
            channel = noise.shape[2]
            noise *= np.float32(image[..., :channel])
        else:
            noise *= np.float32(image)

    return noise


def add_uniform_noise(image,
                      limit,
                      is_gray=False,
                      speckle_size=1.0,
                      noise_num_ratio=1.0,
                      multiplicative=False):
    """
    Add uniform noise to image.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    limit: limit of uniform random number
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]
    multiplicative: whether to add additive noise or multiplicative noise

    Returns
    -------
    noisy_image: image after adding noise
    """
    if limit < 0:
        raise ValueError('limit must >= 0.0')

    noise = generate_uniform_noise(image,
                                   limit=limit,
                                   is_gray=is_gray,
                                   speckle_size=speckle_size,
                                   multiplicative=multiplicative)

    noisy_image = add_noise(image,
                            noise=noise,
                            noise_num_ratio=noise_num_ratio)
    return noisy_image


if __name__ == '__main__':
    # choose one of the following parameter set
    MULTI = True
    LIMIT = 0.1

    # MULTI = False
    # LIMIT = 25

    # read original image / gray scale image / image with alpha channel
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)  # 4 channel

    # generate noisy image
    noisy_image = add_uniform_noise(image, LIMIT, False, 10.0, 1.0, MULTI)
    cv2.imwrite('uniform_noise_color_image_color_noise.png', noisy_image)

    noisy_image = add_uniform_noise(image_gray, LIMIT, False, 10.0, 1.0,
                                    MULTI)
    cv2.imwrite('uniform_noise_gray_image_color_noise.png', noisy_image)

    noisy_image = add_uniform_noise(image_alpha, LIMIT, False, 10.0, 1.0,
                                    MULTI)
    cv2.imwrite('uniform_noise_alpha_image_color_noise.png', noisy_image)

    noisy_image = add_uniform_noise(image, LIMIT, True, 2.5, 1.0, MULTI)
    cv2.imwrite('uniform_noise_color_image_gray_noise.png', noisy_image)

    noisy_image = add_uniform_noise(image_gray, LIMIT, True, 2.5, 1.0, MULTI)
    cv2.imwrite('uniform_noise_gray_image_gray_noise.png', noisy_image)

    noisy_image = add_uniform_noise(image_alpha, LIMIT, True, 2.5, 1.0, MULTI)
    cv2.imwrite('uniform_noise_alpha_image_gray_noise.png', noisy_image)


泊松噪音

泊松噪音是一种计数噪音,比如电话接线员没小时可能接起电话的次数,饭店每天可能接到的外卖订单个数等等。这个变量肯定有一个期望,但是实际的数字在期望附近上下波动,波动的结果就是针对这个期望产生的泊松随机数。

对图像而言,泊松噪音模拟了光子打到传感器的随机过程,这是一个计数过程,所以用泊松过程来模拟。泊松噪音的输入是图像本身(的像素值),输出就是加了泊松噪音的图像,而不是单纯的噪音。但是通过这种方式加泊松噪音无法调节强弱,所以本文档的做法是先使用带有泊松噪音的图像减去原图像,得到噪音,然后给噪音乘上一个系数,最后再加回到原图像,这样就可以利用系数来调节噪音强弱。

请注意,由于泊松噪音是计数噪音,所以泊松噪音的输入必需是uint8类型的图像,数值区间得是[0, 255],不能是[0, 1]范围那种。

在这里插入图片描述

泊松噪音

光从表面来看的话,跟高斯和均匀噪音傻傻分不清。。。

代码如下:

# -*- coding: utf-8 -*-
import cv2
import numpy as np
from util import get_noise_shape
from util import add_noise


def generate_poisson_noise(image,
                           scale,
                           is_gray=False,
                           speckle_size=1.0):
    """
    Generate poisson distributed noise.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    scale:  scale multiplied to poisson noise
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise

    Returns
    -------
    noise: poisson noise
    """
    # preprocess image
    image_temp = image.copy()
    if image_temp.ndim == 3 and image_temp.shape[2] > 3:
        image_temp = image_temp[:, :, :3]

    ndim = image.ndim
    if is_gray and ndim == 3:
        image_temp = cv2.cvtColor(image_temp, cv2.COLOR_BGR2GRAY)

    noise_shape = get_noise_shape(image, speckle_size)
    if np.any(image_temp.shape[:2] != noise_shape[:2]):
        image_temp = cv2.resize(image_temp, dsize=noise_shape[:2],
                                fx=None, fy=None, interpolation=cv2.INTER_AREA)

    # generate noise
    noisy_image = np.float32(np.random.poisson(image_temp))
    noise = noisy_image - image_temp
    if is_gray and ndim == 3:
        noise = np.expand_dims(noise, axis=2).repeat(3, axis=2)
    noise *= scale

    # resize noise to keep same size as image
    if speckle_size > 1.0:
        interp_type = np.random.choice([cv2.INTER_CUBIC, cv2.INTER_LINEAR])
        noise = cv2.resize(noise, dsize=image.shape[:2], fx=None, fy=None,
                           interpolation=interp_type)
    return noise


def add_poisson_noise(image,
                      scale,
                      is_gray=False,
                      speckle_size=1.0,
                      noise_num_ratio=1.0):
    """
    Add poisson noise to image.

    Parameters
    ----------
    image: numpy image data, shape is [H, W, C], C is optional
    scale:  scale multiplied to poisson noise
    is_gray: whether the noise is color or gray
    speckle_size: speckle size of noise
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]

    Returns
    -------
    noisy_image: image after adding noise
    """
    if scale < 0:
        raise ValueError('scale must >= 0.0')

    noise = generate_poisson_noise(image,
                                   scale=scale,
                                   is_gray=is_gray,
                                   speckle_size=speckle_size)

    noisy_image = add_noise(image,
                            noise=noise,
                            noise_num_ratio=noise_num_ratio)
    return noisy_image


if __name__ == '__main__':
    SCALE = 1.0
    # read original image / gray scale image / image with alpha channel
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)  # 4 channel

    # generate noisy image
    noisy_image = add_poisson_noise(image, SCALE, False, 10.0, 1.0)
    cv2.imwrite('poisson_noise_color_image_color_noise.png', noisy_image)

    noisy_image = add_poisson_noise(image_gray, SCALE, False, 10.0, 1.0)
    cv2.imwrite('poisson_noise_gray_image_color_noise.png', noisy_image)

    noisy_image = add_poisson_noise(image_alpha, SCALE, False, 10.0, 1.0)
    cv2.imwrite('poisson_noise_alpha_image_color_noise.png', noisy_image)

    noisy_image = add_poisson_noise(image, SCALE, True, 2.5, 1.0)
    cv2.imwrite('poisson_noise_color_image_gray_noise.png', noisy_image)

    noisy_image = add_poisson_noise(image_gray, SCALE, True, 2.5, 1.0)
    cv2.imwrite('poisson_noise_gray_image_gray_noise.png', noisy_image)

    noisy_image = add_poisson_noise(image_alpha, SCALE, True, 2.5, 1.0)
    cv2.imwrite('poisson_noise_alpha_image_gray_noise.png', noisy_image)


椒盐噪音

椒盐噪音可能是图像在拍摄成像的过程中受到了非常强的干扰,从而导致图像上出现的一些过大或过小的值,如果是在灰度图上,那么就是白色或者黑色的噪点,就像是盐(白)和胡椒(黑)颗粒一样,所以称为椒盐噪音。

在这里插入图片描述

椒盐噪音

(终于来了一个有辨识度的噪音,椒盐噪音很容易认出来)

在下面代码中,我将椒盐的值设置为参数的形式,也就是说用户可以进行调节,不一定非得让盐=255,椒=0。

# -*- coding: utf-8 -*-
import cv2
import numpy as np
from util import get_noise_shape


def generate_salt_pepper_noise_mask(image,
                                    is_gray=False,
                                    noise_num_ratio=0.1,
                                    speckle_size=1.0,
                                    salt_ratio=0.5):
    """
    Generate a mask for salt pepper noise.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    is_gray: whether the noise is color or gray
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]
    speckle_size: speckle size of noise
    salt_ratio: ratio of salt number with respect to the total number of noise
        pixels

    Returns
    -------
    mask: mask for salt pepper noise. Positive values in mask is corresponding
        to position of salt noise, whereas negative values is corresponding to
        position of pepper noise.
    """
    if not 0.0 <= noise_num_ratio <= 1.0:
        raise ValueError('noise_num_ratio must between [0, 1]')

    if not 0.0 <= salt_ratio <= 1.0:
        raise ValueError('salt_ratio must between [0, 1]')

    # get noise num. Noise num is computed by noise_shape, which is affected
    # by speckle_size
    noise_shape = get_noise_shape(image, speckle_size)
    pixel_num = np.prod(noise_shape[0:2])
    noise_num = pixel_num * noise_num_ratio
    salt_num = int((noise_num * salt_ratio))
    pepper_num = int(np.round(noise_num * (1 - salt_ratio)))

    # get mask
    salt_index = []
    pepper_index = []
    if is_gray:
        mask = np.zeros(noise_shape[:2], dtype=np.int32)
        for shape in noise_shape[:2]:
            salt_index.append(np.random.randint(0, shape, salt_num))
            pepper_index.append(np.random.randint(0, shape, pepper_num))
        mask[tuple(salt_index)] = 1
        mask[tuple(pepper_index)] = -1
        if image.ndim == 3 and image.shape[2] > 3:
            mask = np.expand_dims(mask, axis=2).repeat(3, axis=2)
    else:
        mask = np.zeros(noise_shape, dtype=np.int32)
        for shape in noise_shape:
            salt_index.append(np.random.randint(0, shape, salt_num))
            pepper_index.append(np.random.randint(0, shape, pepper_num))
        mask[tuple(salt_index)] = 1
        mask[tuple(pepper_index)] = -1

    if speckle_size > 1.0:
        # mask should be converted to float type before using cv2.resize
        mask = mask.astype(np.float32)
        interp_type = np.random.choice(
            [cv2.INTER_CUBIC, cv2.INTER_LINEAR, cv2.INTER_NEAREST])
        mask = cv2.resize(mask, dsize=image.shape[:2], fx=None, fy=None,
                          interpolation=interp_type)
        mask = np.round(mask).astype(np.int32)
    return mask


def add_salt_pepper_noise(image,
                          is_gray=False,
                          noise_num_ratio=0.1,
                          speckle_size=1.0,
                          salt_value=255,
                          pepper_value=0,
                          salt_ratio=0.5):
    """
    Add salt and pepper nosie to image.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    is_gray: whether the noise is color or gray
    noise_num_ratio: ratio of noise number with respect to the total number of
        pixels, between [0, 1]
    speckle_size: speckle size of noise
    salt_value: value of salt noise
    pepper_value: value of pepper noise
    salt_ratio: ratio of salt number with respect to the total number of noise
        pixels

    Returns
    -------
    noisy_image: image after adding noise
    """
    mask = generate_salt_pepper_noise_mask(image,
                                           is_gray=is_gray,
                                           noise_num_ratio=noise_num_ratio,
                                           speckle_size=speckle_size,
                                           salt_ratio=salt_ratio)
    # add noise
    noisy_image = image.copy()
    if noisy_image.ndim == 3 and noisy_image.shape[2] > 3:
        noisy_image[:, :, :3][mask > 0] = salt_value
        noisy_image[:, :, :3][mask < 0] = pepper_value
    else:
        noisy_image[mask > 0] = salt_value
        noisy_image[mask < 0] = pepper_value
    noisy_image = np.squeeze(noisy_image)
    return noisy_image


if __name__ == '__main__':
    # read original image / gray scale image / image with alpha channel
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)  # 4 channel

    # generate noisy image
    noisy_image = add_salt_pepper_noise(image, False, 0.1, 5.0)
    cv2.imwrite('salt_pepper_noise_color_image_color_noise.png', noisy_image)

    noisy_image = add_salt_pepper_noise(image_gray, False, 0.1, 5.0)
    cv2.imwrite('salt_pepper_noise_gray_image_color_noise.png', noisy_image)

    noisy_image = add_salt_pepper_noise(image_alpha, False, 0.1, 5.0)
    cv2.imwrite('salt_pepper_noise_alpha_image_color_noise.png', noisy_image)

    noisy_image = add_salt_pepper_noise(image, True, 0.1, 1.5)
    cv2.imwrite('salt_pepper_noise_color_image_gray_noise.png', noisy_image)

    noisy_image = add_salt_pepper_noise(image_gray, True, 0.1, 1.5)
    cv2.imwrite('salt_pepper_noise_gray_image_gray_noise.png', noisy_image)

    noisy_image = add_salt_pepper_noise(image_alpha, True, 0.1, 1.5)
    cv2.imwrite('salt_pepper_noise_alpha_image_gray_noise.png', noisy_image)


模糊


高斯模糊

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    blur_image = cv2.GaussianBlur(image, ksize=(5, 5), sigmaX=2, sigmaY=2)

    cv2.imshow('original_image', image)
    cv2.imshow('gaussian_blur_image', blur_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


均值模糊

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    blur_image = cv2.boxFilter(image, ddepth=-1, ksize=(5, 7))

    cv2.imshow('original_image', image)
    cv2.imshow('box_blur_image', blur_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


运动模糊

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np


def motion_blur(image, kernel_length, angle):
    """
    Apply motion blur to image.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    kernel_length: motion trajectory length of blur kernel
    angle: motion trajectory angle

    Returns
    -------
    blur_image: image after blurring
    """
    if not (0 <= angle <= 180):
        raise ValueError('angle should between [0, 180]')

    # preprocess angle and get step along angle direction
    horizontal_flip = False
    if angle > 90:
        angle = 180 - angle
        horizontal_flip = True

    angle = angle / 180 * np.pi
    if 0 <= angle <= np.pi / 4:
        step = 1.0 / np.cos(angle)
    else:
        step = 1.0 / np.sin(angle)
    cos_angle = np.cos(angle)
    sin_angle = np.sin(angle)

    # get coordinates
    x, y = [], []
    for i in range(kernel_length):
        radius = i * step
        x.append(radius * cos_angle)
        y.append(radius * sin_angle)
    x, y = np.round(x).astype(np.int32), np.round(y).astype(np.int32)

    # make kernel
    x_ksize, y_ksize = np.max(x) + 1, np.max(y) + 1
    blur_kernel = np.zeros((y_ksize, x_ksize), np.float32)
    blur_kernel[y, x] = 1
    blur_kernel = blur_kernel / np.sum(blur_kernel)

    # flip kernel. Vertical flip is surely to be apllied because y-axis of
    # image is downside, opposite to the normal axis system. Horizontal flip
    # applied according to the value of 'horizontal_flip'
    blur_kernel = blur_kernel[::-1, :]
    if horizontal_flip:
        blur_kernel = blur_kernel[:, ::-1]

    # use blur kernel to filter image to get motion blur effect
    blur_image = cv2.filter2D(image, ddepth=-1, kernel=blur_kernel)
    return blur_image


if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    blur_image = motion_blur(image, kernel_length=30, angle=45)

    cv2.imshow('original_image', image)
    cv2.imshow('motion_blur_image', blur_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

中值滤波

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np

if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    blur_image = cv2.medianBlur(image, ksize=7)

    cv2.imshow('original_image', image)
    cv2.imshow('median_blur_image', blur_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


粒状模糊

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np
from util import uniform_random


def grainy_blur(image, radius):
    """
    Apply grainy blur to image.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    radius: radius for jittering the pixels

    Returns
    -------
    blur_image: image after blurring
    """
    if not isinstance(radius, (tuple, list)):
        radius = (radius, radius)
    x_radius, y_radius = radius

    # build meshgrid coordinates
    height, width = image.shape[:2]
    x, y = np.arange(width), np.arange(height)
    x, y = np.meshgrid(x, y)
    x, y = x.astype(np.float32), y.astype(np.float32)

    # get jittered coordinates
    x += uniform_random(low=-x_radius, high=x_radius, shape=x.shape)
    y += uniform_random(low=-y_radius, high=y_radius, shape=y.shape)

    # shrink coordinates
    x, y = np.round(x).astype(np.int32), np.round(y).astype(np.int32)
    x[x < 0] = 0
    y[y < 0] = 0
    x[x >= width] = width - 1
    y[y >= height] = height - 1

    # remap to get blur image
    blur_image = image[y, x, ...]

    return blur_image


if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    blur_image = grainy_blur(image, radius=5)

    cv2.imshow('original_image', image)
    cv2.imshow('grainy_blur_image', blur_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


jpeg压缩

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np


def jpeg_compression(image, quality_factor):
    """
    Apply jpeg compression to image without saving it to disk.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    quality_factor: jpeg quality factor, between [0, 1]. Higher value means
        higher quality image

    Returns
    -------
    jpeg_image: jpeg compressed image
    """
    compression_factor = int(quality_factor)
    compression_param = [cv2.IMWRITE_JPEG_QUALITY, compression_factor]
    image_encode = cv2.imencode('.jpg', image, compression_param)[1]
    jpeg_image = cv2.imdecode(image_encode, -1)
    return jpeg_image


if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    compressed_image = jpeg_compression(image, quality_factor=10)

    cv2.imshow('original_image', image)
    cv2.imshow('jpeg_compressed_image', compressed_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


马赛克

在这里插入图片描述

# -*- coding: utf-8 -*-
import cv2
import numpy as np


def mosaic(image, block_size, bbox=None):
    """
    Set the bounding box region of image to mosaic.

    Parameters
    ----------
    image: image data read by opencv, shape is [H, W, C]
    block_size:
    bbox: mosaic region, defined as [xmin, ymin, xmax, ymax]. Default is the
        full image

    Returns
    -------
    mosaic_image: image with mosaic
    """
    if not isinstance(block_size, (tuple, list)):
        block_size = (block_size, block_size)
    x_step, y_step = block_size

    # preprocess bbox
    height, width = image.shape[:2]
    if bbox is None:
        xmin, ymin, xmax, ymax = 0, 0, width, height
    else:
        xmin, ymin, xmax, ymax = bbox

    xmin, ymin = max(xmin, 0), max(ymin, 0)
    xmax, ymax = min(xmax, width), min(ymax, height)

    # get random index for mosaic region
    x_jitter = np.arange(xmin, xmax, x_step)
    y_jitter = np.arange(ymin, ymax, y_step)
    x_jitter, y_jitter = np.meshgrid(x_jitter, y_jitter)

    x_jitter += np.random.randint(0, x_step, x_jitter.shape)
    y_jitter += np.random.randint(0, y_step, y_jitter.shape)

    x_jitter[x_jitter >= width] = width - 1
    y_jitter[y_jitter >= height] = height - 1

    # generate mosaic region by cv2.resize function
    mosaic_region = image[y_jitter, x_jitter, ...]
    mosaic_region = cv2.resize(mosaic_region, dsize=(xmax - xmin, ymax - ymin),
                               interpolation=cv2.INTER_NEAREST)

    mosaic_image = image.copy()
    mosaic_image[ymin:ymax, xmin:xmax, ...] = mosaic_region

    return mosaic_image


if __name__ == '__main__':
    # read image
    image = cv2.imread('lena512color.tiff')
    image_gray = cv2.imread('lena512color.tiff', cv2.IMREAD_GRAYSCALE)

    alpha = np.ones((*image.shape[:2], 1), dtype=np.uint8) * 255
    image_alpha = np.concatenate((image, alpha), axis=2)

    # the following image can be changed to image_gray or image_alpha
    mosaic_image = mosaic(image, block_size=10, bbox=[100, 100, 400, 400])

    cv2.imshow('original_image', image)
    cv2.imshow('mosaic_image', mosaic_image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

重采样

。。。

猜你喜欢

转载自blog.csdn.net/bby1987/article/details/109634254