В настоящее время я работаю над проектом компьютерного зрения и хотел изучить предварительную обработку изображений, чтобы улучшить модели машинного обучения, которые я планирую создать. Предварительная обработка изображения включает в себя применение фильтров изображения к изображению. В этой статье будет сравниваться ряд наиболее известных фильтров изображений.

Фильтры изображений можно использовать для уменьшения количества шума в изображении и улучшения краев изображения. На изображении могут присутствовать два типа шума: спекл-шум и шум соли и перца. Спек-шум - это шум, который возникает во время получения изображения, в то время как шум соли и перца (который относится к редко встречающимся белым и черным пикселям) вызван внезапными помехами в сигнале изображения. Улучшение краев изображения может помочь модели определить особенности изображения.

Этап предварительной обработки изображения может повысить точность моделей машинного обучения. Предварительно обработанные изображения могут помочь базовой модели достичь более высокой точности по сравнению с более сложной моделью, обученной на изображениях, которые не были предварительно обработаны. Для Python пакеты Open-CV и PIL позволяют применять несколько цифровых фильтров. Применение цифрового фильтра включает свертку изображения с ядром (небольшой матрицей). Ядро - это квадратная матрица размером n x n, где n - нечетное число. Ядро зависит от цифрового фильтра. На рисунке 1 показано ядро, которое используется для среднего фильтра 3 x 3. Изображение из набора данных KDEF (которое можно найти здесь: http://kdef.se/) будет использоваться для примеров цифрового фильтра.

1. Средний фильтр

Средний фильтр используется для размытия изображения с целью удаления шума. Он включает определение среднего значения пикселей в ядре n x n. Затем интенсивность пикселей центрального элемента заменяется средним значением. Это устраняет часть шума на изображении и сглаживает края изображения. Функцию размытия из библиотеки Open-CV можно использовать для применения среднего фильтра к изображению.

При работе с цветными изображениями сначала необходимо преобразовать из RGB в HSV, поскольку размеры RGB зависят друг от друга, тогда как три измерения в HSV не зависят друг от друга (это позволяет нам применять фильтры к каждому из трех измерений. раздельно.)

Ниже представлена ​​реализация среднего фильтра на языке Python:

import numpy as np
import cv2
from matplotlib import pyplot as plt
from PIL import Image, ImageFilter
%matplotlib inline
image = cv2.imread('AM04NES.JPG') # reads the image
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) # convert to HSV
figure_size = 9 # the dimension of the x and y axis of the kernal.
new_image = cv2.blur(image,(figure_size, figure_size))
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Mean filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 2 показано, что, хотя некоторые из спекл-шума были уменьшены, на изображении теперь присутствует ряд артефактов, которых раньше не было. Мы можем проверить, возникают ли какие-либо артефакты, когда средний фильтр применяется к полутоновому изображению.

# The image will first be converted to grayscale
image2 = cv2.cvtColor(image, cv2.COLOR_HSV2BGR)
image2 = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)
figure_size = 9
new_image = cv2.blur(image2,(figure_size, figure_size))
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Mean filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 3 показано, что фильтрация средних значений удаляет часть шума и не создает артефактов для изображения в градациях серого. Однако некоторые детали были потеряны.

2. Гауссов фильтр

Фильтр Гаусса похож на фильтр среднего значения, однако он включает в себя средневзвешенное значение окружающих пикселей и имеет параметр сигма. Ядро представляет собой дискретную аппроксимацию гауссова распределения. Хотя фильтр Гаусса размывает края изображения (как и средний фильтр), он лучше сохраняет края, чем средний фильтр аналогичного размера. Функция GaussianBlur из пакета Open-CV может использоваться для реализации фильтра Гаусса. Функция позволяет указать форму ядра. Вы также можете указать стандартное отклонение для направлений x и y отдельно. Если указано только одно значение сигмы, оно считается значением сигмы для обоих направлений x и y.

new_image = cv2.GaussianBlur(image, (figure_size, figure_size),0)
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Gaussian Filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 4 показано, что фильтр Гаусса лучше справляется с сохранением краев изображения по сравнению со средним фильтром, однако он также создает артефакты на цветном изображении. Теперь мы можем проверить, создает ли фильтр Гаусса артефакты на изображении в градациях серого.

new_image_gauss = cv2.GaussianBlur(image2, (figure_size, figure_size),0)
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(new_image_gauss, cmap='gray'),plt.title('Gaussian Filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 5 показано, что фильтр Гаусса 9 x 9 не создает артефактов при применении к изображению в градациях серого. Фильтр может сохранять больше деталей, чем средний фильтр 9 x 9, и удалять некоторый шум.

3. Медианный фильтр

Медианный фильтр вычисляет медианное значение яркости пикселей, окружающих центральный пиксель, в ядре n x n. Затем медиана заменяет интенсивность центрального пикселя. Медианный фильтр лучше удаляет шум соли и перца, чем средний и гауссовский фильтры. Медианный фильтр сохраняет края изображения, но не устраняет спекл-шум. Функция medianBlur из библиотеки Open-CV может использоваться для реализации медианного фильтра.

new_image = cv2.medianBlur(image, figure_size)
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(cv2.cvtColor(image, cv2.COLOR_HSV2RGB)),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_HSV2RGB)),plt.title('Median Filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 6 показано, что медианный фильтр может сохранять края изображения, удаляя при этом шум соли и перца. В отличие от среднего и гауссовского фильтра, медианный фильтр не создает артефактов на цветном изображении. Медианный фильтр теперь будет применен к изображению в оттенках серого.

new_image = cv2.medianBlur(image2, figure_size)
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Median Filter')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 7 показано, что медианный фильтр 9 x 9 может удалить часть шума соли и перца, сохраняя при этом края изображения.

Другие фильтры:

Вот еще несколько фильтров, которые можно использовать для предварительной обработки изображений:

Консервативный фильтр

Консервативный фильтр используется для удаления шума из-за соли и перца. Определяет минимальную и максимальную интенсивность в окрестности пикселя. Если интенсивность центрального пикселя больше максимального значения, оно заменяется максимальным значением. Если оно меньше минимального значения, оно заменяется минимальным значением. Консервативный фильтр сохраняет края, но не удаляет спекл-шум.

Следующий код можно использовать для определения консервативного фильтра:

# first a conservative filter for grayscale images will be defined.
def conservative_smoothing_gray(data, filter_size):
temp = []
    
    indexer = filter_size // 2
    
    new_image = data.copy()
    
    nrow, ncol = data.shape
    
    for i in range(nrow):
        
        for j in range(ncol):
            
            for k in range(i-indexer, i+indexer+1):
                
                for m in range(j-indexer, j+indexer+1):
                    
                    if (k > -1) and (k < nrow):
                        
                        if (m > -1) and (m < ncol):
                            
                            temp.append(data[k,m])
                            
            temp.remove(data[i,j])
            
            
            max_value = max(temp)
            
            min_value = min(temp)
            
            if data[i,j] > max_value:
                
                new_image[i,j] = max_value
            
            elif data[i,j] < min_value:
                
                new_image[i,j] = min_value
            
            temp =[]
    
    return new_image.copy()

Теперь консервативный фильтр можно применить к полутоновому изображению:

new_image = conservative_smoothing_gray(image2,5)
plt.figure(figsize=(11,6))
plt.subplot(121), plt.imshow(image2, cmap='gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(new_image, cmap='gray'),plt.title('Conservative Smoothing')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 9 показано, что консервативный сглаживающий фильтр смог убрать некоторый шум соли и перца. Это также говорит о том, что фильтр не может удалить столько шума с солью и перцем, как средний фильтр (хотя он сохраняет больше деталей).

Лапласовский фильтр

Лапласиан изображения выделяет области с быстрыми изменениями интенсивности и, таким образом, может использоваться для обнаружения краев. Если мы позволим I (x, y) представлять интенсивности изображения, то лапласиан изображения будет задан по следующей формуле:

Дискретное приближение лапласиана в конкретном пикселе можно определить, взяв средневзвешенное значение интенсивностей пикселей в небольшой окрестности пикселя. На рисунке 10 показаны два ядра, которые представляют два разных способа аппроксимации лапласиана.

Поскольку фильтр Лапласа обнаруживает края изображения, его можно использовать вместе с фильтром Гаусса, чтобы сначала удалить спекл-шум, а затем выделить края изображения. Этот метод называется лапальцианом гауссовой фильтрации. Функция «Лапласиан» из библиотеки Open-CV может использоваться для поиска лапласиана изображения.

new_image = cv2.Laplacian(image2,cv2.CV_64F)
plt.figure(figsize=(11,6))
plt.subplot(131), plt.imshow(image2, cmap='gray'),plt.title('Original')
plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(new_image, cmap='gray'),plt.title('Laplacian')
plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(image2 + new_image, cmap='gray'),plt.title('Resulting image')
plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 11 показано, что, хотя добавление лапласиана изображения к исходному изображению может улучшить края, некоторые из шумов также усиливаются.

Частотный фильтр

При применении частотных фильтров к изображению важно сначала преобразовать изображение в представление изображения в частотной области. Преобразование Фурье (которое разлагает функцию на компоненты синуса и косинуса) может применяться к изображению, чтобы получить его представление в частотной области. Причина, по которой мы заинтересованы в представлении изображения в частотной области, заключается в том, что применять частотные фильтры к изображению в частотной области дешевле, чем применять фильтры в пространственной области. Это связано с тем, что каждый пиксель в представлении частотной области соответствует частоте, а не местоположению изображения.

Фильтры нижних частот и фильтры верхних частот являются частотными фильтрами. Фильтры нижних частот сохраняют самые низкие частоты (которые ниже порогового значения), что означает, что они размывают края и удаляют спекл-шум из изображения в пространственной области. Фильтр высоких частот сохраняет высокие частоты, что означает, что он сохраняет края. Функция «dft» определяет дискретное преобразование Фурье изображения. Для изображения N x N двумерное дискретное преобразование Фурье задается следующим образом:

где f - значение изображения в пространственной области, а F - в частотной области. Ниже приводится формула обратного дискретного преобразования Фурье (которое преобразует изображение из его частотной области в пространственную):

После применения частотного фильтра к изображению можно использовать обратное преобразование Фурье для преобразования изображения обратно в пространственную область. Теперь будет дана реализация фильтра нижних частот на Python:

dft = cv2.dft(np.float32(image2),flags = cv2.DFT_COMPLEX_OUTPUT)
# shift the zero-frequncy component to the center of the spectrum
dft_shift = np.fft.fftshift(dft)
# save image of the image in the fourier domain.
magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))
# plot both images
plt.figure(figsize=(11,6))
plt.subplot(121),plt.imshow(image2, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

rows, cols = image2.shape
crow,ccol = rows//2 , cols//2
# create a mask first, center square is 1, remaining all zeros
mask = np.zeros((rows,cols,2),np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1
# apply mask and inverse DFT
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])
# plot both images
plt.figure(figsize=(11,6))
plt.subplot(121),plt.imshow(image2, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Low Pass Filter'), plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 13 показано, что приличное количество деталей было потеряно, однако часть спекл-шума была удалена.

Удаление спеклов Crimmins

Дополнительный алгоритм отбраковки Crimmins используется для удаления спекл-шума и сглаживания краев. Это также снижает интенсивность шума соли и перца. Алгоритм сравнивает интенсивность пикселя изображения с интенсивностью его 8 соседей. Алгоритм учитывает 4 набора соседей (NS, EW, NW-SE, NE-SW). Пусть a, b, c будут тремя последовательными пикселями (например, из ES ). Тогда алгоритм такой:

  1. Для каждой итерации:
    a) Регулировка темных пикселей: для каждого из четырех направлений
    1) Обработка всего изображения с помощью: if ab + 2, затем b = b + 1
    2) Обработать все изображение с помощью: if a ›b и b ≤ c, затем b = b + 1
    3 ) Обработать все изображение с помощью: if c ›b и b ≤ a, то b = b + 1
    4) Обработать все изображение с помощью: если cb + 2, то b = b + 1
    b) Регулировка светлых пикселей: для каждого из четырех направлений
    1) Обработка всего изображения с помощью: if ab - 2, затем b = b - 1
    2) Обработать все изображение: если a ‹b и b ≥ c, то b = b - 1
    3) Обработка всего изображения: если c ‹b и b ≥ a, то b = b - 1
    4) Обработать все изображение с помощью: if c ≤ b - 2, затем b = b - 1

Реализацию дополнительного алгоритма отбраковки в Python можно найти здесь: https://github.com/m4nv1r/medium_articles/blob/master/Image_Filters_in_Python.ipynb

На рисунке 14 показаны результаты применения фильтра Crimmins Speckle Removal к ​​изображению. Часть спекл-шума была удалена, но некоторые края были размыты.

Фильтр нерезкости

Фильтр «Нерезкость» можно использовать для улучшения краев изображения. Функция ImageFilter.Unsharpmask из пакета PIL применяет фильтр нерезкости к изображению (изображение сначала необходимо преобразовать в объект изображения PIL). Функция ImageFilter.Unsharpmask имеет три параметра. Параметр «radius» указывает, сколько соседних пикселей по краям будет затронуто. Параметр «процент» указывает, насколько темнее или светлее становятся края. Третий параметр «порог» определяет, как далеко должны быть друг от друга соседние тональные значения, прежде чем фильтр что-нибудь сделает.

image = Image.fromarray(image.astype('uint8'))
new_image = image.filter(ImageFilter.UnsharpMask(radius=2, percent=150))
plt.subplot(121),plt.imshow(image, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(new_image, cmap = 'gray')
plt.title('Unsharp Filter'), plt.xticks([]), plt.yticks([])
plt.show()

На рисунке 15 показаны результаты фильтра нерезкости. Хотя края изображения были улучшены, некоторые шумы также были улучшены.

Заключение

Всегда существует компромисс между удалением шума и сохранением краев изображения. Чтобы удалить спекл-шум на изображении, необходимо применить фильтр размытия, который, в свою очередь, размывает края изображения. Если вы хотите сохранить края изображения, единственный шум, который вы можете удалить, - это шум соли и перца. Блокнот Jupyter со всем кодом, использованным для этой статьи, можно найти здесь: https://github.com/m4nv1r/medium_articles/blob/master/Image_Filters_in_Python.ipynb