Crop a PNG image to its minimum size

前端 未结 6 824
旧巷少年郎
旧巷少年郎 2020-12-16 00:00

How to cut off the blank border area of a PNG image and shrink it to its minimum size using Python?

相关标签:
6条回答
  • 2020-12-16 00:35

    I had the same problem today. Here is my solution to crop the transparent borders. Just throw this script in your folder with your batch .png files:

    from PIL import Image
    import numpy as np
    from os import listdir
    
    def crop(png_image_name):
        pil_image = Image.open(png_image_name)
        np_array = np.array(pil_image)
        blank_px = [255, 255, 255, 0]
        mask = np_array != blank_px
        coords = np.argwhere(mask)
        x0, y0, z0 = coords.min(axis=0)
        x1, y1, z1 = coords.max(axis=0) + 1
        cropped_box = np_array[x0:x1, y0:y1, z0:z1]
        pil_image = Image.fromarray(cropped_box, 'RGBA')
        print(pil_image.width, pil_image.height)
        pil_image.save(png_image_name)
        print(png_image_name)
    
    for f in listdir('.'):
        if f.endswith('.png'):
            crop(f)
    
    0 讨论(0)
  • 2020-12-16 00:48

    You can use PIL to find rows and cols of your image that are made up purely of your border color.

    Using this information, you can easily determine the extents of the inlaid image.

    PIL again will then allow you to crop the image to remove the border.

    0 讨论(0)
  • 2020-12-16 00:51

    PIL's getbbox is working for me

    im.getbbox() => 4-tuple or None

    Calculates the bounding box of the non-zero regions in the image. The bounding box is returned as a 4-tuple defining the left, upper, right, and lower pixel coordinate. If the image is completely empty, this method returns None.

    Code Sample that I tried, I have tested with bmp, but it should work for png too.

    import Image
    im = Image.open("test.bmp")
    im.size  # (364, 471)
    im.getbbox()  # (64, 89, 278, 267)
    im2 = im.crop(im.getbbox())
    im2.size  # (214, 178)
    im2.save("test2.bmp")
    
    0 讨论(0)
  • 2020-12-16 00:51

    https://gist.github.com/3141140

    import Image
    import sys
    import glob
    
    # Trim all png images with alpha in a folder
    # Usage "python PNGAlphaTrim.py ../someFolder"
    
    try:
        folderName = sys.argv[1]
    except :
        print "Usage: python PNGPNGAlphaTrim.py ../someFolder"
        sys.exit(1)
    
    filePaths = glob.glob(folderName + "/*.png") #search for all png images in the folder
    
    for filePath in filePaths:
        image=Image.open(filePath)
        image.load()
    
        imageSize = image.size
        imageBox = image.getbbox()
    
        imageComponents = image.split()
    
        if len(imageComponents) < 4: continue #don't process images without alpha
    
        rgbImage = Image.new("RGB", imageSize, (0,0,0))
        rgbImage.paste(image, mask=imageComponents[3])
        croppedBox = rgbImage.getbbox()
    
        if imageBox != croppedBox:
            cropped=image.crop(croppedBox)
            print filePath, "Size:", imageSize, "New Size:",croppedBox
            cropped.save(filePath)
    
    0 讨论(0)
  • 2020-12-16 00:53

    I think it's necessary to supplement @Frank Krueger's answer. He makes a good point, but it doesn't include how to properly crop extra border color out of an image. I found that here. Specifically, I found this useful:

    from PIL import Image, ImageChops
    
    def trim(im):
        bg = Image.new(im.mode, im.size, im.getpixel((0,0)))
        diff = ImageChops.difference(im, bg)
        diff = ImageChops.add(diff, diff, 2.0, -100)
        bbox = diff.getbbox()
        if bbox:
            return im.crop(bbox)
    
    im = Image.open("bord3.jpg")
    im = trim(im)
    im.show()
    
    0 讨论(0)
  • 2020-12-16 00:55

    Here is ready-to-use solution:

    import numpy as np
    from PIL import Image
    
    def bbox(im):
        a = np.array(im)[:,:,:3]  # keep RGB only
        m = np.any(a != [255, 255, 255], axis=2)
        coords = np.argwhere(m)
        y0, x0, y1, x1 = *np.min(coords, axis=0), *np.max(coords, axis=0)
        return (x0, y0, x1+1, y1+1)
    
    im = Image.open('test.png')
    print(bbox(im))  # (33, 12, 223, 80)
    im2 = im.crop(bbox(im))
    im2.save('test_cropped.png')
    

    Example input (download link if you want to try):

    Output:

    0 讨论(0)
提交回复
热议问题