已知椭圆两焦点及常数d时如何在图像上绘制椭圆
你可以通过椭圆的几何定义推导所需参数,完全可以在Pillow等直接操作图像的库中完成绘制,不需要依赖matplotlib,具体实现分两种方案:
方案1:计算边界框调用原生椭圆接口
多数绘图库的椭圆接口支持传入边界框+旋转角度参数,你可以按以下步骤计算参数:
- 先明确已知量:两个焦点坐标记为
F1=(x1,y1)、F2=(x2,y2),椭圆上任意点到两焦点的距离和为常数d,注意d必须大于两焦点的直线距离,否则无法构成有效椭圆 - 计算椭圆基础几何参数
- 椭圆中心为两焦点中点:
center_x = (x1+x2)/2,center_y = (y1+y2)/2 - 焦点到中心的距离:
c = sqrt((x2-x1)**2 + (y2-y1)**2)/2 - 椭圆长半轴:
a = d/2(长轴长度等于d) - 椭圆短半轴:由椭圆几何关系
a² = b² + c²推导得b = sqrt(a**2 - c**2) - 椭圆长轴旋转角度:即两焦点连线与x轴的夹角,
theta = atan2(y2-y1, x2-x1)(弧度制)
- 椭圆中心为两焦点中点:
- 计算轴对齐外接边界框
旋转椭圆的外接矩形半宽、半高计算公式如下:
对应Pillow等库要求的边界框坐标(左上角、右下角)为:half_width = sqrt( (a * cos(theta))**2 + (b * sin(theta))**2 ) half_height = sqrt( (a * sin(theta))**2 + (b * cos(theta))**2 )bbox = ( center_x - half_width, center_y - half_height, center_x + half_width, center_y + half_height )
注意:如果你用的库只支持绘制轴对齐椭圆、不支持传入旋转角度,该方案仅在两焦点处于同一水平线/竖直线时生效,旋转椭圆请使用方案2。如果库支持图层旋转,也可以先在透明图层绘制轴对齐椭圆(bbox为
(center_x-a, center_y-b, center_x+a, center_y+b)),再将图层旋转对应角度后贴回原图即可。
方案2:按椭圆定义逐点生成路径绘制(兼容所有场景)
该方法不需要依赖库的椭圆旋转能力,适配所有绘图工具,生成的椭圆精度可控:
- 以椭圆中心为原点、长轴为x'轴建立局部坐标系,从0到2π按固定步长取角度t,生成局部坐标系下的椭圆点:
(a*cos(t), b*sin(t)),步长越小最终椭圆越平滑,一般取0.01弧度即可满足日常需求 - 对每个局部坐标点做旋转变换,旋转角度为之前计算的theta:
x_rot = x_local * cos(theta) - y_local * sin(theta) y_rot = x_local * sin(theta) + y_local * cos(theta) - 将旋转后的点平移到图像实际坐标系:
x = x_rot + center_x,y = y_rot + center_y - 按顺序连接所有点形成闭合多边形,即可得到目标椭圆。
Pillow 可运行实现代码
import math from PIL import Image, ImageDraw def draw_ellipse_by_foci(draw, focus1, focus2, d, fill=None, outline=None, width=1): x1, y1 = focus1 x2, y2 = focus2 # 校验参数合法性 focal_distance = math.hypot(x2 - x1, y2 - y1) if d < focal_distance: raise ValueError("参数错误:d必须大于两焦点之间的直线距离") # 计算基础参数 cx, cy = (x1 + x2)/2, (y1 + y2)/2 a = d / 2 c = focal_distance / 2 b = math.sqrt(a**2 - c**2) theta = math.atan2(y2 - y1, x2 - x1) cos_t, sin_t = math.cos(theta), math.sin(theta) # 生成椭圆点集 points = [] step = 0.01 # 角度步长,越小椭圆越平滑 for i in range(int(2 * math.pi / step) + 1): t = i * step x_local = a * math.cos(t) y_local = b * math.sin(t) # 坐标变换:旋转+平移 x = x_local * cos_t - y_local * sin_t + cx y = x_local * sin_t + y_local * cos_t + cy points.append((x, y)) # 绘制闭合多边形 draw.polygon(points, fill=fill, outline=outline, width=width) # 调用示例 if __name__ == "__main__": img = Image.new("RGB", (600, 600), "white") draw = ImageDraw.Draw(img) # 传入两个焦点、d值绘制红色椭圆 draw_ellipse_by_foci(draw, focus1=(200, 200), focus2=(400, 350), d=350, outline="red", width=2) img.save("ellipse_demo.png")
内容的提问来源于stack exchange,提问作者GameDungeon
相关产品推荐
相关产品推荐

