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

如何在64×64二值图像中降低像素非均匀性?

解决64×64二值图像的像素化凸起问题

问题描述

我正在使用matplotlib和PIL库生成仅含黑白两色的二值图像,图像像素尺寸限制为64×64且必须为二值格式。在低像素规格下,生成的图像出现像素化问题,白色像素存在明显凸起,无法均匀平缓过渡(放大图如下)。

64×64像素放大图

使用的生成代码如下:

import matplotlib.pyplot as plt
import numpy as np
import io
from PIL import Image

def get_image_array(_fig):
    io_buffer = io.BytesIO()
    plt.savefig(io_buffer, format="raw")
    io_buffer.seek(0)
    _image_array = np.reshape(
        np.frombuffer(io_buffer.getvalue(), dtype=np.uint8),
        newshape=(int(_fig.bbox.bounds[3]), int(_fig.bbox.bounds[2]), -1)
    )
    io_buffer.close()
    return _image_array

def draw_box_and_circle(bbox, xc, yc, r, pixels=(100, 100), angular_parts=100):
    fig = plt.figure(figsize=(pixels[0]*0.01, pixels[1]*0.01))
    fig.add_axes(plt.Axes(fig, [0., 0., 1., 1.]))
    # draw bbox
    x0, y0, x1, y1 = bbox
    plt.fill([x0, x1, x1, x0], [y0, y0, y1, y1], color='0')
    # draw circle
    theta = np.linspace(0.0, 2.0*np.pi, angular_parts)
    x = xc + (r*np.cos(theta))
    y = yc + (r*np.sin(theta))
    plt.fill(x, y, color='1')
    #
    plt.axis('off')
    plt.axis('equal')
    plt.xlim([BBOX[0], BBOX[2]])
    plt.ylim([BBOX[1], BBOX[3]])
    image_array = get_image_array(fig)
    print(image_array.shape)
    image = Image.fromarray(image_array)
    image.save("before_conversion.png")
    image = image.convert(mode="1")
    image.save("after_conversion.png")
    print(np.array(image).shape)    
    return
   

BBOX = (0.0, 0.0, 1.0, 1.0)
draw_box_and_circle(BBOX, 0.5, 0.5, 0.25)

解决方案

1. 高分辨率渲染后下采样+抗锯齿

直接生成64×64图像时,matplotlib的渲染精度不足以平滑边缘。先生成更高分辨率的图像(比如256×256),再用LANCZOS抗锯齿算法下采样到目标尺寸,能有效减少像素凸起:

import matplotlib.pyplot as plt
import numpy as np
import io
from PIL import Image

def get_image_array(_fig):
    io_buffer = io.BytesIO()
    plt.savefig(io_buffer, format="raw")
    io_buffer.seek(0)
    _image_array = np.reshape(
        np.frombuffer(io_buffer.getvalue(), dtype=np.uint8),
        newshape=(int(_fig.bbox.bounds[3]), int(_fig.bbox.bounds[2]), -1)
    )
    io_buffer.close()
    return _image_array

def draw_box_and_circle(bbox, xc, yc, r, pixels=(256, 256), target_size=(64,64), angular_parts=100):
    fig = plt.figure(figsize=(pixels[0]*0.01, pixels[1]*0.01))
    fig.add_axes(plt.Axes(fig, [0., 0., 1., 1.]))
    # 绘制背景和圆形
    x0, y0, x1, y1 = bbox
    plt.fill([x0, x1, x1, x0], [y0, y0, y1, y1], color='0')
    theta = np.linspace(0.0, 2.0*np.pi, angular_parts)
    x = xc + (r*np.cos(theta))
    y = yc + (r*np.sin(theta))
    plt.fill(x, y, color='1')
    
    plt.axis('off')
    plt.axis('equal')
    plt.xlim([BBOX[0], BBOX[2]])
    plt.ylim([BBOX[1], BBOX[3]])
    
    image_array = get_image_array(fig)
    image = Image.fromarray(image_array)
    # 抗锯齿下采样
    image = image.resize(target_size, Image.Resampling.LANCZOS)
    # 转灰度后用均值阈值二值化,比直接转mode="1"更平滑
    gray_img = image.convert('L')
    threshold = np.array(gray_img).mean()
    binary_img = gray_img.point(lambda p: 255 if p > threshold else 0).convert('1')
    binary_img.save("smoothed_binary.png")
    plt.close(fig)
    return binary_img

BBOX = (0.0, 0.0, 1.0, 1.0)
draw_box_and_circle(BBOX, 0.5, 0.5, 0.25)

2. 直接用numpy计算生成精准二值图

跳过matplotlib的渲染流程,通过数学公式直接判断每个像素是否属于目标形状,完全避免渲染带来的像素误差:

import numpy as np
from PIL import Image

def generate_precision_binary(target_size=(64,64)):
    width, height = target_size
    # 创建网格坐标
    x_coords = np.linspace(0, 1, width)
    y_coords = np.linspace(0, 1, height)
    xx, yy = np.meshgrid(x_coords, y_coords)
    
    # 计算每个像素是否在圆形内(白色),背景为黑色
    circle_mask = (xx - 0.5)**2 + (yy - 0.5)**2 <= 0.25**2
    binary_array = np.where(circle_mask, 255, 0).astype(np.uint8)
    
    # 转换为二值图像
    binary_img = Image.fromarray(binary_array).convert('1')
    binary_img.save("precision_binary.png")
    return binary_img

generate_precision_binary()

3. 二值化前添加高斯模糊

如果必须保留matplotlib渲染流程,可在转二值图像前对灰度图做高斯模糊,弱化尖锐的像素凸起:

# 在原代码的image = Image.fromarray(image_array)后添加:
from PIL import ImageFilter

image = image.convert('L')
# 高斯模糊,半径可根据效果调整(0.5-1.0之间)
blurred_img = image.filter(ImageFilter.GaussianBlur(radius=0.8))
binary_img = blurred_img.convert('1')
binary_img.save("blurred_binary.png")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:15:43