想要在Python绘图上对特定填充对象应用模糊



我在Linux Mint 20.3 Una上使用Python 3.8.10。我正在用大量(可能是数千种)鱼的形状制作一系列动画,每个形状都是通过指定带有点的2D轮廓来生成的,然后使用Pyplot填充函数填充。

我想做的是基于计算的距离来模拟图像深度,对每个单独填充的区域应用独特的模糊。更复杂的是这些填充区域经常重叠。

理论上,这可以通过导出SVG文件并在Inkscape或其他软件包中手动应用模糊来完成,但可能有数千条鱼和数百帧,因此在代码中实现这一目标的方法确实是唯一现实的方法,如果可能的话。

下面是生成两个填充的配置文件的最小代码,我想分别模糊:

import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter
#define profile of object with points
x_profile = [0.5,0.485951301332915,0.423371700761206,0.358237605529776,0.281609306290982,0.23180095266422,0.152618567550257,0.053001860296735,-0.005746611462221,-0.060663545623872,-0.05683323438022,-0.257343937095579,-0.317369329156755,-0.345466399463283,-0.469348762061393,-0.492337251833031,-0.5,-0.439974607938825,-0.418263242861681,-0.415709156986512,-0.461686095651334,-0.492337415346851,-0.483397419850022,-0.466794594429313,-0.363346513092306,-0.342912313588113,-0.31864669912198,-0.289272544999412,-0.236909860226751,-0.210090037250083,-0.183269887245775,-0.146233189348514,-0.078544599457363,0.086206203027589,0.210088361233424,0.310982111424531,0.418261893872663,0.478287408569203,0.493612741389321]
y_profile = [-0.019156461632871,0.002554903444271,0.031928934931474,0.051085805348896,0.065134504015981,0.07024308455087,0.071518492350251,0.067688181106599,0.158365179012477,0.068965632828735,0.049808353626761,0.028096988549618,0.025542085105346,0.03192770857782,0.10217038434414,0.104725287788412,0.091954040843463,0.00255449465972,-0.00255449465972,-0.017879827479838,-0.067688181106599,-0.148148017942698,-0.158365179012477,-0.151979555540003,-0.061302557634125,-0.047254267751592,-0.040868235494567,-0.042143643293948,-0.080457792913345,-0.084288104156997,-0.079179523622108,-0.097059759886497,-0.111108049769031,-0.127710834311284,-0.126435426511903,-0.107278556094481,-0.076627072885143,-0.045975589675805,-0.031927299793271]
#this just makes a second object and offsets it down 0.5 units
n_objects = 2
n_points = len(y_profile)
x_points = np.zeros((n_objects, n_points))
y_points = np.zeros((n_objects, n_points))                  
for i in range(n_objects):
for j in range(n_points):
x_points[i,j] = x_profile[j]
y_points[i,j] = y_profile[j] - i*0.5
#make plot
fig = plt.figure(frameon=False)
fig.set_size_inches(6.5, 6.5)
ax = plt.axes()
ax.set_facecolor((0,0,1.0))
ax.set_xlim(-1,+1)
ax.set_ylim(-1,+1)
ax.set_aspect('equal', adjustable='box')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
#create filled regions defined by copies of profile points (I want to be able to apply a blur these fills individually)

for i in range(n_objects):
plt.fill(x_points[i,:], y_points[i,:], color = (0, 0, 0.5))
#tried the following, but does not work at all.
#handle = plt.fill(x_profile, y_profile, color = (0, 0, 0.5))
#blurred = gaussian_filter(handle, sigma=1)
#show plot (normally exporting PNG frames for animation)
plt.show()

生成如下图像:

鱼概要文件

如果这在Python中是不可能的,我愿意接受关于如何以其他方式动态实现这一功能的建议。

我已经看到了SciPy高斯模糊应用于静态图像区域的例子,但我想要实现的模糊是特定于填充的"对象"。这不是一个整齐的矩形。我注意到,当此图像导出为SVG时,各个填充对象在该文件中显示为不同的实体,但我没有看到在Python中为其分配句柄并对其应用模糊的方法。我尝试了'handle = plt.fill(x,y)'和'gaussian_filter(handle, sigma=1)'的变体,但没有成功。

我认为我能够做你所要求的使用卷积,但它并没有优化速度。另外,很难说它能不能很好地转化为更大的代码。

离开你发布的方式,我将图形转换为rgb数组,并使用从头开始的卷积函数(不是我自己的,1)分别卷积每个维度。这段代码将输出第一张鱼图像,然后几秒钟后它将输出模糊的鱼图像。

import matplotlib.pyplot as plt
import numpy as np
import cv2
import plotly.express as px
def Convolve(img, kernel):
(imgX, imgY) = img.shape[:2]
(kernelX, kernelY) = kernel.shape[:2]
#print(imgX,imgY, kernelX,kernelY)
pad = (kernelX - 1) // 2
img = cv2.copyMakeBorder(img, pad, pad, pad, pad, cv2.BORDER_REPLICATE) #top, bottom, left, right
#the above line prevents error with convolution: operands could not be broadcast together with shapes (23,22) (23,23)
output = np.zeros((imgX, imgY), dtype="float32")
#shift kernel vertical and horizontal across image, pad prevents kernel from going out of bounds
for y in np.arange(pad, imgY + pad):
for x in np.arange(pad, imgX + pad):
#locate specific pixel
roi = img[y - pad:y + pad + 1, x - pad:x + pad + 1]
#print(roi)
#perform convolution
k = (roi * kernel).sum()
#populate the result into the previously created np.zeroes array
output[y - pad, x - pad] = k
return output

# define profile of object with points
x_profile = [0.5, 0.485951301332915, 0.423371700761206, 0.358237605529776, 0.281609306290982, 0.23180095266422,
0.152618567550257, 0.053001860296735, -0.005746611462221, -0.060663545623872, -0.05683323438022,
-0.257343937095579, -0.317369329156755, -0.345466399463283, -0.469348762061393, -0.492337251833031, -0.5,
-0.439974607938825, -0.418263242861681, -0.415709156986512, -0.461686095651334, -0.492337415346851,
-0.483397419850022, -0.466794594429313, -0.363346513092306, -0.342912313588113, -0.31864669912198,
-0.289272544999412, -0.236909860226751, -0.210090037250083, -0.183269887245775, -0.146233189348514,
-0.078544599457363, 0.086206203027589, 0.210088361233424, 0.310982111424531, 0.418261893872663,
0.478287408569203, 0.493612741389321]
y_profile = [-0.019156461632871, 0.002554903444271, 0.031928934931474, 0.051085805348896, 0.065134504015981,
0.07024308455087, 0.071518492350251, 0.067688181106599, 0.158365179012477, 0.068965632828735,
0.049808353626761, 0.028096988549618, 0.025542085105346, 0.03192770857782, 0.10217038434414,
0.104725287788412, 0.091954040843463, 0.00255449465972, -0.00255449465972, -0.017879827479838,
-0.067688181106599, -0.148148017942698, -0.158365179012477, -0.151979555540003, -0.061302557634125,
-0.047254267751592, -0.040868235494567, -0.042143643293948, -0.080457792913345, -0.084288104156997,
-0.079179523622108, -0.097059759886497, -0.111108049769031, -0.127710834311284, -0.126435426511903,
-0.107278556094481, -0.076627072885143, -0.045975589675805, -0.031927299793271]
# make plot
fig = plt.figure(frameon=False)
fig.set_size_inches(6.5, 6.5)
ax = plt.axes()
ax.set_facecolor((0, 0, 1.0))
ax.set_xlim(-1, +1)
ax.set_ylim(-1, +1)
ax.set_aspect('equal', adjustable='box')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
# create filled region defined by profile points (I want to be able to apply a blur to this)
plt.fill(x_profile, y_profile, color=(0, 0, 0.5))
fig.canvas.draw()
data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
array=np.array(data)
R=array[:,:,0]
G=array[:,:,1]
B=array[:,:,2]
fig=px.imshow(array, color_continuous_scale="gray")
fig.show()
_1DKern = cv2.getGaussianKernel(33, 2)  # first value is dimensions, second is sigma
_2DKern = np.outer(_1DKern, _1DKern.transpose())
convR = Convolve(R, _2DKern)
convG=Convolve(G,_2DKern)
convB=Convolve(B,_2DKern)
conv=np.stack([convR,convG,convB],2)
fig = px.imshow(conv, color_continuous_scale="gray")
fig.show()

最新更新