Что не так с этой функцией размытия изображения на питоне? - PullRequest
5 голосов
/ 03 апреля 2011

РЕДАКТИРОВАТЬ : Благодаря Говарду я исправил здесь код, и он, кажется, работает сейчас.

РЕДАКТИРОВАТЬ * : я обновил кодвключить вертикальное размытие, как первоначально предполагалось.Результирующий пример вывода с различными настройками: Сравнение размытия images.jpg

Еще один справочник по операциям размытия (Java): Размытие для начинающих


оригинальный пост:

Я пытаюсь узнать об основной обработке изображений и продублировать этот простой Blur метод (вторая функция BlurHor Horizontal в разделе «Повторное использование результатов») в Python.Я знаю, что в PIL уже есть функции размытия, но я хочу сам опробовать основные пиксельные операции.

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

При радиусе размытия 2 метод усреднения складываетсязначения RGB для 5 пикселей с центром на входном пикселе.Он использует «скользящее окно» для сохранения промежуточного итога, вычитая исходящий пиксель (левая сторона) и добавляя новый входящий пиксель (правая сторона окна). Метод размытия, описанный здесь

Пример: Вывод тестового изображения размытия. Jpg

Есть идеи, где я ошибся?Я не уверен, почему некоторые части изображения размываются чисто, в то время как другие области заполнены цветами, совершенно не связанными с окружающими областями.

Спасибо за вашу помощь.

ФИКСИРОВАННЫЙ РАБОЧИЙ Код (Спасибо, Говард)

import Image, numpy, ImageFilter
img = Image.open('testimage.jpg')

imgArr = numpy.asarray(img) # readonly

# blur radius in pixels
radius = 2

# blur window length in pixels
windowLen = radius*2+1

# columns (x) image width in pixels
imgWidth = imgArr.shape[1]

# rows (y) image height in pixels
imgHeight = imgArr.shape[0]

#simple box/window blur
def doblur(imgArr):
    # create array for processed image based on input image dimensions
    imgB = numpy.zeros((imgHeight,imgWidth,3),numpy.uint8)
    imgC = numpy.zeros((imgHeight,imgWidth,3),numpy.uint8)

    # blur horizontal row by row
    for ro in range(imgHeight):
        # RGB color values
        totalR = 0
        totalG = 0
        totalB = 0

        # calculate blurred value of first pixel in each row
        for rads in range(-radius, radius+1):
            if (rads) >= 0 and (rads) <= imgWidth-1:
                totalR += imgArr[ro,rads][0]/windowLen
                totalG += imgArr[ro,rads][1]/windowLen
                totalB += imgArr[ro,rads][2]/windowLen

        imgB[ro,0] = [totalR,totalG,totalB]

        # calculate blurred value of the rest of the row based on
        # unweighted average of surrounding pixels within blur radius
        # using sliding window totals (add incoming, subtract outgoing pixels)
        for co in range(1,imgWidth):
            if (co-radius-1) >= 0:
                totalR -= imgArr[ro,co-radius-1][0]/windowLen
                totalG -= imgArr[ro,co-radius-1][1]/windowLen
                totalB -= imgArr[ro,co-radius-1][2]/windowLen
            if (co+radius) <= imgWidth-1:
                totalR += imgArr[ro,co+radius][0]/windowLen
                totalG += imgArr[ro,co+radius][1]/windowLen
                totalB += imgArr[ro,co+radius][2]/windowLen

            # put average color value into imgB pixel

            imgB[ro,co] = [totalR,totalG,totalB]

    # blur vertical

    for co in range(imgWidth):
        totalR = 0
        totalG = 0
        totalB = 0

        for rads in range(-radius, radius+1):
            if (rads) >= 0 and (rads) <= imgHeight-1:
                totalR += imgB[rads,co][0]/windowLen
                totalG += imgB[rads,co][1]/windowLen
                totalB += imgB[rads,co][2]/windowLen

        imgC[0,co] = [totalR,totalG,totalB]

        for ro in range(1,imgHeight):
            if (ro-radius-1) >= 0:
                totalR -= imgB[ro-radius-1,co][0]/windowLen
                totalG -= imgB[ro-radius-1,co][1]/windowLen
                totalB -= imgB[ro-radius-1,co][2]/windowLen
            if (ro+radius) <= imgHeight-1:
                totalR += imgB[ro+radius,co][0]/windowLen
                totalG += imgB[ro+radius,co][1]/windowLen
                totalB += imgB[ro+radius,co][2]/windowLen

            imgC[ro,co] = [totalR,totalG,totalB]

    return imgC

# number of times to run blur operation
blurPasses = 3

# temporary image array for multiple passes
imgTmp = imgArr

for k in range(blurPasses):
    imgTmp = doblur(imgTmp)
    print "pass #",k,"done."

imgOut = Image.fromarray(numpy.uint8(imgTmp))

imgOut.save('testimage-processed.png', 'PNG')

Ответы [ 2 ]

2 голосов
/ 03 апреля 2011

Полагаю, у вас есть проблема со строкой

for rads in range(-radius, radius):

, который работает только с радиусом-1 (диапазон исключает последний). Добавьте один ко второму аргументу диапазона.

Обновление: В строке есть еще один маленький вопрос

if (co-radius-1) > 0:

, который должен быть

if (co-radius-1) >= 0:
0 голосов
/ 23 декабря 2013

Я немного изменил / реорганизовал ваш код и решил поделиться.Мне нужно было что-то сделать для пользовательского размытия, которое бы: 1) работало с массивом данных и 2) только обтекание по горизонтали, а не по вертикали.Как отмечает TODO, я думаю о дальнейшем рефакторинге, чтобы он мог выполнять частичное смешивание пикселей (то есть 0,5).Надеюсь, это кому-нибудь поможет:

def blur_image(image_data, blur_horizontal=True, blur_vertical=True, height=256, width=256, radius=1):
    #TODO: Modify to support partial pixel blending

    # blur window length in pixels
    blur_window = radius*2+1

    out_image_data = image_data

    # blur horizontal row by row, and wrap around edges
    if blur_horizontal:
        for row in range(height):
            for column in range(0, width):
                total_red = 0
                total_green = 0
                total_blue = 0

                for rads in range(-radius, radius+1):
                    pixel = (row*width) + ((column+rads) % width)
                    total_red += image_data[pixel][0]/blur_window
                    total_green += image_data[pixel][1]/blur_window
                    total_blue += image_data[pixel][2]/blur_window

                out_image_data[row*width + column] = (total_red, total_green, total_blue, 255)
        image_data = out_image_data

    # blur vertical, but no wrapping
    if blur_vertical:
        for column in range(width):
            for row in range(0, height):
                total_red = 0
                total_green = 0
                total_blue = 0

                blur_window = 0
                for rads in range(-radius, radius+1):
                    if rads in range(0, height):
                        blur_window += 1

                for rads in range(-radius, radius+1):
                    row_mod = row+rads
                    if row_mod in range(0, height):
                        pixel = (row_mod*width) + column
                        total_red += image_data[pixel][0]/blur_window
                        total_green += image_data[pixel][1]/blur_window
                        total_blue += image_data[pixel][2]/blur_window

                out_image_data[row*width + column] = (total_red, total_green, total_blue, 255)
        image_data = out_image_data

    return image_data

Вы можете использовать его, когда у вас уже есть изображение в массиве пикселей RGBA, а затем выполнить:

image_data = blur_image(image_data, height=height, width=width, radius=2)

im = Image.new('RGB', (width, height))
im.putdata(image_data)
...