복붙노트

[PYTHON] python / PIL로 자동 자르기

PYTHON

python / PIL로 자동 자르기

누구든지 내 이미지 자동 자르기 스크립트에서 무슨 일이 일어나는지 알아낼 수 있습니까? 큰 투명한 영역 / 공간이있는 png 이미지가 있습니다. 그 공간을 자동으로 잘라내어 본질을 남기고 싶습니다. 원래 이미지는 제곱 된 캔버스를 가지고 있으며, 분자를 캡슐화하는 사각형이 최적 인 것이 가장 좋습니다.

여기에 원본 이미지가 있습니다.

일부 인터넷 검색을 사용하면 PIL / Python 코드가 작동한다고보고되었지만 내 손안에는 아래 코드를 실행하면 이미지가 잘려 보입니다.

import Image
import sys

image=Image.open('L_2d.png')
image.load()

imageSize = image.size
imageBox = image.getbbox()

imageComponents = image.split()

rgbImage = Image.new("RGB", imageSize, (0,0,0))
rgbImage.paste(image, mask=imageComponents[3])
croppedBox = rgbImage.getbbox()
print imageBox
print croppedBox
if imageBox != croppedBox:
    cropped=image.crop(croppedBox)
    print 'L_2d.png:', "Size:", imageSize, "New Size:",croppedBox
    cropped.save('L_2d_cropped.png')

출력은 다음과 같습니다.

이미지 처리 / PLI에 익숙한 사람이라면 문제를 파악하는 데 도움이 될까요?

해결법

  1. ==============================

    1.numpy를 사용하여 이미지를 배열로 변환하고 비어 있지 않은 모든 열과 행을 찾아 다음에서 이미지를 만들 수 있습니다.

    numpy를 사용하여 이미지를 배열로 변환하고 비어 있지 않은 모든 열과 행을 찾아 다음에서 이미지를 만들 수 있습니다.

    import Image
    import numpy as np
    
    image=Image.open('L_2d.png')
    image.load()
    
    image_data = np.asarray(image)
    image_data_bw = image_data.max(axis=2)
    non_empty_columns = np.where(image_data_bw.max(axis=0)>0)[0]
    non_empty_rows = np.where(image_data_bw.max(axis=1)>0)[0]
    cropBox = (min(non_empty_rows), max(non_empty_rows), min(non_empty_columns), max(non_empty_columns))
    
    image_data_new = image_data[cropBox[0]:cropBox[1]+1, cropBox[2]:cropBox[3]+1 , :]
    
    new_image = Image.fromarray(image_data_new)
    new_image.save('L_2d_cropped.png')
    

    결과는 다음과 같다.

    불명확 한 점이 있으면 그냥 물어보십시오.

  2. ==============================

    2.나를 위해 그것이 작동합니다 :

    나를 위해 그것이 작동합니다 :

    import Image
    
    image=Image.open('L_2d.png')
    
    imageBox = image.getbbox()
    cropped=image.crop(imageBox)
    cropped.save('L_2d_cropped.png')
    

    mask = imageComponents [3]로 경계를 검색하면 파란색 채널 만 검색합니다.

  3. ==============================

    3.나는이 포스트에서 응답 된 응답의 대부분을 시험했다, 그러나, 나는 나의 자신의 응답 높은쪽으로 끝냈다. 나는 anaconda python3을 사용했다.

    나는이 포스트에서 응답 된 응답의 대부분을 시험했다, 그러나, 나는 나의 자신의 응답 높은쪽으로 끝냈다. 나는 anaconda python3을 사용했다.

    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)
    
    if __name__ == "__main__":
        bg = Image.open("test.jpg") # The image to be cropped
        new_im = trim(bg)
        new_im.show()
    
  4. ==============================

    4.pyvips를 사용하는 다른 버전이 있습니다.

    pyvips를 사용하는 다른 버전이 있습니다.

    이것은 약간 애호가입니다. (0, 0)의 픽셀을보고, 배경색으로 가정하고, 중간 값 필터를 수행하고, 더 많은 픽셀과 다른 픽셀을 포함하는 첫 번째와 마지막 행과 열을 찾습니다. 임계 값보다 이 추가 처리는 사진이나 압축 된 이미지에서 작동한다는 것을 의미합니다. 단순한 트림이 노이즈 또는 압축 아티팩트에 의해 제거 될 수 있습니다.

    import sys
    import pyvips
    
    # An equivalent of ImageMagick's -trim in libvips ... automatically remove
    # "boring" image edges.
    
    # We use .project to sum the rows and columns of a 0/255 mask image, the first
    # non-zero row or column is the object edge. We make the mask image with an
    # amount-differnt-from-background image plus a threshold.
    
    im = pyvips.Image.new_from_file(sys.argv[1])
    
    # find the value of the pixel at (0, 0) ... we will search for all pixels 
    # significantly different from this
    background = im(0, 0)
    
    # we need to smooth the image, subtract the background from every pixel, take 
    # the absolute value of the difference, then threshold
    mask = (im.median(3) - background).abs() > 10
    
    # sum mask rows and columns, then search for the first non-zero sum in each
    # direction
    columns, rows = mask.project()
    
    # .profile() returns a pair (v-profile, h-profile) 
    left = columns.profile()[1].min()
    right = columns.width - columns.fliphor().profile()[1].min()
    top = rows.profile()[0].min()
    bottom = rows.height - rows.flipver().profile()[0].min()
    
    # and now crop the original image
    
    im = im.crop(left, top, right - left, bottom - top)
    
    im.write_to_file(sys.argv[2])
    

    여기는 8k x 8k 픽셀의 NASA 지구 이미지에서 실행됩니다.

    $ time ./trim.py /data/john/pics/city_lights_asia_night_8k.jpg x.jpg
    real    0m1.868s
    user    0m13.204s
    sys     0m0.280s
    peak memory: 100mb
    

    전에:

    후:

    여기에 더 많은 토론이있는 블로그 게시물이 있습니다.

  5. ==============================

    5.최근이 게시물을 통해 PIL 라이브러리가 변경되었음을 알게되었습니다. openCV로 다시 구현했습니다.

    최근이 게시물을 통해 PIL 라이브러리가 변경되었음을 알게되었습니다. openCV로 다시 구현했습니다.

    import cv2
    
    def crop_im(im, padding=0.1):
        """
        Takes cv2 image, im, and padding % as a float, padding,
        and returns cropped image.
        """
        bw = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
        rows, cols = bw.shape
        non_empty_columns = np.where(bw.min(axis=0)<255)[0]
        non_empty_rows = np.where(bw.min(axis=1)<255)[0]
        cropBox = (min(non_empty_rows) * (1 - padding),
                    min(max(non_empty_rows) * (1 + padding), rows),
                    min(non_empty_columns) * (1 - padding),
                    min(max(non_empty_columns) * (1 + padding), cols))
        cropped = im[cropBox[0]:cropBox[1]+1, cropBox[2]:cropBox[3]+1 , :]
    
        return cropped
    
    im = cv2.imread('testimage.png')
    cropped = crop_im(im)
    cv2.imshow('', cropped)
    cv2.waitKey(0)
    
  6. ==============================

    6.나는이 지위가 낡았다는 것을 알고있다. 그러나 어떤 이유로 든 제안 된 해답 중 아무 것도 나를 위해 일하지 않았다. 그래서 나는 기존 답변에서 내 자신의 버전을 해킹했다.

    나는이 지위가 낡았다는 것을 알고있다. 그러나 어떤 이유로 든 제안 된 해답 중 아무 것도 나를 위해 일하지 않았다. 그래서 나는 기존 답변에서 내 자신의 버전을 해킹했다.

    import Image
    import numpy as np
    import glob
    import shutil
    import os
    
    grey_tolerance = 0.7 # (0,1) = crop (more,less)
    
    f = 'test_image.png'
    file,ext = os.path.splitext(f)
    
    def get_cropped_line(non_empty_elms,tolerance,S):
        if (sum(non_empty_elms) == 0):
            cropBox = ()
        else:
            non_empty_min = non_empty_elms.argmax()
            non_empty_max = S - non_empty_elms[::-1].argmax()+1
            cropBox = (non_empty_min,non_empty_max)
        return cropBox
    
    def get_cropped_area(image_bw,tol):
        max_val = image_bw.max()
        tolerance = max_val*tol
        non_empty_elms = (image_bw<=tolerance).astype(int)
        S = non_empty_elms.shape
        # Traverse rows
        cropBox = [get_cropped_line(non_empty_elms[k,:],tolerance,S[1]) for k in range(0,S[0])]
        cropBox = filter(None, cropBox)
        xmin = [k[0] for k in cropBox]
        xmax = [k[1] for k in cropBox]
        # Traverse cols
        cropBox = [get_cropped_line(non_empty_elms[:,k],tolerance,S[0]) for k in range(0,S[1])]
        cropBox = filter(None, cropBox)
        ymin = [k[0] for k in cropBox]
        ymax = [k[1] for k in cropBox]
        xmin = min(xmin)
        xmax = max(xmax)
        ymin = min(ymin)
        ymax = max(ymax)
        ymax = ymax-1 # Not sure why this is necessary, but it seems to be.
        cropBox = (ymin, ymax-ymin, xmin, xmax-xmin)
        return cropBox
    
    def auto_crop(f,ext):
        image=Image.open(f)
        image.load()
        image_data = np.asarray(image)
        image_data_bw = image_data[:,:,0]+image_data[:,:,1]+image_data[:,:,2]
        cropBox = get_cropped_area(image_data_bw,grey_tolerance)
        image_data_new = image_data[cropBox[0]:cropBox[1]+1, cropBox[2]:cropBox[3]+1 , :]
        new_image = Image.fromarray(image_data_new)
        f_new = f.replace(ext,'')+'_cropped'+ext
        new_image.save(f_new)
    
  7. ==============================

    7.이것은 투명한 배경에서 작동하는 snew의 회신에 비해 개선 된 기능입니다. 수학적 형태학을 사용하면 다음 코드를 사용하여 투명하지 않고 흰색 배경에서 작업 할 수 있습니다.

    이것은 투명한 배경에서 작동하는 snew의 회신에 비해 개선 된 기능입니다. 수학적 형태학을 사용하면 다음 코드를 사용하여 투명하지 않고 흰색 배경에서 작업 할 수 있습니다.

    from PIL import Image
    from skimage.io import imread
    from skimage.morphology import convex_hull_image
    im = imread('L_2d.jpg')
    plt.imshow(im)
    plt.title('input image')
    plt.show()
    # create a binary image
    im1 = 1 - rgb2gray(im)
    threshold = 0.5
    im1[im1 <= threshold] = 0
    im1[im1 > threshold] = 1
    chull = convex_hull_image(im1)
    plt.imshow(chull)
    plt.title('convex hull in the binary image')
    plt.show()
    imageBox = Image.fromarray((chull*255).astype(np.uint8)).getbbox()
    cropped = Image.fromarray(im).crop(imageBox)
    cropped.save('L_2d_cropped.jpg')
    plt.imshow(cropped)
    plt.show()
    

  8. from https://stackoverflow.com/questions/14211340/automatically-cropping-an-image-with-python-pil by cc-by-sa and MIT license