Вот моя попытка воссоздать это с помощью OpenCV Python. Это довольно хак-иш-решение, которое немного интенсивно вычислительно, но оно, безусловно, выполняет свою работу.
Во-первых, создать маску, где пиксели, которые нулевой соответствуют те пиксели, которые вы хотите сохранить в высоком разрешении и пикселей, которые один соответствуют тем пикселей, которые вы хотите, чтобы размыть. Чтобы сделать все просто, я бы создал круг темных пикселей, которые определяют пиксели высокого разрешения.
С этой маской, одним из инструментов, который я могу предложить, чтобы сделать эту работу, является использование distance transform на этой маске. Для каждой точки в двоичной маске соответствующей выходной точкой в преобразовании расстояния является расстояние от этой точки до ближайшего нулевого пикселя. Таким образом, поскольку вы рискуете далеко от нулевых пикселей в маске, тем больше будет расстояние.
Следовательно, чем дальше вы идете от нулевого пикселя в этой маске, тем больше размывает вас. Используя эту идею, я просто написал цикл через изображение и в каждой точке создаю маску размытия - будь то усреднение или гауссовское или что-то связанное с этим - это пропорционально расстоянию в преобразовании расстояния и размывает эту точку с этим размытие маски. Любые значения, равные нулю в этой маске, не должны иметь никакого размытия. Для всех других точек в маске мы используем значения в маске, чтобы направлять нас в собирании окрестности пикселей с центром в этой точке и выполнять размытие. Чем больше расстояние, тем больше будет пиксельная окрестность и тем сильнее будет размытие.
Чтобы упростить работу, я собираюсь использовать маску усреднения. В частности, для каждого значения на расстоянии преобразования, размером этой маски будет M x 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()
Выход я получаю:
Я добавил в качестве примера изображение, чтобы сделать его самодостаточным. Надеюсь, все в порядке! – rayryeng
Спасибо, сделано более ясно. –