Python去除图片四周留白

近日遇到一批类似下图的商品图,图片中主要内容(相机)占区域太小,周围的白色背景区域太大,我们希望通过裁剪,让内容尽量铺满图片,减少周围的留白。

image

因为中间有内容的区域,大小是不固定的,所以不能使用按统一比例裁剪。例如下面两幅图:

image

按照常规的方法,我们可以遍历图片中的每一个像素点,找到位于最上、最右、最下、最左的4个非白色的像素点,从而算出裁切的位置。但是这样做存在两个问题:

  1. 遍历所有像素点,如果图片很大,耗时会很久。
  2. 如果白色留白的部分存在一些噪点,会影响到裁切位置。

我采用了一个折中的思路,只检查图片的中线和对角线上的点,如下图蓝色轨迹所示,通过遍历蓝色轨迹,确定图中红色圈出的 6 个点,最后通过这 6 个点确定裁切位置。

image

这样做的好处是:

  1. 计算量非常少,只需要一个循环。
  2. 假设图中 A 位置有一个噪点,也不会影响。

不过这样做也会有不足之处,如果噪点位于蓝色轨迹上,就无法避免。另外,如果图片内容不是在正中,而是在下图中红色区域(蓝色轨迹和内容无法确定 6 个点),就会影响裁剪的结果。我觉得对于后者,大家可以在我的代码上做些改进。

image

这是示例图的裁切效果:

image

代码:

from PIL import Image, ImageDraw
import numpy as np
import os

imgDir = r"./img"  # 原始图片目录
side = 512  # 输出边长


def min_not_zero(data):
    new_data = [x for x in data if x != 0]
    return min(new_data if len(new_data) != 0 else [0])


i = 0
for file in os.listdir(imgDir):
    print(file)
    if not (file.endswith('.jpg') or file.endswith('.png')):
        continue
    path = os.path.join(imgDir, file)
    img = Image.open(path)
    imgArr = np.array(img)
    height = imgArr.shape[0]
    width = imgArr.shape[1]
    gap = width * 0.02

    draw = ImageDraw.Draw(img)

    xStart = [width, width, width, width]
    yStart = [height, height, height, height]
    xEnd = [0, 0, 0, 0]
    yEnd = [0, 0, 0, 0]
    i += 1

    length = min(width, height)

    for x in range(length):
        y = x  # 正方形图片可以直接相等
        # 先简单处理非正方形
        if x > height:
            y = height - 1

        # 左上到右下
        if imgArr[y][x][0] < 255 or imgArr[y][x][1] < 255 or imgArr[y][x][2] < 255:
            if xStart[0] > x:
                xStart[0] = x
            if yStart[0] > y:
                yStart[0] = y
            if x > xEnd[0]:
                xEnd[0] = x
            if y > yEnd[0]:
                yEnd[0] = y
        # 左下到右上
        if imgArr[height - y - 1][x][0] < 255 or imgArr[height - y - 1][x][1] < 255 or imgArr[height - y - 1][x][2] < 255:
            if xStart[1] > x:
                xStart[1] = x
            if yStart[1] > height - y:
                yStart[1] = height - y
            if x > xEnd[1]:
                xEnd[1] = x
            if yEnd[1] == 0:
                yEnd[1] = height - y
        # 中横线
        if imgArr[int(height / 2)][x][0] < 255 or imgArr[int(height / 2)][x][1] < 255 or imgArr[int(height / 2)][x][2] < 255:
            if xStart[2] > x:
                xStart[2] = x
            if x > xEnd[2]:
                xEnd[2] = x
        # 中竖线
        if imgArr[y][int(width / 2)][0] < 255 or imgArr[y][int(width / 2)][1] < 255 or imgArr[y][int(width / 2)][2] < 255:
            if yStart[3] > y:
                yStart[3] = y
            if y > yEnd[3]:
                yEnd[3] = y
    xS = min_not_zero(xStart)
    xE = max(xEnd)
    yS = min_not_zero(yStart)
    yE = max(yEnd)
    if xS == width:
        xS = 0
    if yS == height:
        yS = 0
    if xE == 0:
        xE = width
    if yE == 0:
        yE = height
    # 裁切正方形,取 x y 并集
    start = min(xS, yS)
    end = max(xE, yE)
    xS = start
    yS = start
    xE = end
    yE = end
    cropped = img.crop((xS - (gap if xS > gap else xS), yS - (gap if yS > gap else yS), xE + (gap if width - xE > gap else width - xE), yE + (gap if height - yE > gap else height - yE))).resize((side, side))
    cropped.save(r"./temp/{}.jpg".format(file[:-4], i))  # 输出图片

您的赞助将会支持作者创作及本站运维

发表评论


TOP