Почему яркость изображения уменьшилась после применения гауссовского фильтра? - PullRequest
1 голос
/ 02 апреля 2020

Я только что узнал, как применить фильтр Гаусса с нуля к изображению в градациях серого в python из этого блога:
http://www.adeveloperdiary.com/data-science/computer-vision/applying-gaussian-smoothing-to-an-image-using-python-from-scratch/

Теперь я хочу применить Фильтр Гаусса для 3-канального (RGB) изображения.
Для этого я реализовал код, но на выходе получаю размытое тусклое изображение с очень низкой яркостью, Кроме того, края изображения не размыты должным образом.

Вот мой код:

# import libraries
import cv2
import numpy as np
import os
import matplotlib.pyplot as plt
%matplotlib inline
import cv2

# loading image
img_orig = cv2.imread('home.jpg')

# convert GBR image to RGB image
img_orig = cv2.cvtColor(img_orig, cv2.COLOR_BGR2RGB)

# Gaussian function
def dnorm(x, mu, sd):
    return 1 / (np.sqrt(2 * np.pi) * sd) * np.exp(-((x-mu)/sd)** 2 / 2)

# function for making gaussian kernel
def gaussian_kernel(kernel_size, mu = 0):
    # initializing mu and SD
    sd = np.sqrt(kernel_size)

    # creating 1D kernel
    kernel_1D = np.linspace(-(kernel_size // 2), kernel_size // 2, kernel_size)

    # normalizing 1D kernel
    for i in range(kernel_size):
        kernel_1D[i] = dnorm(kernel_1D[i], mu, sd)

    # creating 2D kernel
    kernel_2D = np.outer(kernel_1D, kernel_1D)
    kernel_2D /= kernel_2D.max()

    return kernel_2D

Вот как выглядит ядро ​​11 X 11 как:
enter image description here


# Covolution function with zero padding
def convolution(image, kernel):
    # find row and column of 3 channel (RGB) image
    img_row, img_col, img_channel = image.shape

    kernel_size = kernel.shape[0]
    padding_width = (kernel_size - 1) // 2

    #initialize output image
    output = np.zeros(image.shape, dtype = np.uint8)

    # initialize padded image with zeros
    padded_img = np.zeros((img_row + 2*padding_width, img_col + 2*padding_width, img_channel), dtype = np.uint8)

    # copy orignal image inside padded image
    padded_img[padding_width : padding_width + img_row, padding_width : padding_width + img_col] = image

    # average pixel values using gaussian kernel
    for i in range(img_row):
        for j in range(img_col):
            # average each pixel's R channel value
            output[i, j, 0] = np.sum(padded_img[i : i+kernel_size, j : j+kernel_size, 0] * kernel) // (kernel_size * kernel_size)

            # average each pixel's G channel value
            output[i, j, 1] = np.sum(padded_img[i : i+kernel_size, j : j+kernel_size, 1] * kernel) // (kernel_size * kernel_size)

            # average each pixel's B channel value
            output[i, j, 2] = np.sum(padded_img[i : i+kernel_size, j : j+kernel_size, 2] * kernel) // (kernel_size * kernel_size)

    return output

def gaussian_filter(image, kernel_size = 3):
    # initialize mu
    mu = 0

    # create gaussian kernel
    kernel = gaussian_kernel(kernel_size, mu)

    # apply convolution to image
    conv_img = convolution(image, kernel)

    # return blurred image
    return conv_img


Тестовый код для фильтра Гаусса:

plt.figure(figsize = (7, 5))
print('orignal image')
plt.imshow(img_orig)
plt.show()

plt.figure(figsize = (7, 5))
print('blurred image')
plt.imshow(gaussian_filter(img_orig, 11))
plt.show()

Выход:
enter image description here


Сравнение с openCV GaussianBlur:

print('openCV blurred image')
plt.imshow(cv2.GaussianBlur(img_orig, (11,11), 0))
plt.show()


Выход:
enter image description here


Мои вопросы:
1) почему я получаю унылое изображение в качестве вывода.
2) НЕПРАВИЛЬНА ли приведенная выше реализация фильтра Гаусса для изображения RGB? Если это неправильно, как я могу исправить это?
3) Почему края не размыты должным образом (см. Тень blacki sh по краям)?
4) Приведенная выше реализация фильтра Гаусса принимает очень долгое время для выполнения по сравнению с OpenCV GaussianBlur. Как я могу сделать это эффективным по времени?

1 Ответ

2 голосов
/ 04 апреля 2020

Неправильны две вещи, из-за которых интенсивность изображения не сохраняется: сначала вы нормализуете ядро ​​путем деления на его максимальное значение, а затем в свертке делите на число выборок в ядре. Вместо этих двух нормализаций нормализуйте только один раз, когда вы создаете ядро, путем деления на сумму значений ядра. Это делает сумму весов ядра равной 1 и заставляет свертку сохранять среднюю интенсивность изображения. Обратите внимание, что свертка вычисляет локальное средневзвешенное значение; в средневзвешенном весе нам нужно добавить веса к 1, чтобы избежать смещения.

Темные края вызваны заполнением: вы дополняете нулями (черным), который смешивается со значениями по краям изображение в свертке. OpenCV, вероятно, использует другое граничное условие или заполнение изображения. Типичные варианты включают зеркальное отображение значений или просто расширение значений ребер.

Наконец, основная причина, по которой ваш код работает медленно, заключается в том, что вы используете al oop in Python. Python - интерпретируемый язык и, следовательно, медленный. Вы могли бы использовать Numba для ускорения циклов (это компилятор для Python), или просто использовать свертку в NumPy, которая реализована на скомпилированном языке.

Другой Причина, по которой ваш код медленный (что не будет иметь большого значения, пока вы не исправите первое), заключается в том, что вы не используете отделимость по Гауссу. Вы строите двумерный гауссиан, умножая два одномерных гауссовских, но вместо этого вы можете применить два одномерных сверток в последовательности. Для вашего примера ядра 11x11 вычислительные затраты уменьшены с 11 * 11 = 121 умножения и сложения до 11 + 11 = 22 умножения и сложения. Чем больше ядро, тем лучше прирост скорости.

...