Python 简易背景抠图方案实践与探索
本文详细阐述了利用 Python 进行图像背景抠图的三种技术路径。首先介绍了针对纯色背景的像素级透明度处理;其次探讨了通过采样学习多色背景颜色的方法;最后提出了一种基于 RGB 最大最小值范围的统计阈值法。文章通过代码示例和效果对比,分析了各方案的优缺点,展示了如何通过简单的编程逻辑解决图像处理中的自动化需求,并总结了代码化处理工具在缺乏专业软件时的替代优势及性能优化方向。

本文详细阐述了利用 Python 进行图像背景抠图的三种技术路径。首先介绍了针对纯色背景的像素级透明度处理;其次探讨了通过采样学习多色背景颜色的方法;最后提出了一种基于 RGB 最大最小值范围的统计阈值法。文章通过代码示例和效果对比,分析了各方案的优缺点,展示了如何通过简单的编程逻辑解决图像处理中的自动化需求,并总结了代码化处理工具在缺乏专业软件时的替代优势及性能优化方向。

在日常数据处理或可视化工作中,我们经常需要从截图中提取特定元素。例如,从某 APP 中截取背单词曲线后,发现其中蕴藏的数学规律:每六个月达到一次峰值,且高度不断减小。为了在图上画一条线来拟合这个折线,我尝试使用 MATLAB 绘制了正弦函数受衰减指数函数调制的图像。

但问题来了,我不知道怎么把这两个图贴在一起,需要把 MATLAB 绘制的曲线从白色背景中抠出来。联想到曾经用 Excel 抠二维码的操作,我觉得有必要进化一下我的抠图技能,利用 Python 实现自动化处理。
本方案主要依赖 Python 的标准库和图像处理库 Pillow(PIL 的分支)。
pip install Pillow
确保已安装 Python 3.x 环境,并配置好开发工具。
从原理上讲,从简单纯色背景中把图案抠出来,无非就是对每一个像素点检查它的颜色。如果是背景色就把透明度变成 0,否则保留。这是非常容易实现的。
from PIL import Image
# 读取图片
im = Image.open('SaoMaYouJingXi.jpg')
# 转化成 RGBA(RGB+透明度)格式
im = im.convert('RGBA')
pix = im.load()
width, height = im.size
for x in range(width):
for y in range(height):
r, g, b, a = pix[x, y]
# 判断是否为白色背景(这里设定阈值)
if (r > 100) and (g > 100) and (b > 100):
a = 0 # 透明度改成 0
im.putpixel((x, y), (r, g, b, a))
im.save("SaoMaYouJingXi.PNG")
小试牛刀,放大了看也干净利索。

纯色背景抠图虽然精确,但在面对渐变或复杂背景时显得力不从心。我又联想到曾经用 Excel 从照片里抠手绘班徽的经历,当时抠出来的效果在浅色背景下还可以,但是在深色背景中就非常不堪入目。于是,我打算想办法把班徽重新抠一下。
既然不是简单的纯黑或纯白背景,没办法直接判断颜色的 RGB 值大概在什么范围,那么我们首先要让程序去识别并记住背景有哪些颜色,然后再把这些颜色的像素抠掉。
我们需要分两大步:
这张班徽的照片里,我在四条边附近分别截取了一张背景样本,后来为了优化效果,又在没抠掉的地方补了一张。

接下来就是让计算机记住这些出现过的颜色。我们知道,一个像素的颜色可以用 R(0-255)、G(0-255)、B(0-255) 三个变量来表示。鉴于我们现在只关心某一种颜色有没有出现过,不妨创建一个三维哈希表。
可以想象一个大的正方体,里边有 256^3 个小格子。初始状态下每个小格子里都是 0,当计算机看到出现某一种颜色 (m,n,p),就把第 m 行、第 n 列、第 p 层的那个小格子里的 0 变成 1。
为了减小计算机的工作量,我们可以把相邻的 3 个 R/G/B 值简化成一个(如 R=0,1,2→R'=0),把这个 256x256x256 的正方体压缩一下,长宽高各变为原来的 1/3,列表大小缩小到原来的 1/27。
matrix = []
for i in range(85):
matrix.append([])
for j in range(85):
matrix[i].append([])
for k in range(85):
matrix[i][j].append(0)
def learn(str_path):
im = Image.open(str_path)
pix = im.load()
width = im.size[0]
height = im.size[1]
for x in range(width):
for y in range(height):
r, g, b = pix[x, y]
# 应用压缩逻辑
matrix[r // 3][g // 3][b // 3] = 1
在本例中,让程序 learn 完五张采样得到的背景图,再执行抠图的主程序,保存图片。

失望,非常失望。无论我再怎么给它查漏补缺,补充新的背景来 learn,抠出来还是会有大量残留。
接下来,我们增设一个模糊度 dim,意思是在抠图的判断条件中,只要这个点的 RGB 坐标出现在被标记坐标的附近,三个坐标分量都在某被标记点对应坐标分量 ±dim 的范围内,即该点的 RGB 坐标在以某被标记点为中心、棱长为 2*dim 的正方体内,我们就把它抠掉。
随着我不断增大 dim 的值,残留的区域确实越来越少,但是结果总是不尽人意。

于是,我不再满足于小立方体级别的模糊,决定大手一挥,激进一点。这次,我打算计算出背景中所有像素点的 R、G、B 各自的最大值和最小值,然后把所有 R、G、B 都在最大值之下、最小值之上(包含最值)的像素全部抠掉。
为了判断方案的可行性,我把图片信息按颜色展开,在坐标系中呈现出来。这里,X、Y、Z 轴分别是压缩之后的 RGB 值,范围是 [0,85] 中的整数。

在三维坐标系中,引入透明度,可以刻画第四个维度的信息。我们选取该坐标处像素的数量作为透明度的自变量。为了避免最大值过大导致大部分点的透明度都非常高,我们换一种映射,把通过比值求出来的相对透明度开四次根号。
最终,我们画出来的图像是这样的:越不透明的点表示对应颜色的像素越多。

分析一下刚刚费了不少力气画的这个图,我们发现,三种主色还是相对来说界限分明的。如果你不信,我可以随便找一张图给你展开一下:

我们来看一下背景样本中所有颜色的 R、G、B 的最大值和最小值。

这个结果其实非常符合预期,因为从刚才那个坐标系中可以看到,跟背景相近的颜色都集中在 R、G、B 都非常大的地方。
我们把 R、G、B 都在对应区间里的点抠掉,然后看一下效果:


**AMAZING!**简直完美。尽管我事实上并不认可这个原理,毕竟本质上这属于莽夫行为。但是不得不说,这个效果确实是非常不错的。

对比一下 Excel 抠图、learn 抠图和莽夫抠图:

简直是一部愈演愈烈的革命乐章!
上述方案虽然有效,但在处理大图时,Python 的循环遍历效率较低。如果追求更高的性能,可以考虑以下优化方向:
示例代码片段(基于 NumPy 思路):
import numpy as np
from PIL import Image
img_array = np.array(Image.open('image.png'))
# 假设背景色为白色 (255, 255, 255)
mask = np.all(img_array[:, :, :3] == [255, 255, 255], axis=2)
img_array[:, :, 3][mask] = 0
Image.fromarray(img_array).save('output.png')
至此,我已经完成了最初的目标,并且积累了一套极其简陋但却好像还挺像回事儿的抠图方案。
在我看来,代码抠图的魅力不仅在于它精确到像素的精准打击,也在于它可以让你随心所欲定制需求的可塑性,还在于它只需要你更改一两个参数、点一下编译运行,就能自动输出成果的便捷。此外,我还顺便做了一些同样没什么用的小功能,比如马赛克处理。

不论是抠图也好,打码也好,逻辑上不难理解,代码上也不难实现。在日常生活中,有时候手边就是缺乏处理简单任务的专业工具,比如 PhotoShop。这时候与其安装 Ps,倒不如自己写一个程序,根据自己的需求编码实现功能,看似麻烦,实则不失为一条捷径。
未来,随着深度学习技术的发展,基于语义分割的背景移除将更加普及。但在轻量级、特定场景的任务中,这种基于规则的传统算法依然具有极高的实用价值和教学意义。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online