这个Python图像模糊函数有什么问题?
编辑: 感谢Howard的帮助,我已经修正了代码,现在看起来可以正常工作了。
编辑2: 我更新了代码,加入了原本想要的垂直模糊效果。这里是不同设置下的效果对比:模糊效果对比图.jpg
关于模糊操作的另一个参考(Java):初学者的模糊教程
原始帖子:
我正在学习基本的图像处理,想要在Python中复制这个简单的模糊方法(在“重用结果”下的第二个函数BlurHorizontal)。我知道PIL里已经有模糊函数了,但我想自己尝试一下基本的像素操作。
这个函数应该接收一张源图像,然后根据一定的半径计算RGB像素值的平均值,并把处理后的图像写入一个新文件。我的问题是,我得到的很多像素的平均值完全错误(例如,在某些区域出现明亮的绿色线条,而不是红色)。
在模糊半径为2的情况下,平均方法会对输入像素周围的5个像素的RGB值进行累加。它使用一个“滑动窗口”来保持一个运行总和,减去即将离开的像素(左侧)并添加新的进入像素(窗口的右侧)。这里解释了模糊方法
示例:模糊测试图像输出.jpg
有没有人知道我哪里出错了?我不明白为什么图像的某些部分模糊得很干净,而其他区域却充满了与周围区域完全无关的颜色。
谢谢你的帮助。
修复后的工作代码(感谢Howard)
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 个回答
我稍微修改了一下你的代码,觉得可以分享一下。我需要一个自定义模糊的功能,它要满足两个条件: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)
我想你在这行代码上遇到了问题:
for rads in range(-radius, radius):
这行代码的运行范围只到半径减一(也就是说最后一个数字不包括在内)。你需要在第二个范围的参数上加一。
更新:在这行代码中还有一个小问题:
if (co-radius-1) > 0:
应该改成:
if (co-radius-1) >= 0: