002.01 图片移除背景成PNG文件
建立一个程序, 用来处理一般图片, 移除类似颜色成为透明的背景.
建档日期: 2019/08/26
更新日期: 2020/09/29
相关软件信息:
WIN 10 | Python 3.8.3 | PIL Pillow 7.1.2 | PySimpleGUI 4.29.0.14 |
目标:
- 可开启任意图片, 并显示.
- 点击图片上任意一点以指定要移除的区域
- 以颜色的偏差作为是否同一区域的认定
- 预设颜色偏差值为16, RGB三色都在偏差值之内, 可以更改偏差值.
- 可以存为 PNG 文件.
- 不使用递回呼叫, 避免出错.
可以 Undo 一次.
程序画面
代码及说明
- 库的导入
import ctypes
from io import BytesIO
from pathlib import Path
from PIL import Image
import PySimpleGUI as sg
- 建立图片处理的类
class Picture():
def __init__(self):
self.im = None # 处理中的图片
self.old_im = None # 保留图片供 Undo
self.default_tolerance = 16 # 预设颜色偏差值
self.tolerances = [0, 8, 16, 32, 64, 128] # 可选颜色偏差值
self.picture = None # 显示的图片对象
self.width, self.height = self.size = (1600, 800) # 图片显示区尺寸
self.transparency = (0, 0, 0, 0) # 用来取代的透明值
- 根据点的颜色值及偏差值, 计算容许的颜色范围
def limit(self, color, tolerance):
result = []
for c in color:
result += [
min(max(c-tolerance, 0), 255), min(max(c+tolerance, 0), 255)]
return result
- 背景处理的准备
def change(self, x, y):
r, g, b, a = self.im.getpixel((x, y))
e = self.tolerance
self.r0, self.r1, self.g0, self.g1, self.b0, self.b1 = self.limit(
(r, g, b), self.tolerance)
self.old_im = self.im.copy()
self.update(x, y)
- 检查该点是否被认为是同色的
def is_same(self, x, y):
r, g, b, a = self.im.getpixel((x, y))
return True if (
self.r0 <= r <= self.r1 and
self.g0 <= g <= self.g1 and
self.b0 <= b <= self.b1 and
a != 0
) else False
- 点检查更新
- 设置所有的点都未检
- 检查起点, 设为已检, 如果为同色, 则更改为透明点
- 新增检查点为上下左右四点, 直到无点可检
def update(self, x, y):
checked = [[False for y in range(self.im.height)]
for x in range(self.im.width)]
to_do = [(x, y)]
while to_do:
temp = []
for x, y in to_do:
checked[x][y] = True
if self.is_same(x, y):
self.im.putpixel((x, y), self.transparency)
if x-1 >= 0 and not checked[x-1][y]:
temp.append((x-1, y))
if x+1 < self.im.width and not checked[x+1][y]:
temp.append((x+1, y))
if y-1 >= 0 and not checked[x][y-1]:
temp.append((x, y-1))
if y+1 < self.im.height and not checked[x][y+1]:
temp.append((x, y+1))
to_do = temp.copy()
- 更新图片的显示
def new(self):
if self.picture:
graph.delete_figure(self.picture)
with BytesIO() as output:
self.im.save(output, format="PNG")
data = output.getvalue()
self.picture = graph.draw_image(data=data, location=(self.x0, self.y0))
- 显示屏的图片点击处理
def click(self, position):
if self.picture is None:
return
x1, y1 = position
x, y = x1-self.x0, self.y0-y1
if not (0<=x<self.im.width and 0<=y<self.im.height):
return
self.change(x, y)
self.new()
- 新图片文件的开启
def open(self):
filename = sg.popup_get_file('Open new image', no_window=True, modal=True)
if filename and Path(filename).is_file():
try:
self.im = Image.open(filename).convert(mode='RGBA')
except:
return
self.x0 = (self.width-self.im.width)//2
self.y0 = (self.height+self.im.height)//2
self.tolerance = self.default_tolerance
window['TOLERANCE'].update(value=self.tolerance)
self.new()
- 图片保存为 PNG 文件
def save(self):
if self.im is None:
return
filename = sg.popup_get_file('Save new image', no_window=True,
modal=True, save_as=True)
if filename:
if not filename.lower().endswith(".png"):
filename += ".png"
self.im.save(filename)
return
- Undo 已保留的前图片
def undo(self):
if self.old_im:
self.im = self.old_im.copy()
self.new()
- 程序预设
ctypes.windll.user32.SetProcessDPIAware() # Set unit of GUI to pixels
font = ('Courier New', 16, 'bold')
p = Picture()
- 程序窗口布局
layout = [
[sg.Button("OPEN", font=font),
sg.Button("SAVE", font=font),
sg.Button("UNDO", font=font),
sg.Text("Tolerance", font=font),
sg.Combo(p.tolerances, p.default_tolerance, font=font, key='TOLERANCE',
enable_events=True)],
[sg.Graph(p.size, (0, 0), p.size, enable_events=True, key='GRAPH',
background_color='darkgreen')],
]
- 程序窗口生成
window = sg.Window('Move Outline of Image', layout, finalize=True)
graph = window['GRAPH']
- 事件处理
while True:
event, values = window.read()
if event == sg.WINDOW_CLOSED:
break
elif event == 'OPEN':
p.open()
elif event == 'SAVE':
p.save()
elif event == 'UNDO':
p.undo()
elif event == 'TOLERANCE':
p.tolerance = values['TOLERANCE']
elif event == 'GRAPH':
p.click(values['GRAPH'])
- 程序窗口关闭
window.close()
本作品采用《CC 协议》,转载必须注明作者和本文链接