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

Python PIL生成图像像素呈矩形而非正方形的原因与解决方法

问题描述

我是Python Imaging Library (PIL)的初学者,近期使用时发现生成的图像像素为矩形而非正方形,效果参考如下截图:

左侧红色像素显示异常
像素明显呈矩形形态

  • 图1:左侧红色像素存在明显显示异常(为96x96像素图像放大后的效果)
  • 图2:所有像素均呈现矩形形态(为48x48像素图像放大后的效果)

经测试,尺寸小于20x20的小图像显示基本正常(可能也存在矩形像素问题但肉眼难以辨识),但当图像尺寸大于30x30时,矩形像素的问题会非常明显。

现咨询该问题的产生原因是什么?应当如何修复?是否存在可设置像素宽高比/尺寸的相关方法?

用于复现图2效果的代码如下:

import numpy as np
from PIL import Image

def random_rgb():
    """
    Creates tuple of random values between 0 - 255 inclusive.
    :return: tuple
    """
    r, g, b = np.random.randint(0, 256), np.random.randint(0, 256), np.random.randint(0, 256)
    rgb = (r, g, b)
    return rgb

def rainbow_bg(n):
    """
    Creates a list of lists of dimensions n x n
    :return: list
    """
    img = []
    for i in range(n):
        img.append([random_rgb() for i in range(n)])

    return img

def generate_image(pixels, dim, img_name):
    """
    Generates pixel image from list.
    :param pixels: list
    :param dim: tuple
    :param img_name: string
    :return:
    """

    arr = np.array(pixels, dtype=np.uint8)

    new_image = Image.fromarray(arr)
    new_image = new_image.resize(dim, resample=0)

    filename = f'{img_name}.png'
    new_image.save(filename)
    return


bg = rainbow_bg(48)
generate_image(pixels=bg, dim=(400, 400), img_name=f'background')
问题原因

该现象不是PIL生成的图像本身像素为非正方形导致,核心问题出在缩放逻辑:

  1. 通过Image.fromarray从numpy数组生成的原始图像是n×n正方形,标准栅格图像的像素默认均为1:1正方形比例,本身不存在形状异常。
  2. 代码中调用resize时使用了最近邻插值(resample=0对应NEAREST模式,是像素风格图常用的硬边插值方式),但设置的目标尺寸不是原始尺寸的整数倍。以复现图2的代码为例:原始图像尺寸为48×48,目标尺寸为400×400,缩放比例约为8.33,不是整数。
  3. 非整数倍缩放搭配最近邻插值时,原始图像的单个像素映射到目标图的过程中,占有的像素宽/高会在8~9个像素之间浮动,部分像素块宽高不等,肉眼看起来就是矩形、大小不均的效果。图像尺寸越大,这种浮动带来的视觉差异越明显;小尺寸图总像素少,差异很难被肉眼察觉。

PIL没有提供单独设置像素宽高比的特殊接口,不存在额外调整像素形状的配置项。

修复方案
  • 整数倍缩放(最推荐,完美保留硬边像素风格):缩放时直接选择原始尺寸的整数倍作为目标尺寸。比如48×48的原图,放大8倍对应384×384、放大9倍对应432×432,保证每个原始像素对应整数个输出像素,所有像素块大小完全一致,呈现标准正方形。
  • 固定尺寸适配:如果必须输出400×400这类非整数倍的固定尺寸,可以先将原图用整数倍放大到大于目标尺寸的大小,再通过居中裁剪得到目标尺寸;也可以放弃最近邻插值,改用双线性、双三次等平滑插值方式,但后者会模糊像素块边缘,丢失像素风格的硬边效果。

修正后的参考代码:

import numpy as np
from PIL import Image

def random_rgb():
    """生成0-255区间的随机RGB三元组"""
    r, g, b = np.random.randint(0, 256, size=3)
    return (int(r), int(g), int(b))

def rainbow_bg(n):
    """生成n×n尺寸的随机彩色像素数组"""
    img = []
    for i in range(n):
        img.append([random_rgb() for _ in range(n)])
    return img

def generate_image(pixels, scale, img_name):
    """
    按整数倍缩放生成像素图
    :param pixels: 像素数组
    :param scale: 整数缩放倍数
    :param img_name: 保存文件名
    """
    arr = np.array(pixels, dtype=np.uint8)
    new_image = Image.fromarray(arr)
    origin_w, origin_h = new_image.size
    target_size = (origin_w * scale, origin_h * scale)
    # 使用最近邻插值保留硬边像素效果
    new_image = new_image.resize(target_size, resample=Image.Resampling.NEAREST)
    new_image.save(f'{img_name}.png')

bg = rainbow_bg(48)
# 放大8倍得到384×384图像,所有像素块均为标准正方形
generate_image(pixels=bg, scale=8, img_name='background')

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 02:42:44