我在 Pillow 中有这段代码可以完美裁剪图像。例如,我有相同背景下不同颜色的内容,此代码将其裁剪为尽可能最小的尺寸,边距为 32 像素。
但是,速度很慢!在运行它的计算机(具有 4 GiB RAM 的 Cloud Run 4 vCPU)上需要近 12 秒,而启动完整浏览器并解释 HTML 和 CSS 的 Chromium 进程只需要 3 秒。
所以,我想知道我可以做些什么来优化这个过程。
我知道它是像素完美的,并且鉴于其性质,需要更多时间,但 12 秒似乎太长了。
或者,我可以使用 OpenCV,或者我可以采用一些涉及 JavaScript 和 Python 的技术来确定裁剪的理想大小,但我不知道如何去做。
未裁剪的完整图像:https://delduca-to25gludpq-uc.a.run.app/get
def trim(buffer, margin=32, trim_color="#92b88a"):
with PIL.Image.open(io.BytesIO(buffer)).convert("RGB") as image:
width, height = image.size
left, top, right, bottom = 0, 0, width - 1, height - 1
trim_color_rgb = tuple(int(trim_color[i : i + 2], 16) for i in (1, 3, 5))
while left < width:
column = [image.getpixel((left, y)) for y in range(height)]
if all(pix == trim_color_rgb for pix in column):
left += 1
else:
break
while top < height:
row = [image.getpixel((x, top)) for x in range(width)]
if all(pix == trim_color_rgb for pix in row):
top += 1
else:
break
while right > left:
column = [image.getpixel((right, y)) for y in range(height)]
if all(pix == trim_color_rgb for pix in column):
right -= 1
else:
break
while bottom > top:
row = [image.getpixel((x, bottom)) for x in range(width)]
if all(pix == trim_color_rgb for pix in row):
bottom -= 1
else:
break
left = max(left - margin, 0)
top = max(top - margin, 0)
right = min(right + margin, width - 1)
bottom = min(bottom + margin, height - 1)
return image.crop((left, top, right, bottom))
一只在这里无法说出名字的小鸟给了我这个解决方案,并且它在不到一秒的时间内执行!
def trim(buffer, margin=32, trim_color="#92b88a"):
with PIL.Image.open(io.BytesIO(buffer)).convert("RGB") as image:
data = np.array(image)
trim_color_rgb = tuple(int(trim_color[i : i + 2], 16) for i in (1, 3, 5))
non_trim_rows = np.where(np.any(data != trim_color_rgb, axis=(1, 2)))[0]
non_trim_cols = np.where(np.any(data != trim_color_rgb, axis=(0, 2)))[0]
left = max(0, non_trim_cols[0] - margin)
top = max(0, non_trim_rows[0] - margin)
right = min(data.shape[1] - 1, non_trim_cols[-1] + margin)
bottom = min(data.shape[0] - 1, non_trim_rows[-1] + margin)
return PIL.Image.fromarray(data[top:bottom, left:right])
据我所知,
get_pixel()
可能会非常慢。您应该将图像转换为 numpy.array
numpy_array = np.array(image) # convert to `numpy array`
并在不使用
for
-loops 的情况下运行一些代码
column = array[0:y, right]
if (column == trim_color_rgb).all():
我必须编写
minimal working
代码来检查它并且
25 to 0 seconds
,numpy
大约需要1 second
我用于测试的完整工作代码:
import time
import numpy as np
from PIL import Image
def trim_1(buffer, margin=32, trim_color="#92b88a"):
with Image.open('logo.png').convert("RGB") as image:
#with Image.open(io.BytesIO(buffer)).convert("RGB") as image:
width, height = image.size
left, top, right, bottom = 0, 0, width - 1, height - 1
trim_color_rgb = tuple(int(trim_color[i : i + 2], 16) for i in (1, 3, 5))
while left < width:
column = [image.getpixel((left, y)) for y in range(height)]
if all(pix == trim_color_rgb for pix in column):
left += 1
else:
break
while top < height:
row = [image.getpixel((x, top)) for x in range(width)]
if all(pix == trim_color_rgb for pix in row):
top += 1
else:
break
while right > left:
column = [image.getpixel((right, y)) for y in range(height)]
if all(pix == trim_color_rgb for pix in column):
right -= 1
else:
break
while bottom > top:
row = [image.getpixel((x, bottom)) for x in range(width)]
if all(pix == trim_color_rgb for pix in row):
bottom -= 1
else:
break
left = max(left - margin, 0)
top = max(top - margin, 0)
right = min(right + margin, width - 1)
bottom = min(bottom + margin, height - 1)
print(left, top, right, bottom)
return image.crop((left, top, right, bottom))
def trim_2(buffer, margin=32, trim_color="#92b88a"):
with Image.open('logo.png').convert("RGB") as image:
#with Image.open(io.BytesIO(buffer)).convert("RGB") as image:
width, height = image.size
numpy_array = np.array(image) # convert to `numpy array`
#height, width, _ = numpy_array.shape
left, top, right, bottom = 0, 0, width - 1, height - 1
trim_color_rgb = tuple(int(trim_color[i : i + 2], 16) for i in (1, 3, 5))
while left < width:
column = numpy_array[:,left]
if (column == trim_color_rgb).all():
left += 1
else:
break
while top < height:
row = numpy_array[top,:]
if (row == trim_color_rgb).all():
top += 1
else:
break
while right > left:
column = numpy_array[:,right]
if (column == trim_color_rgb).all():
right -= 1
else:
break
while bottom > top:
row = numpy_array[bottom,:]
if (row == trim_color_rgb).all():
bottom -= 1
else:
break
left = max(left - margin, 0)
top = max(top - margin, 0)
right = min(right + margin, width - 1)
bottom = min(bottom + margin, height - 1)
print(left, top, right, bottom)
return image.crop((left, top, right, bottom))
start = time.time()
print('running ...')
result = trim_1(None)
end = time.time()
print('trim_1 time:', end - start)
result.save('trim_1.png')
start = time.time()
print('running ...')
result = trim_2(None)
end = time.time()
print('trim_2 time:', end - start)
result.save('trim_2.png')
结果:
running ...
668 48 1227 551
trim_1 time: 25.654032468795776
running ...
668 48 1227 551
trim_2 time: 0.8261435031890869