如何生成网格渐变?求多色二维颜色插值的伪代码/Python实现
多色二维网格渐变实现方案
要实现类似的随机点多色网格渐变,常用两种主流方法:逆距离加权(IDW)插值和Delaunay三角剖分+重心插值,以下是具体实现:
方法一:逆距离加权(IDW)插值
该方法通过目标点与所有控制点的距离反比权重,加权求和得到目标颜色,距离越近的控制点对颜色影响越大。
伪代码
// 定义控制点集合,每个元素包含坐标与RGB颜色 控制点列表 = [ (x1,y1,r1,g1,b1), (x2,y2,r2,g2,b2), ..., (xn,yn,rn,gn,bn) ] // 计算目标点颜色 函数 计算目标颜色(x_target, y_target): 总权重 = 0 累计r = 0, 累计g = 0, 累计b = 0 极小值 = 1e-6 // 避免除以0 遍历每个控制点 in 控制点列表: dx = x_target - 控制点.x dy = y_target - 控制点.y 距离 = sqrt(dx*dx + dy*dy) 如果 距离 < 极小值: 返回 (控制点.r, 控制点.g, 控制点.b) 权重 = 1 / (距离^p) // p为幂次,通常取2,可调整 累计r += 控制点.r * 权重 累计g += 控制点.g * 权重 累计b += 控制点.b * 权重 总权重 += 权重 // 归一化得到最终颜色 最终r = 累计r / 总权重 最终g = 累计g / 总权重 最终b = 累计b / 总权重 返回 (最终r, 最终g, 最终b)
Python实现
import numpy as np from PIL import Image def idw_color_interpolation(target_x, target_y, control_points, p=2): total_weight = 0.0 r_sum, g_sum, b_sum = 0.0, 0.0, 0.0 eps = 1e-6 for (x, y, r, g, b) in control_points: dx = target_x - x dy = target_y - y dist = np.sqrt(dx**2 + dy**2) if dist < eps: return (r, g, b) weight = 1.0 / (dist ** p) r_sum += r * weight g_sum += g * weight b_sum += b * weight total_weight += weight r = int(round(r_sum / total_weight)) g = int(round(g_sum / total_weight)) b = int(round(b_sum / total_weight)) # 确保颜色值在0-255范围内 r = max(0, min(255, r)) g = max(0, min(255, g)) b = max(0, min(255, b)) return (r, g, b) if __name__ == "__main__": # 生成随机控制点,可替换为自定义点 np.random.seed(42) width, height = 500, 500 num_points = 10 control_points = [] for _ in range(num_points): x = np.random.randint(0, width) y = np.random.randint(0, height) r = np.random.randint(0, 256) g = np.random.randint(0, 256) b = np.random.randint(0, 256) control_points.append((x, y, r, g, b)) # 生成图像 img_array = np.zeros((height, width, 3), dtype=np.uint8) for y in range(height): for x in range(width): img_array[y, x] = idw_color_interpolation(x, y, control_points) img = Image.fromarray(img_array) img.save("idw_color_gradient.png") img.show()
方法二:Delaunay三角剖分 + 重心插值
该方法先将控制点剖分为三角网格,再在每个三角形内部通过重心坐标做线性插值,渐变过渡更平滑,效果更接近目标网站的网格风格。
伪代码
// 步骤1:执行Delaunay三角剖分 三角剖分结果 = 生成Delaunay三角网格(控制点的(x,y)坐标集合) // 每个三角形由三个控制点的索引组成 // 步骤2:找到目标点所属的三角形 函数 找到所属三角形(x_target, y_target, 三角剖分结果, 控制点列表): 遍历每个三角形 in 三角剖分结果: p1 = 控制点列表[三角形索引0] p2 = 控制点列表[三角形索引1] p3 = 控制点列表[三角形索引2] 如果 点(x_target,y_target) 在三角形p1-p2-p3内部: 返回 (p1, p2, p3) // 边界外的点返回最近的控制点 返回 距离最近的控制点 // 步骤3:重心坐标插值计算颜色 函数 重心插值颜色(x_target, y_target, p1, p2, p3): x1, y1, r1, g1, b1 = p1 x2, y2, r2, g2, b2 = p2 x3, y3, r3, g3, b3 = p3 // 计算重心坐标(u, v, w),满足u + v + w = 1 分母 = (y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3) u = ((y2 - y3)*(x_target - x3) + (x3 - x2)*(y_target - y3)) / 分母 v = ((y3 - y1)*(x_target - x3) + (x1 - x3)*(y_target - y3)) / 分母 w = 1 - u - v // 加权计算颜色 r = u*r1 + v*r2 + w*r3 g = u*g1 + v*g2 + w*g3 b = u*b1 + v*b2 + w*b3 返回 (r, g, b) // 主函数 函数 计算目标颜色(x_target, y_target): 三角形顶点 = 找到所属三角形(x_target, y_target, 三角剖分结果, 控制点列表) 如果 三角形顶点是单个控制点: 返回 该控制点颜色 否则: 返回 重心插值颜色(x_target, y_target, 三角形顶点[0], 三角形顶点[1], 三角形顶点[2])
Python实现
import numpy as np from PIL import Image from scipy.spatial import Delaunay def barycentric_interpolation(target_x, target_y, p1, p2, p3): x1, y1, r1, g1, b1 = p1 x2, y2, r2, g2, b2 = p2 x3, y3, r3, g3, b3 = p3 denom = (y2 - y3)*(x1 - x3) + (x3 - x2)*(y1 - y3) u = ((y2 - y3)*(target_x - x3) + (x3 - x2)*(target_y - y3)) / denom v = ((y3 - y1)*(target_x - x3) + (x1 - x3)*(target_y - y3)) / denom w = 1.0 - u - v r = u*r1 + v*r2 + w*r3 g = u*g1 + v*g2 + w*g3 b = u*b1 + v*b2 + w*b3 r = int(round(max(0, min(255, r)))) g = int(round(max(0, min(255, g)))) b = int(round(max(0, min(255, b)))) return (r, g, b) def get_nearest_control_point(target_x, target_y, control_points): min_dist = float('inf') nearest_point = None for pt in control_points: dx = target_x - pt[0] dy = target_y - pt[1] dist = np.sqrt(dx**2 + dy**2) if dist < min_dist: min_dist = dist nearest_point = pt return nearest_point if __name__ == "__main__": np.random.seed(42) width, height = 500, 500 num_points = 10 # 生成随机控制点 control_points = [] coords = [] for _ in range(num_points): x = np.random.randint(0, width) y = np.random.randint(0, height) r = np.random.randint(0, 256) g = np.random.randint(0, 256) b = np.random.randint(0, 256) control_points.append((x, y, r, g, b)) coords.append([x, y]) coords = np.array(coords) # 执行Delaunay三角剖分 tri = Delaunay(coords) # 生成图像 img_array = np.zeros((height, width, 3), dtype=np.uint8) for y in range(height): for x in range(width): simplex_idx = tri.find_simplex([x, y]) if simplex_idx == -1: # 边界外点用最近控制点颜色 nearest_pt = get_nearest_control_point(x, y, control_points) img_array[y, x] = (nearest_pt[2], nearest_pt[3], nearest_pt[4]) else: idx1, idx2, idx3 = tri.simplices[simplex_idx] p1 = control_points[idx1] p2 = control_points[idx2] p3 = control_points[idx3] img_array[y, x] = barycentric_interpolation(x, y, p1, p2, p3) img = Image.fromarray(img_array) img.save("delaunay_color_gradient.png") img.show()
内容的提问来源于stack exchange,提问作者Jacob
相关产品推荐
相关产品推荐

