图像就是函数,梯度对应的数值就是像素值的突变情况。
图像梯度可以把图像看成二维离散函数,图像梯度其实就是这个二维离散函数的求导。
图像梯度: G(x,y) = dx(i,j) + dy(i,j);
dx(i,j) = I(i+1,j) - I(i,j);
dy(i,j) = I(i,j+1) - I(i,j);
其中,I是图像像素的值(如:RGB值),(i,j)为像素的坐标。
图像梯度一般也可以用中值差分:
dx(i,j) = [I(i+1,j) - I(i-1,j)]/2;
dy(i,j) = [I(i,j+1) - I(i,j-1)]/2;
图像边缘一般都是通过对图像进行梯度运算来实现的。
以Sobel算子为例
class Sobel(nn.Module):
def __init__(self):
super(Sobel, self).__init__()
self.edge_conv = nn.Conv2d(1, 2, kernel_size=3, stride=1, padding=1, bias=False)
edge_kx = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]])
edge_ky = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
edge_k = np.stack((edge_kx, edge_ky))
edge_k = torch.from_numpy(edge_k).float().view(2, 1, 3, 3)
self.edge_conv.weight = nn.Parameter(edge_k)
for param in self.parameters():
param.requires_grad = False
def forward(self, x):
out = self.edge_conv(x)
out = out.contiguous().view(-1, 2, x.size(2), x.size(3))
return out
image = Image.open(r"F:\Datasets\nyu_depth_v2\test\bedroom\rgb_01038.jpg")
image = torchvision.transforms.Grayscale()(image)
image = torchvision.transforms.ToTensor()(image)
sobel = Sobel()
out = sobel(image.unsqueeze(dim=0))
dx = out[:, 0, :, :]
dy = out[:, 1, :, :]
plt.imshow(dx.squeeze().numpy())
plt.show()
plt.imshow(dy.squeeze().numpy())
plt.show()
grad = dx * 0.5 + dy * 0.5
plt.imshow(grad.squeeze().numpy())
plt.show()
之后我们再尝试用cv2封装好的函数去提取梯度
image = cv2.imread(r"F:\Datasets\nyu_depth_v2\test\bedroom\rgb_01038.jpg", 0)
grad_x = cv2.Sobel(image,cv2.CV_64F, 1, 0, ksize = 3)
sobelx = cv2.convertScaleAbs(grad_x)
grad_y = cv2.Sobel(image,cv2.CV_64F, 0, 1, ksize = 3)
sobely = cv2.convertScaleAbs(grad_y)
plt.imshow(sobelx)
plt.show()
plt.imshow(sobely)
plt.show()
grad = sobely * 0.5 + sobelx * 0.5
plt.imshow(grad)
plt.show()
二者图像显示不同的原因是因为cv2提取的是灰度图像 numpy像素值在0-255之间,而tensor不同。
tensor的像素值特别接近,我们可以简单的设定一个阈值,将无用像素设置为0,并将其他像素归一化到0-255区间,就可以得到近似的结果
dx = out[:, 0, :, :]
dy = out[:, 1, :, :]
dx = torch.where(dx > 0, dx, 0)
dy = torch.where(dy > 0, dy, 0)
dx = torch.sigmoid(dx) * 255
dy = torch.sigmoid(dy) * 255
结果已经接近,不过还是有差别(舍弃的梯度值较多),原因是此时的dx,dy有负值。在筛选时将负数去除,导致梯度值损失较多,只需要在更改 dx = torch,where(dx!=0,dx.abs(),0) 就可得到如下结果