如何在64×64二值图像中降低像素非均匀性?
解决64×64二值图像的像素化凸起问题
问题描述
我正在使用matplotlib和PIL库生成仅含黑白两色的二值图像,图像像素尺寸限制为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
相关产品推荐
相关产品推荐

