这是我尝试使用OpenCV Python重新创建它.这是一个相当破解的解决方案,有点计算密集,但它确实完成了工作.
首先,创建一个遮罩,其中零像素对应于您希望保持高分辨率的像素,而一个像素对应于您想要模糊的像素.为了简单起见,我会创建一个暗像素圆圈来定义高分辨率像素.
使用这个掩码,我建议使用的一个工具就是在这个掩码上使用distance transform.对于二进制掩码中的每个点,距离变换中的对应输出点是从该点到最近的零像素的距离.因此,当你冒险远离掩模中的零像素时,距离就越大.
因此,距离此蒙版中的零像素越远,您应用的模糊越多.使用这个想法,我只是在图像中编写一个循环,并在每个点创建一个模糊蒙版 – 无论是平均还是高斯或与此相关的任何东西 – 与距离变换中的距离成比例并将此点模糊模糊面具.此蒙版中的任何零值都不应该应用模糊.对于蒙版中的所有其他点,我们使用蒙版中的值来指导我们收集以此点为中心的像素邻域并执行模糊.距离越大,像素邻域应该越大,因此模糊越强.
为简化起见,我将使用平均掩码.具体来说,对于距离变换中的每个值,该掩码的大小将为M×M,其中M为:
M = d / S
d是距离变换的距离值,S是缩小d值的比例因子,因此平均可以更可行.这是因为距离变换可以变得非常大,因为你离零像素越来越远,因此比例因子使得平均更加真实.形式上,对于输出中的每个像素,我们收集M x M像素的邻域,得到平均值并将其设置为我们的输出.
我们需要记住的一个复杂性是,当我们收集邻域中心位于图像边界的像素时,我们需要确保我们收集图像边界内的像素,以便任何位于外部的位置我们跳过了图像.
现在是时候展示一些结果了.作为参考,我使用了Camera Man图像,这是一个标准的测试图像,非常受欢迎.它显示在这里:
我还要将位于第70行和第100列的蒙版设置为半径为25的圆.不用多说了,这里是完全注释的代码.我会让你自己解析评论.
import cv2 # Import relevant libraries
import cv
import numpy as np
img = cv2.imread('cameraman.png', 0) # Read in image
height = img.shape[0] # Get the dimensions
width = img.shape[1]
# Define mask
mask = 255*np.ones(img.shape, dtype='uint8')
# Draw circle at x = 100, y = 70 of radius 25 and fill this in with 0
cv2.circle(mask, (100, 70), 25, 0, -1)
# Apply distance transform to mask
out = cv2.distanceTransform(mask, cv.CV_DIST_L2, 3)
# Define scale factor
scale_factor = 10
# Create output image that is the same as the original
filtered = img.copy()
# Create floating point copy for precision
img_float = img.copy().astype('float')
# Number of channels
if len(img_float.shape) == 3:
num_chan = img_float.shape[2]
else:
# If there is a single channel, make the images 3D with a singleton
# dimension to allow for loop to work properly
num_chan = 1
img_float = img_float[:,:,None]
filtered = filtered[:,:,None]
# For each pixel in the input...
for y in range(height):
for x in range(width):
# If distance transform is 0, skip
if out[y,x] == 0.0:
continue
# Calculate M = d / S
mask_val = np.ceil(out[y,x] / scale_factor)
# If M is too small, set the mask size to the smallest possible value
if mask_val <= 3:
mask_val = 3
# Get beginning and ending x and y coordinates for neighbourhood
# and ensure they are within bounds
beginx = x-int(mask_val/2)
if beginx < 0:
beginx = 0
beginy = y-int(mask_val/2)
if beginy < 0:
beginy = 0
endx = x+int(mask_val/2)
if endx >= width:
endx = width-1
endy = y+int(mask_val/2)
if endy >= height:
endy = height-1
# Get the coordinates of where we need to grab pixels
xvals = np.arange(beginx, endx+1)
yvals = np.arange(beginy, endy+1)
(col_neigh,row_neigh) = np.meshgrid(xvals, yvals)
col_neigh = col_neigh.astype('int')
row_neigh = row_neigh.astype('int')
# Get the pixels now
# For each channel, do the foveation
for ii in range(num_chan):
chan = img_float[:,:,ii]
pix = chan[row_neigh, col_neigh].ravel()
# Calculate the average and set it to be the output
filtered[y,x,ii] = int(np.mean(pix))
# Remove singleton dimension if required for display and saving
if num_chan == 1:
filtered = filtered[:,:,0]
# Show the image
cv2.imshow('Output', filtered)
cv2.waitKey(0)
cv2.destroyAllWindows()
我得到的输出是: