python - Implementing Sobel operators with python without opencv
Asked Answered
C

3

5

Given a greyscale 8 bit image (2D array with values from 0 - 255 for pixel intensity), I want to implement the Sobel operators (mask) on an image. The Sobel function below basically loops around a given pixel,applies the following weight to the pixels: enter image description here

enter image description here

And then aplies the given formula:

enter image description here

Im trying to implement the formulas from this link: http://homepages.inf.ed.ac.uk/rbf/HIPR2/sobel.htm

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import Image


def Sobel(arr,rstart, cstart,masksize, divisor):
  sum = 0;
  x = 0
  y = 0

  for i in range(rstart, rstart+masksize, 1):
    x = 0
    for j in range(cstart, cstart+masksize, 1):
        if x == 0 and y == 0:
            p1 = arr[i][j]
        if x == 0 and y == 1:
            p2 = arr[i][j]
        if x == 0 and y == 2:
            p3 = arr[i][j]
        if x == 1 and y == 0:
            p4 = arr[i][j]
        if x == 1 and y == 1:
            p5 = arr[i][j]           
        if x == 1 and y == 2:
            p6 = arr[i][j]
        if x == 2 and y == 0:
            p7 = arr[i][j]
        if x == 2 and y == 1:
            p8 = arr[i][j]
        if x == 2 and y == 2:
            p9 = arr[i][j]
        x +=1
    y +=1
  return np.abs((p1 + 2*p2 + p3) - (p7 + 2*p8+p9)) + np.abs((p3 + 2*p6 + p9) - (p1 + 2*p4 +p7)) 


def padwithzeros(vector, pad_width, iaxis, kwargs):
   vector[:pad_width[0]] = 0
   vector[-pad_width[1]:] = 0
   return vector

im = Image.open('charlie.jpg')
im.show()
img = np.asarray(im)
img.flags.writeable = True
p = 1
k = 2
m = img.shape[0]
n = img.shape[1]
masksize = 3
img = np.lib.pad(img, p, padwithzeros) #this function padds image with zeros to cater for pixels on the border.
x = 0
y = 0
for row in img:
  y = 0
  for col in row:
    if not (x < p or y < p or y > (n-k) or x > (m-k)):
        img[x][y] = Sobel(img, x-p,y-p,masksize,masksize*masksize)
    y = y + 1
  x = x + 1

img2 = Image.fromarray(img)
img2.show()

Given this greyscale 8 bit image

enter image description here

I get this when applying the function:

enter image description here

but should get this:

enter image description here

I have implemented other gaussian filters with python, I'm not sure where I'm going wrong here?

Cunctation answered 19/8, 2016 at 9:24 Comment(2)
There are (at least) two bugs in the code: (2) You cannot do this filter in-place. You overwrite a value in the input matrix, and for the next pixel you read this new value instead of the original one. You must have a separate output array. (2) The equation is likely to produce values that don’t fit in the 8-bit range, when assigning the value into the array, larger values wrap around. You might want to clip or scale the values before writing them into the output array.Moly
here too I should point out that I've been grave-digging in the [vision] tag and this question is 8 years oldPinch
A
4

Sticking close to what your code is doing, one elegant solution is to use the scipy.ndimage.filters.generic_filter() with the formula provided above.

import numpy as np
from scipy.ndimage.filters import generic_filter
from scipy.ndimage import imread

# Load sample data
with np.DataSource().open("https://i.sstatic.net/8zINU.gif", "rb") as f:
    img = imread(f, mode="I")

# Apply the Sobel operator
def sobel_filter(P):
    return (np.abs((P[0] + 2 * P[1] + P[2]) - (P[6] + 2 * P[7] + P[8])) +
            np.abs((P[2] + 2 * P[6] + P[7]) - (P[0] + 2 * P[3] + P[6])))
G = generic_filter(img, sobel_filter, (3, 3))

Running this on the sample image takes about 400 ms. For comparison, the convolve2d's performance is about 6.5 ms.

Agitation answered 19/8, 2016 at 11:50 Comment(0)
A
3

If using NumPy ans SciPy is not a problem, then a simple solution is to use the SciPy's convolve2d().

import numpy as np
from scipy.signal import convolve2d
from scipy.ndimage import imread

# Load sample data
with np.DataSource().open("https://i.sstatic.net/8zINU.gif", "rb") as f:
    img = imread(f, mode="I")

# Prepare the kernels
a1 = np.matrix([1, 2, 1])
a2 = np.matrix([-1, 0, 1])
Kx = a1.T * a2
Ky = a2.T * a1

# Apply the Sobel operator
Gx = convolve2d(img, Kx, "same", "symm")
Gy = convolve2d(img, Ky, "same", "symm")
G = np.sqrt(Gx**2 + Gy**2)
# or using the absolute values
G = np.abs(Gx) + np.abs(Gy)
Agitation answered 19/8, 2016 at 11:17 Comment(0)
H
0

I met the same problem as you. I fix it by reading the image of format 'gray', you could see below

import PIL.Image
img = PIL.Image.open('image.gif').convert('L')
Hillery answered 16/10, 2019 at 8:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.