如何使用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
相关产品推荐
相关产品推荐

