Carrega as bibliotecas necessárias e a imagem da Lena.
%matplotlib inline
from skimage import color
import urllib, cStringIO
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image as ImagePIL
lenaUrl = 'https://upload.wikimedia.org/wikipedia/en/2/24/Lenna.png'
f = cStringIO.StringIO(urllib.urlopen(lenaUrl).read())
img = ImagePIL.open(f)
img = np.array(img.getdata(), np.uint8).reshape(img.size[1], img.size[0], 3)
img = color.rgb2gray(img)
plt.imshow(img, cmap=plt.cm.gray)
plt.show()
Foram implementadas duas funções, a conv2d_naive onde a máscara é sobreposta a cada pixel da imagem e conv2d na qual a imagem que desliza sobre a máscara. A função conv2d é muito mais rápida do que a conv2d_naive, sua velocidade é comparada com a função convolve2d da biblioteca scikit. O tempo de execução (Wall time) segue a ordem: conv2d_naive, conv2d e convolve2d (scikit).
def kernel_flatten(k):
ret = []
mid_w = k.shape[0]/2
mid_h = k.shape[1]/2
for i in range(k.shape[0]):
for j in range(k.shape[1]):
ret.append((mid_w-i, mid_h-j, k[i, j]))
return ret, mid_w, mid_h
# A naive aproach
def conv2d_naive(img, kernel, border='valid'):
ret = np.zeros(img.shape)
shape = img.shape
ker, mid_w, mid_h = kernel_flatten(kernel)
for i in range(shape[0]):
for j in range(shape[1]):
ignore = False
for k, l, value in ker:
x, y = (i-k), (j-l) #convolução
if border == 'valid' and (x < 0 or x >= shape[0] or y < 0 or y >= shape[1]):
ignore = True
break
if border == 'same' and (x < 0 or x >= shape[0] or y < 0 or y >= shape[1]):
if x < 0:
x = shape[0]+x
elif x >= shape[0]:
x = x-shape[0]
if y < 0:
y = shape[1]+y
elif y >= shape[1]:
y = y-shape[1]
ret[i, j] += value*img[x, y]
if ignore:
ret[i, j] = 0
if border == 'valid':
ret = ret[mid_w:shape[0]-mid_w,mid_h:shape[1]-mid_h]
return ret
def correlation_2d(img, kernel, border):
shape = img.shape
ker, mid_w, mid_h = kernel_flatten(kernel)
ret = np.zeros(shape)
tmp = np.tile(img, (3, 3))
tmp = tmp[shape[0]-mid_w:(shape[0]*2)+mid_w, shape[1]-mid_h:(shape[1]*2)+mid_h]
if border == 'valid':
tmp[0:mid_w, ::] = 0
tmp[shape[0]+1:, ::] = 0
tmp[::, 0:mid_h] = 0
tmp[::, shape[1]+1:] = 0
for k, l, value in ker:
x1, x2 = (k+tmp.shape[0]-shape[0]-1, shape[0]+k+1)
y1, y2 = (l+tmp.shape[1]-shape[1]-1, shape[1]+l+1)
ret += np.float32(value) * tmp[x1:x2, y1:y2]
if border == 'valid':
ret = ret[mid_w:shape[0]-mid_w,mid_h:shape[1]-mid_h]
return ret
def conv2d(img, kernel, border='valid'):
return correlation_2d(img, kernel[::-1, ::-1], border)
mean_kernel = np.array([[1, 1, 1],[1, 1, 1],[1, 1, 1]])
edge_kernel = np.array([[-1, -1, -1],[-1, 8, -1],[-1, -1, -1]])
%time conv1 = conv2d_naive(img, edge_kernel, border='valid')
%time conv2 = conv2d(img, edge_kernel, border='valid')
from scipy import signal
%time conv3 = signal.convolve2d(img, edge_kernel, mode='valid')
plt.imshow(np.absolute(conv2), cmap=plt.cm.gray)
plt.show()
Wall time: 8.71 s
Wall time: 22 ms
Wall time: 14 ms
Resultado da execução da função conv2d utilizando o filtro de Sobel.