如何在Python绘图中为特定填充对象单独添加模糊效果?
为独立填充的非规则图形添加个性化模糊效果(模拟景深)
背景与需求
- 环境:Linux Mint 20.3 Una,Python 3.8.10
- 任务:制作包含数千个鱼形的动画,每个鱼形通过指定2D轮廓点,用Matplotlib的
fill函数生成填充区域 - 核心需求:根据计算出的距离,为每个独立填充区域添加独特的模糊效果以模拟景深,且区域存在重叠情况
- 痛点:手动在Inkscape等工具处理SVG不可行(数千鱼形+数百帧),尝试
plt.fill句柄结合gaussian_filter无效,需代码化解决方案
最小复现代码
import matplotlib.pyplot as plt import numpy as np # 定义鱼形轮廓点 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] 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 # 创建画布 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) for spine in ax.spines.values(): spine.set_visible(False) # 绘制两个鱼形填充区域 for i in range(n_objects): plt.fill(x_points[i,:], y_points[i,:], color = (0, 0, 0.5)) plt.show()
上述代码生成两个上下偏移的深蓝色鱼形,背景为纯蓝色。
可行解决方案
方案1:像素级分层渲染+模糊(Matplotlib+SciPy)
思路:将每个鱼形单独渲染到透明临时画布,应用高斯模糊后再合成到主画布,保留重叠区域的正确层级。
import matplotlib.pyplot as plt import numpy as np from scipy.ndimage import gaussian_filter # 鱼形轮廓点(同前文,省略重复代码) x_profile = [...] y_profile = [...] 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 # 自定义每个鱼形的模糊sigma值(可根据距离动态计算) sigmas = [1, 3] # 创建主画布 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) for spine in ax.spines.values(): spine.set_visible(False) # 逐个渲染鱼形并模糊 for i in range(n_objects): # 创建临时透明画布 temp_fig = plt.figure(frameon=False) temp_fig.set_size_inches(6.5, 6.5) temp_ax = temp_fig.add_axes([0,0,1,1]) temp_ax.set_xlim(-1,+1) temp_ax.set_ylim(-1,+1) temp_ax.set_aspect('equal', adjustable='box') temp_ax.set_facecolor('none') temp_ax.get_xaxis().set_visible(False) temp_ax.get_yaxis().set_visible(False) for spine in temp_ax.spines.values(): spine.set_visible(False) # 在临时画布绘制单个鱼形 temp_ax.fill(x_points[i,:], y_points[i,:], color=(0,0,0.5)) # 将画布转为像素数组 temp_fig.canvas.draw() buf = temp_fig.canvas.buffer_rgba() temp_img = np.asarray(buf) # 分离RGB和Alpha通道,仅模糊RGB部分 rgb = temp_img[:, :, :3] alpha = temp_img[:, :, 3:] blurred_rgb = gaussian_filter(rgb, sigma=sigmas[i]) blurred_img = np.concatenate([blurred_rgb, alpha], axis=2) # 将模糊后的图像合成到主画布 ax.imshow(blurred_img, extent=[-1,1,-1,1], origin='lower') # 关闭临时画布释放资源 plt.close(temp_fig) plt.show()
方案2:矢量级SVG批量修改
思路:利用Matplotlib导出的SVG中每个填充区域是独立<path>元素的特性,通过XML解析工具为每个路径添加高斯模糊滤镜。
import matplotlib.pyplot as plt import numpy as np from xml.etree import ElementTree as ET # 鱼形轮廓点(同前文,省略重复代码) x_profile = [...] y_profile = [...] n_objects = 2 # 对应每个鱼形的模糊程度 sigmas = [1, 3] # 生成原始SVG 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) for spine in ax.spines.values(): spine.set_visible(False) # 绘制鱼形 for i in range(n_objects): x = x_profile y = [y - i*0.5 for y in y_profile] ax.fill(x, y, color=(0,0,0.5)) # 导出SVG svg_path = 'fish_frame.svg' plt.savefig(svg_path, format='svg', transparent=True) plt.close() # 解析并修改SVG ET.register_namespace('', "http://www.w3.org/2000/svg") tree = ET.parse(svg_path) root = tree.getroot() # 添加滤镜定义 defs = ET.SubElement(root, '{http://www.w3.org/2000/svg}defs') for idx, sigma in enumerate(sigmas): filter_id = f'blur-filter-{idx}' filter_elem = ET.SubElement(defs, '{http://www.w3.org/2000/svg}filter', id=filter_id) ET.SubElement(filter_elem, '{http://www.w3.org/2000/svg}feGaussianBlur', stdDeviation=str(sigma)) # 为对应路径添加滤镜引用(跳过第一个path,为背景) path_elem = root.findall('.//{http://www.w3.org/2000/svg}path')[idx+1] path_elem.set('filter', f'url(#{filter_id})') # 保存修改后的SVG tree.write('fish_frame_blurred.svg', encoding='utf-8', xml_declaration=True)
方案3:使用专业动画库(如Manim)
如果需要制作复杂动画,Manim等专业动画库支持直接为矢量对象添加模糊滤镜,适合批量处理大量元素。示例片段:
from manim import * class FishBlurAnimation(Scene): def construct(self): # 定义鱼形路径 fish_path = VMobject() fish_path.set_points_as_corners([ (0.5, -0.019, 0), (0.485, 0.002, 0), ... # 完整轮廓点 ]) fish_path.set_fill(BLUE_D, opacity=1) # 复制并偏移鱼形,添加不同模糊 fish1 = fish_path.copy().shift(UP*0.5) fish1.set_blur(1) fish2 = fish_path.copy().shift(DOWN*0.5) fish2.set_blur(3) self.add(fish1, fish2) self.wait()
内容的提问来源于stack exchange,提问作者user20787420
相关产品推荐
相关产品推荐

