1

I have an image which consists of two arbitrarily placed black 1px "blobs" on a white canvas 100px by 200px. I'm trying to "blur these blobs by turning some of the neighbouring pixels (within a radius of 10px of each blob) black also. I've put together the following code, but am not quite sure of the next step..

import numpy as np
from PIL import Image 
from scipy import ndimage
from matplotlib import pyplot
from matplotlib import cm
from scipy.misc import imsave

im = Image.open("test.png")
pix = np.asarray(im)
new_pix = np.copy(pix[:,:,0]) # need this otherwise can't write to the pix array.
pix_to_enlarge = np.where(new_pix != 255)
pixels_to_enlarge_by = 10
i=0
for each_pixel in pix_to_enlarge[0]: # this cycles through each non-white pixel
    for y in range(len(new_pix)): # this looks across the length (down) the page
        for x in new_pix[y]: # this looks across the x-axis for each y step
            radius = pixels_to_enlarge_by**2

so essentially I've found the locations of the non-white pixels in the variable pixels_to_enlarge_by. What I'm trying (and so far failing to do) is to select the surrounding pixels (within 10px) and change them to black also. Any ideas?

user714852
  • 2,054
  • 4
  • 30
  • 52

2 Answers2

5

In one way or the other you need to look at each pixel and then figure out how much of that content you want to distribute to each other pixel in your picture, for example by comparing the distance between the two.

Here's how it's done with numpy. If you want to do it manually then that may in any case help as a starting point. It's called convolution. 2d convolution using python and numpy

Here's a good starting point about gaussian blur: http://en.wikipedia.org/wiki/Gaussian_blur

But if you just want to make a on/off blurring effect then the function to convolve with (convolution kernel) is just an if statement on the distance between the source point and any possible neighbor pixel. For sure there are optimizations for that case: your loops will not need to span all pixels for all pixels, only enough so that a circle of size R fits within the range considered.

Since you want first principles, here it is. It works only for rgb black and white

from PIL import Image 

im = Image.open("cat2.png")
px=im.load()
im2 = im.copy()
px2=im2.load()
sx,sy=im.size
def turnpixel(Nix,Niy):
    for ix in range(sx):
        for iy in range(sy):
            r2=(Nix-ix)**2+(Niy-iy)**2
            if r2<5:
                if sum(px[ix,iy])>100: # rgb sum>100 is considered ON.
                    px2[Nix,Niy]=(255,255,255)                            
                    return
                    # we turned a pixel on, so we are done with it.

for Nix in range(sx):
    for Niy in range(sy):
        px2[Nix,Niy]=(0,0,0)
        turnpixel(Nix,Niy)

im.show()
im2.show()

If you want a smoothing that is a function of the distance use something like

import math
def turnpixel(Nix,Niy):
    for ix in range(sx):
        for iy in range(sy):
            r=int(math.sqrt((Nix-ix)**2+(Niy-iy)**2))
            def convfunc(o,v):
                return o+int(v/(r*20+1))
            px2[Nix,Niy]=tuple(map(convfunc,px2[Nix,Niy],px[ix,iy]))
            if sum(px2[Nix,Niy])>=3*250:
                return

Obviously, you should operate on floats, not integers if you do stuff like this. And use numpy or some of the image manipulation modules.

Gaussian blur would be (within the function above). Now integer is a really bad idea btw:

        inv2sigma2=1.0/(2*3)
        r2=(Nix-ix)**2+(Niy-iy)**2
        fact=inv2sigma2/(math.pi)*math.exp(-r2*inv2sigma2)
        def convfunc(o,v):
            return o+int(v*fact)
Community
  • 1
  • 1
Johan Lundberg
  • 26,184
  • 12
  • 71
  • 97
  • wow, you really went above and beyond, thank you! It's been a great help! – user714852 Mar 01 '12 at 13:37
  • thanks. It was fun. There's some further subtle things: when you are close to an edge a real convolution should deal with the possible in/out flux of information beyond the picture edges. If you just use my code for Gaussian blur you'll notice that object become fainter there. – Johan Lundberg Mar 01 '12 at 13:46
3

The standard way to do this would be to use a convolution. If you want all black and white, you'll need to apply a threshold after doing the convolution.

tom10
  • 67,082
  • 10
  • 127
  • 137