Python 이미지 유틸리티 - Pillow, CV2
포스트
취소

Python 이미지 유틸리티 - Pillow, CV2

이미지 resize, crop, thumbnail 등의 처리를 수행하는 파이썬 유틸리티들에 대해 공부합니다.

출처 Image Resizing in Python explained

1. Pillow (PIL)

현재 최신버전은 9.3 이지만 Python 3.11 을 필요로 함 (참고)

  • Python 3.7 이상에 대해 Pillow 9.2 를 설치하여 작성함
    • 설치 python3 -m pip install -U Pillow

1) 메인 코드

이미지(5464 x 3640) 에 대해 Crop, Resize, Thumbnail 수행

  • 이미지를 스트림으로 다운로드
  • 이미지 파일명과 현재 크기를 출력
  • 옵션과 함께 이미지 작업 수행: Crop, Resize, Thumbnail
  • thumbnail 또는 resized 디렉토리에 JPG 포맷으로 저장
  • 변경된 이미지 크기를 반환 (실패시 None 반환)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
"""
Pillow : resize, crop, thumbnail
"""
from PIL import Image
import glob, os

import requests  # URL 로 이미지 다운로드

image_url = "https://images.pexels.com/photos/3601453/pexels-photo-3601453.jpeg"
im=Image.open(requests.get(image_url, stream=True).raw)

basename = os.path.basename(image_url.lower())
filename, ext = os.path.splitext(basename)
print(f"Original: {filename}{ext} {dict(zip(['w','h'],im.size))}")

# 이미지 작업용 옵션
option = {
    "crop_px": 50,      # 50px 만큼 테두리 잘라내기 
    "resize_px": 300,   # 가로 300px 기준으로 줄이기
    "resize_ratio": 50, # 가로, 세로 50% 크기로 줄이기
    "thumbnail": 128,   # 가로, 세로 128px 크기의 섬네일 만들기
}

# Crop
option2 = option | {'crop_px':1000} # 잘라내기 1000px 로 수정 
print('Crop', im_crop(im, option2, filename))

# Resize
print('Resize', im_resize(im, option, filename))
print('Resize by ratio', im_resize_ratio(im, option, filename))

# Thumbnail
print('Thumbnail', im_thumbnail(im, option, filename))

2) Image Crop

이미지를 테두리 크기만큼 잘라내고 저장 (작아서 못하면 None)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def im_crop(im, option, filename):
    try:
        w,h = im.size
        if w<= option["crop_px"]*2 or h<= option["crop_px"]*2:
            print(f"image {im.size} is too small to crop {option['crop_px']}px")
            return None
        box = (option["crop_px"], option["crop_px"], w-option["crop_px"], h-option["crop_px"])
        im = im.crop(box)
        outfile = f"resized/{filename}_w{w-option['crop_px']*2}_crop.jpg"
        im.save(outfile)
    except Exception as e:
        print(e)
        return None
    return im.size

3) Image Resize

리사이즈 크기 px 값은 int 타입이어야 함

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 옵션의 가로 px 크기로 세로 크기도 조정하여 줄이기
def im_resize(im, option, filename):
    try:
        w,h = im.size
        height = int(option["resize_px"] / w * h)
        im = im.resize((option["resize_px"],height,), Image.Resampling.LANCZOS)
        outfile = f"resized/{filename}_w{option['resize_px']}_px.jpg"
        im.save(outfile)
    except Exception as e:
        print(e)
        return None
    return im.size

# 옵션의 비율 크기로 가로, 세로 크기를 조정하여 줄이기
def im_resize_ratio(im, option, filename):
    try:
        w,h = im.size
        width = int(w * option["resize_ratio"] / 100)
        height = int(h * option["resize_ratio"] / 100)
        im = im.resize((width,height,), Image.Resampling.LANCZOS)
        outfile = f"resized/{filename}_w{width}_ratio.jpg"
        im.save(outfile)
    except Exception as e:
        print(e)
        return None
    return im.size    

4) Image Thumbnail

가로, 세로 크기를 동일하게 crop 한 뒤에 thumbnail 생성

  • 변경된 im 개체를 im 변수로 다시 받아서 작업 연결
  • thumbnail 은 im 개체를 반환하지 않는다 (crop/resize 와 다름)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def im_thumbnail(im, option: dict, filename):
    try:
        w,h = im.size
        if w != h:
            gap = max(w,h)-min(w,h)
            box = (gap//2, 0, w-(gap//2+gap%2), h) if w > h else (0, gap//2, w, h-(gap//2+gap%2))
            im = im.crop(box)
            print(f"after crop: {w},{h} with {gap//2} => {box} {im.size}")
        im.thumbnail((option["thumbnail"],option["thumbnail"]), Image.Resampling.LANCZOS)
        outfile = f"thumbnail/{filename}_w{option['thumbnail']}.jpg"
        im.save(outfile)
    except Exception as e:
        print(e)
        return None
    return im.size

5) Image Save with Quality, Optimize

Image 정보

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from PIL import Image

im = Image.open('img/fiber.png')
print(type(im))  # <class 'PIL.PngImagePlugin.PngImageFile'>
im.show()        # 기본 이미지 애플리케이션 실행

print(f'{im.filename}')  # img/fiber.png
print(f'{im.format}')    # PNG
print(f'{im.mode}')      # RGBA
print(f'{im.size}')      # (1024, 600)
print(f'{im.width}')     # 1024
print(f'{im.height}')    # 600

# DPI 정보는 없다
print(f'{[k for k in img.info ]}')  
# ==> ['srgb', 'exif', 'XML:com.adobe.xmp']

썸네일 이미지 저장

품질 100 인 경우, 90 품질의 2배 이상 (원본보다 큰) 크기로 저장

  • 90 품질 저장 : 조금 더 큰 사이즈, ex) 5kb (100% 품질 11kb)
  • 90 품질 & 최적화 저장 : 중간 사이즈, ex) 4kb
  • 기본 저장 : 가장 파일 사이즈가 작다, ex) 3kb
1
2
3
4
5
6
7
8
9
10
11
12
13
size = 128, 128
quality = 90    
with Image.open(infile) as im:
    basename = os.path.basename(file.lower())
    outfile0 = f"thumbnail/{basename}_w{size[0]}.png"
    print(infile,'=>',outfile2)

    # 썸네일 
    im.thumbnail(size, Image.Resampling.LANCZOS)
    
    im.save(outfile0, "JPEG")
    im.save(outfile1, "JPEG", quality = quality)
    im.save(outfile2, "JPEG", quality = quality, optimize=True)

2. OpenCV

OpenCV 는 얼굴인식, 픽셀변환, 3D 모델링 등의 복잡한 이미지 처리에 사용되는 업계 표준 라이브러리. (이미지 처리의 기본이다)

  • 설치 Python3 -m pip install opencv-Python
    • 최신 버전 4.6.0.66 (2022년 6월 7일)
  • 참고 : OpenCV-Python Study documentation! (한글)
    • 영문 페이지가 아니라 한글이 나옴 (원문은 링크가 없어진듯)
    • Python 버전 OpenCV 설명문은 이 문서가 유일한듯. 못찾겠다.

1) URL 이미지를 requests 다운로드하여 읽기

  • requests 의 Response 를 bytes 형태로 read 한 후에 사용 가능
  • cv2 는 이미지 처리를 행렬 연산으로 하기 때문에 numpy array 필요
    • ex) img[100,100] => [154 131 45] (RGB)
  • cv2 의 이미지 크기는 numpy 의 shape 형태로 출력
    • shape = (height, width, dimension), dimension=RGB (3차원)
1
2
3
4
5
6
7
8
9
10
import requests
import cv2 as cv2
import numpy as np

image_url = "https://images.pexels.com/photos/3601453/pexels-photo-3601453.jpeg"
img_bytes = bytearray(requests.get(image_url, stream=True).raw.read())
img_array = np.array(img_bytes, dtype=np.uint8)
img = cv2.imdecode(img_array, -1)
print(img.shape)     # (3640, 5464, 3)
print(img[100,100])  # [154 131  45]

2) Resize

  • cv2 리사이즈 파일 크기 989kb
    • Pillow 리사이즈 95% 품질 크기(935kb)와 비슷
1
2
3
4
5
6
7
8
9
10
11
12
13
import cv2

print(f"Original Dimensions : {img.shape}")
# Original Dimensions : (3640, 5464, 3)

# resize image by specifying custom width and height
resized = cv2.resize(img, (2000, 1500))

# shape = (height, width, dimension), dimension=RGB (3차원)
print(f"Resized Dimensions : {resized.shape}")
# Resized Dimensions : (1500, 2000, 3)

cv2.imwrite(f'resized_imaged_w{resized.shape[1]}.jpg', resized)

3) Crop

이미지 3차원 행렬이라 바로 slice 처리하여 잘라낸다.

1
2
3
4
# image[startY:endY, startX:endX]
cropped_image = img[0:3640, 0:2732]
cv2.imwrite('cropped-image-opencv.jpg', cropped_image)
print(f"Cropped Image Dimensions : {cropped_image.shape}") # 2732x3640

3. ImageKit.ioImageKit 사용하기

이미지 최적화 서비스인데 무료 버전으로도 여러가지를 할 수 있는듯

1) API 서비스

  • 이미지를 imagekit.io 에 올리고 (사용자 계정)
  • 파라미터와 함께 API 를 호출하면 됨 (끝!)
    • 웹사이트에 API 를 연결하면 쉽다 (별도의 백엔드 필요없음)
    • 물론 용량과 사용량이 커지면 유료버전을 구독해야 함
1
2
3
4
5
6
7
8
# 업로드된 이미지 (원본 이미지)
https://ik.imagekit.io/{your_imagekit_id}/default-image.jpg

# 리사이징 width=300, height=300
https://ik.imagekit.io/{your_imagekit_id}/default-image.jpg?tr=w-300,h-300

# 품질 50% 변환
https://ik.imagekit.io/{your_imagekit_id}/default-image.jpg?tr=q-50

2) Python 라이브러리 설정

설치 pip install imagekitio

1
2
3
4
5
6
7
from imagekitio import ImageKit

imagekit = ImageKit(
    private_key='your_private_key',
    public_key='your_public_key',
    url_endpoint='your_url_endpoint'
)

9. Review

 
 

끝!   읽어주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.