You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用PIL复现matplotlib绘制的正弦曲线折线图

问题描述

现有如下测试数据:

import numpy as np

N = 461
x = np.linspace(0, 4 * np.pi, N)
y = np.sin(x) + 50

使用matplotlib可正常绘制对应折线图,实现代码如下:

import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot(x, y)
plt.show()

现需要用PIL库复现上述折线图效果,初始尝试代码如下,运行后无法得到和matplotlib一致的结果,初步怀疑问题和数据为4.005,4.006这类浮点格式有关:

from PIL import Image, ImageDraw

im = Image.new('L', (N, N))
draw = ImageDraw.Draw(im)
for i in range(len(x) - 1):
    draw.line((x[i], y[i], x[i+1], y[i+1]), fill='red', width=2)
im.show()
问题原因

代码运行异常和浮点格式没有直接关系,PIL的ImageDraw.line本身支持传入浮点坐标,内部会自动完成像素取整。核心问题有三个:

  • 缺少坐标映射逻辑:matplotlib会自动将数据值域拉伸适配到整个绘图区域,而你直接把原始数据值当像素坐标传入。当前x范围是012.56,y范围是4951,所有点都会挤在画布左上角极小的区域内,根本看不到完整的正弦波形。
  • 图像模式与填充色不匹配:你创建的是'L'模式8位灰度图,仅支持0-255的灰度值,传入fill='red'这类RGB彩色值无法正确渲染。
  • 坐标轴方向错误:图片像素坐标的原点在左上角,y值越大位置越靠下;而matplotlib默认绘图原点在左下角,y值越大位置越靠上,直接传入原始y值会让绘制出的正弦曲线上下颠倒。
修正代码

实现时先完成数据坐标到像素坐标的映射,调整图像模式为RGB,翻转y轴映射方向即可得到和matplotlib一致的效果,代码如下:

from PIL import Image, ImageDraw
import numpy as np

N = 461
x = np.linspace(0, 4 * np.pi, N)
y = np.sin(x) + 50

# 画布配置,设置合适尺寸和留白边距
canvas_width, canvas_height = 640, 480
margin = 40
# 用RGB模式创建白色背景画布,匹配matplotlib默认白底效果
im = Image.new('RGB', (canvas_width, canvas_height), color='white')
draw = ImageDraw.Draw(im)

# 坐标映射工具:把数据值转换为对应像素位置
def data_to_pixel(val, val_min, val_max, px_min, px_max):
    return int(px_min + (val - val_min) / (val_max - val_min) * (px_max - px_min))

x_min, x_max = x.min(), x.max()
y_min, y_max = y.min(), y.max()
# x轴从左到右映射,y轴翻转方向实现左下原点
x_pixel = [data_to_pixel(xi, x_min, x_max, margin, canvas_width - margin) for xi in x]
y_pixel = [data_to_pixel(yi, y_min, y_max, canvas_height - margin, margin) for yi in y]

# 逐段绘制折线,用matplotlib默认蓝色线条
for i in range(len(x) - 1):
    draw.line(
        (x_pixel[i], y_pixel[i], x_pixel[i+1], y_pixel[i+1]),
        fill='#1f77b4',
        width=2
    )

# 可选:绘制坐标轴,对齐matplotlib基础样式
draw.line((margin, margin, margin, canvas_height - margin), fill='black', width=1)
draw.line((margin, canvas_height - margin, canvas_width - margin, canvas_height - margin), fill='black', width=1)

im.show()
补充说明

如果需要完全匹配matplotlib的默认输出比例,可以将画布尺寸调整为matplotlib默认的6.4:4.8(即640*480像素),边距比例设为0.125即可。如果不需要彩色效果,也可以保留'L'灰度模式,将线条填充值改为对应灰度值即可。

内容的提问来源于stack exchange,提问作者NadeY

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 21:51:19