2

Python去除图片四周留白

 1 year ago
source link: https://feizhaojun.com/?p=3809
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Python去除图片四周留白

分类: Python  标签:
作者: mukti  时间:2023-04-13 17:51:10 (星期四)

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

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))  # 输出图片

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

wxpay-qrcode.png

alipay-qrcode.png


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK