如何实现类似Procreate的填充算法与圆形渐变动画效果?
实现Procreate风格的圆形渐变填充动画(基于多边形洪水填充)
我正在开发一款美观的洪水填充算法,注意到Procreate的填充动画效果非常出色——慢放后能看到它采用圆形扩散填充模式,同时围绕目标色生成不同尺寸的颜色渐变。我希望保留现有的多边形填充功能,仅需实现这种动画与渐变效果,但不知道该从哪里入手。
现有非递归洪水填充代码:
import mahotas import numpy as np import cv2 as cv from shapely.geometry import Polygon def flood_fill(poly, new_color): # separate the x's and y's xs = [i[0] for i in poly.exterior.coords] ys = [i[1] for i in poly.exterior.coords] minx, maxx = min(xs), max(xs) miny, maxy = min(ys), max(ys) X = int(maxx - minx) Y = int(maxy - miny) # create a new polygon cord that have no excess white space newPoly = [(int(x - minx), int(y - miny)) for (x, y) in poly.exterior.coords] # creates a mask for all the points in the polygon grid = np.zeros((X, Y), dtype=np.int8) mahotas.polygon.fill_polygon(newPoly, grid) # after masking return to points and translate back to original positions points = [[x + minx, y + miny] for (x, y) in zip(*np.nonzero(grid))] # now turn all the corresponding pixels in the blank_iamge to a desiered color for x, y in points: blank_image[int(y), int(x)] = np.array(new_color) # create a blank image blank_image = np.zeros((300, 400, 3), np.uint8) blank_image[:, :] = (255, 255, 255) flood_fill(poly=Polygon([[0,0],[0,100],[100,100],[100,0]]),new_color= (255,0,0)) cv.imshow("blank",blank_image) cv.waitKey(0)
实现思路
Procreate的填充效果核心是从中心向外扩散的同心圆环渐变,结合多边形掩码限制填充区域。具体步骤:
- 计算多边形的中心作为扩散起点
- 生成从0到多边形最大内切圆半径的半径序列,控制扩散节奏
- 为每个半径的圆环生成渐变颜色(从背景色向目标色过渡)
- 利用多边形掩码,只渲染圆环落在多边形内部的像素
- 逐帧渲染圆环,通过控制每帧的显示时间实现动画
修改后的完整代码
import mahotas import numpy as np import cv2 as cv from shapely.geometry import Polygon from shapely.geometry import Point def get_polygon_center(poly): """计算多边形的中心坐标""" centroid = poly.centroid return (int(centroid.x), int(centroid.y)) def get_max_radius(poly, center): """计算从中心到多边形边界的最大距离(扩散的最大半径)""" center_point = Point(center) max_dist = max(center_point.distance(Point(coord)) for coord in poly.exterior.coords) return int(max_dist) def generate_gradient_color(bg_color, target_color, progress): """根据进度生成渐变颜色:progress从0到1,从背景色过渡到目标色""" bg = np.array(bg_color, dtype=np.float32) target = np.array(target_color, dtype=np.float32) color = bg + (target - bg) * progress return tuple(map(int, color)) def animated_flood_fill(poly, new_color, bg_color=(255,255,255), fps=30): # 复用原有逻辑生成多边形掩码 xs = [i[0] for i in poly.exterior.coords] ys = [i[1] for i in poly.exterior.coords] minx, maxx = min(xs), max(xs) miny, maxy = min(ys), max(ys) X = int(maxx - minx) Y = int(maxy - miny) newPoly = [(int(x - minx), int(y - miny)) for (x, y) in poly.exterior.coords] # 生成掩码(1为多边形内部,0为外部) grid = np.zeros((Y, X), dtype=np.uint8) # 调整维度适配OpenCV图像格式 mahotas.polygon.fill_polygon(newPoly, grid) # 将掩码转换为原始图像坐标的掩码 full_mask = np.zeros_like(blank_image[:, :, 0], dtype=np.uint8) full_mask[int(miny):int(maxy), int(minx):int(maxx)] = grid # 计算扩散起点和最大半径 center = get_polygon_center(poly) max_r = get_max_radius(poly, center) # 生成半径序列(步长越小动画越平滑) radii = np.linspace(0, max_r, num=int(max_r * 0.8), dtype=int) # 逐帧渲染动画 for r in radii: # 计算当前扩散进度 progress = r / max_r # 生成当前圆环的渐变颜色 current_color = generate_gradient_color(bg_color, new_color, progress) # 绘制当前圆环 temp_img = blank_image.copy() cv.circle(temp_img, center, r, current_color, thickness=2) # 用掩码过滤,只保留多边形内部的像素 temp_img = cv.bitwise_and(temp_img, temp_img, mask=full_mask) blank_image[full_mask == 1] = temp_img[full_mask == 1] # 显示当前帧 cv.imshow("animated_fill", blank_image) # 控制帧率,按q可提前终止 if cv.waitKey(int(1000/fps)) & 0xFF == ord('q'): break # 最后填充完整目标色,完成动画 blank_image[full_mask == 1] = new_color cv.imshow("animated_fill", blank_image) cv.waitKey(0) # 创建空白图像 blank_image = np.zeros((300, 400, 3), np.uint8) blank_image[:, :] = (255, 255, 255) # 测试动画填充 animated_flood_fill( poly=Polygon([[0,0],[0,100],[100,100],[100,0]]), new_color=(255,0,0), fps=30 ) cv.destroyAllWindows()
代码说明
核心工具函数:
get_polygon_center:利用shapely的centroid属性获取多边形中心,作为扩散起点get_max_radius:计算中心到多边形最远顶点的距离,确定扩散的最大半径generate_gradient_color:根据当前扩散进度,生成从背景色到目标色的过渡色
动画逻辑:
- 复用原有代码生成多边形掩码,确保填充区域严格限制在多边形内
- 生成线性递增的半径序列,控制扩散的平滑度
- 逐帧绘制同心圆环,通过掩码过滤后合并到原始图像,实现扩散效果
- 最后填充完整目标色,完成动画收尾
可调参数:
fps:控制动画帧率,数值越大动画越快radii的num参数:调整生成的圆环数量,数值越大动画越平滑cv.circle的thickness:调整圆环宽度,模拟Procreate的渐变层次
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

