HOWTO · Matplotlib

Python에서 NumPy 배열을 PIL 이미지로 변환

이 튜토리얼에서는 PIL 패키지의 Image.fromarray()를 사용하여 NumPy 배열을 PIL 이미지로 변환하는 방법을 설명합니다.

이 페이지의 내용

Pillow의 Image.fromarray()를 사용하면 NumPy 배열에서 PIL 이미지를 만들 수 있습니다. 2차원 배열은 그레이스케일 픽셀을 나타내며, (height, width, 3) 또는 (height, width, 4) 모양의 배열은 각각 RGB 또는 RGBA 픽셀을 나타냅니다. 배열의 dtype과 값 범위도 중요하므로 변환 전에 확인하세요. PIL은 익숙한 가져오기 이름이고 Pillow는 현재 유지 관리되는 라이브러리입니다.

RGB 이미지를 만들려면 3채널 uint8 배열을 생성해 Image.fromarray()에 전달하세요. 예제 출력에는 이미지 모드, (width, height) 크기, 왼쪽 위 픽셀 값이 표시됩니다. 펜스 안의 출력 레이블은 코드 바이트를 그대로 보존하므로 영어로 표시됩니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import numpy as np
from PIL import Image

pixels = np.array(
    [[[255, 0, 0], [0, 255, 0]], [[0, 0, 255], [255, 255, 255]]],
    dtype=np.uint8,
)
image = Image.fromarray(pixels)
print(image.mode, image.size, image.getpixel((0, 0)))
RGB (2, 2) (255, 0, 0)

변환 결과는 메모리에 있는 Pillow 이미지 객체입니다. 모드와 크기를 확인한 다음, 이미지 미리 보기를 지원하는 환경에서 표시하거나 파일로 저장할 수 있습니다.

NumPy 배열을 Image.fromarray()로 PIL 이미지로 변환하기

Image.fromarray(array)는 배열의 모양과 dtype을 바탕으로 이미지 모드를 추론합니다. 그레이스케일 데이터에는 (height, width) 모양의 2차원 배열을 사용하세요. RGB 데이터에는 (height, width, 3)을 사용하고, 네 번째 채널을 추가하면 RGBA가 됩니다. 처음 두 차원은 행과 열이므로, 행이 40개이고 열이 60개인 배열은 너비 60픽셀, 높이 40픽셀인 이미지를 만듭니다.

값이 이미 0부터 255까지의 정수인 일반적인 8비트 이미지 샘플에는 np.uint8을 사용하세요. 무조건 형 변환하지 마세요. 부동 소수점 데이터를 형 변환하면 소수 부분이 잘리고, 표현 가능한 범위를 벗어난 값은 순환할 수 있습니다. 다른 척도로 측정한 데이터라면 변환 전에 표시 강도에 어떻게 대응시킬지 정하세요. 예를 들어 0–1 범위의 데이터는 255를 곱하고 반올림할 수 있지만, 다른 범위의 데이터에는 의미에 맞춘 명시적인 정규화가 필요합니다.

배열의 모양, dtype, 값 범위 준비하기

(height, width) 모양의 배열은 그레이스케일 이미지입니다. RGB와 RGBA 배열은 마지막 차원에 채널을 추가하며, 픽셀마다 각각 세 개 또는 네 개의 값을 가집니다. (height, width, 1) 같은 모양은 일반적인 2차원 그레이스케일 모양이 아닙니다. 마지막 크기 1인 축이 그레이스케일 채널이라면 array[..., 0]으로 제거하세요. 변환 전에 array.shape, array.dtype, 최솟값과 최댓값을 확인하세요. 배열은 규칙적인 직사각형 격자여야 하며, 길이가 들쭉날쭉한 중첩 목록은 이미지 데이터가 아닙니다.

다음은 독립적으로 실행할 수 있는 그레이스케일 변환 예제입니다. 값이 8비트 범위에 맞도록 직접 지정되어 있어 별도의 스케일 조정이 필요하지 않습니다. 결과 이미지의 모드는 그레이스케일 L이고 크기는 (3, 2)입니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import numpy as np
from PIL import Image

gray = np.array([[0, 64, 128], [32, 160, 255]], dtype=np.uint8)
image = Image.fromarray(gray)
print(image.mode, image.size)
L (3, 2)

원본이 0에서 1 사이의 부동 소수점 배열이라면 np.round(array * 255).astype(np.uint8)로 의도를 분명히 해 변환하세요. 이 변환은 해당 값이 정규화된 강도 값일 때만 적절합니다. 온도나 임의의 점수 같은 측정값에는 의미 있는 하한과 상한을 정하고, 그 범위에 맞춰 자르거나 정규화한 뒤 변환 규칙을 기록하세요. 그렇지 않으면 대비와 픽셀 값이 오해를 일으킬 수 있습니다.

PIL 이미지를 만들기 전에 Matplotlib 컬러맵 적용하기

2차원 스칼라 필드의 값을 색으로 표현하려면 Matplotlib 컬러맵을 사용하세요. 데이터를 선택한 컬러맵이 기대하는 범위로 정규화한 다음, 컬러맵 결과를 8비트 RGB 또는 RGBA 배열로 변환합니다. plasma 컬러맵은 정규화된 빨강, 초록, 파랑, 알파 채널을 반환하므로 [..., :3]으로 알파 채널을 제외하면 RGB 배열이 됩니다. 이 과정은 Image.fromarray()와 별개입니다. 컬러맵이 색을 만들고 Pillow가 그 픽셀을 이미지 객체로 감쌉니다.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import numpy as np
import matplotlib
from PIL import Image

values = np.array([[0.0, 0.25], [0.75, 1.0]])
colors = matplotlib.colormaps["plasma"](values)[..., :3]
rgb = np.round(colors * 255).astype(np.uint8)
image = Image.fromarray(rgb)
print(image.mode, image.size)
RGB (2, 2)

이 예제는 values가 이미 0에서 1 사이로 정규화되었다고 가정합니다. 실제 스칼라 범위가 다르면 적절한 데이터 경계를 사용해 컬러맵을 적용하기 전에 정규화하세요. 컬러맵은 시각화에 유용하지만 스칼라 값을 표시용 색으로 바꾸므로, 분석에 원래 측정값이 필요하다면 그 값을 대체해서는 안 됩니다. 결과 이미지는 RGB 모드이며 크기는 (2, 2)입니다.

PIL 이미지 크기 조정 및 저장하기

변환한 뒤 image.resize((width, height))로 크기를 조정하세요. Pillow는 새 크기를 너비, 높이 순으로 받습니다. 표시는 저장과 별개입니다. show()는 현재 환경에서 미리 보기를 열고, save()는 이미지 파일을 씁니다. 무손실 출력에는 .png, JPEG에는 .jpg처럼 원하는 형식에 맞는 파일 확장자를 선택하세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

import numpy as np
from PIL import Image

pixels = np.zeros((20, 30, 3), dtype=np.uint8)
pixels[:, :15] = [255, 80, 0]
pixels[:, 15:] = [20, 90, 220]
image = Image.fromarray(pixels)
resized = image.resize((60, 40))
resized.save("resized-image.png")
print(resized.size)

예제는 입력 이미지를 메모리에서 만들고 현재 작업 디렉터리에 resized-image.png를 씁니다. 애플리케이션에서는 출력 위치에 맞는 경로를 사용하세요. 저장해도 NumPy 배열은 변경되지 않으며, 크기 조정은 Pillow 이미지의 픽셀 크기를 바꿉니다.

변환할 때 자주 발생하는 문제

  • 예상과 다른 모드 또는 거부되는 모양: array.shape를 확인하세요. 그레이스케일에는 (height, width)를, RGB 또는 RGBA에는 마지막 차원에 3개 또는 4개의 채널을 사용하세요. 불필요한 크기 1 채널은 제거하고, 원본에서 다른 채널 순서를 사용한다면 채널을 재배열하세요.
  • 밝기나 색상이 예상과 다름: 변환 전에 값 범위를 확인하세요. 0–1 범위의 부동 소수점 값은 8비트 이미지에 맞게 적절히 스케일 조정해야 합니다. 이미 0–255 범위인 값에는 다시 255를 곱하지 마세요. 다른 범위라면 문서화된 경계에 맞춰 정규화하세요.
  • 예상과 다른 dtype: array.dtype을 확인하고 변환 방법을 정하세요. 형 변환만으로 데이터의 스케일이 바뀌지는 않습니다. 원본 정밀도가 중요하다면 복사본을 보존하세요.
  • 컬러맵 결과가 이상함: 스칼라 입력이 의도한 대로 정규화되었는지 확인하고 결과에 RGB 채널이 세 개 있는지 RGBA 채널이 네 개 있는지 살펴보세요. 이미지를 만들기 전에 정규화된 색상 채널을 uint8로 변환하세요.
  • 이미지 파일이 생성되지 않음: 쓰기 가능한 경로와 지원되는 확장자를 지정해 save()를 호출하세요. 미리 보기가 필요할 때만 show()를 사용하세요. show()는 이미지를 저장하지 않습니다.