Following system colour scheme Selected dark colour scheme Selected light colour scheme

Python 개선 제안 한국어 번역

PEP 368 – 표준 이미지 프로토콜과 클래스

Author:
Lino Mastrodomenico <l.mastrodomenico at gmail.com>
Status:
Deferred
Type:
Standards Track
Created:
28-Jun-2007
Python-Version:
2.6, 3.0
Post-History:


Table of Contents

번역·라이선스 안내

이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판

초록

Python 세계의 이미지 저장 및 조작 상황은 극도로 파편화되어 있습니다. 이미지 객체를 사용하는 거의 모든 라이브러리가 저마다 고유한 이미지 클래스를 구현해 왔는데, 이 클래스들은 다른 라이브러리의 클래스와 호환되지 않으며 Python답지 않은 경우도 많습니다. 표준 라이브러리에 기본적인 RGB 이미지 클래스(Tkinter.PhotoImage)가 있기는 하지만, Tkinter 프로그래밍 외의 용도로는 사실상 쓸 수 없으며 실제로 쓰이지도 않습니다.

이러한 파편화는 개발자들의 머릿속에서 소중한 공간을 차지할 뿐만 아니라, 서로 다른 라이브러리 간의 이미지 교환(비교적 흔한 사용 사례에서 필요합니다)을 필요 이상으로 느리고 복잡하게 만듭니다.

이 PEP는 단순하고 Python다운 이미지 프로토콜/인터페이스를 정의하여 이 상황을 개선할 것을 제안합니다. 이 프로토콜/인터페이스는 표준 라이브러리 안팎의 기존 이미지 클래스들이 기존 사용자 기반과의 하위 호환성을 깨뜨리지 않고 받아들여 구현할 수 있기를 기대합니다. 실질적으로 이것은 최소한의 이미지와 유사한 객체가 어떤 모습을 갖추고 어떻게 동작해야 하는지에 대한 정의입니다(파일과 유사한 객체의 read()와 write() 메서드와 비슷한 방식입니다).

또한 기본적인 이미지 조작 기능을 제공하고 새 프로토콜을 구현하는 클래스를 표준 라이브러리에 포함할 것을 제안하며, 기존 이미지 클래스에 이 프로토콜 지원을 추가하는 데 도움이 되는 믹스인 클래스도 함께 제안합니다.

PEP 보류

이 PEP에서 다루는 개념에 대한 추가 탐구는 보류되었습니다. 현재 이 PEP의 목표를 추진하고 피드백을 수집하여 반영하는 데 관심이 있으면서, 이를 효과적으로 수행할 충분한 시간을 가진 주도자가 없기 때문입니다.

근거

Python 표준 라이브러리에 포함될 준비가 된 고품질 모듈을 얻는 좋은 방법은, 경쟁하는 외부 라이브러리들 사이의 자연 선택을 통해 유용한 기능과 큰 사용자 기반을 갖춘 명확한 승자가 나타날 때까지 그냥 기다리는 것입니다. 그런 다음 그 사실상의 표준을 표준 라이브러리에 포함함으로써 공식적으로 인정할 수 있습니다.

안타깝게도 이 방식은 Python 세계에서 지배적인 이미지 클래스를 만들어 내는 데에는 잘 작동하지 않았습니다. 이미지 객체가 필요한 거의 모든 서드파티 라이브러리가 다른 라이브러리의 클래스와 호환되지 않는 자체 클래스를 만듭니다. 이것은 실제적인 문제입니다. 프로그램이 예를 들어 PIL(Python Imaging 라이브러리)로 이미지를 만들고 조작한 다음 wxPython이나 pygame으로 표시하는 것은 지극히 합리적이기 때문입니다. 하지만 이 라이브러리들은 서로 다르고 호환되지 않는 이미지 클래스를 가지고 있으며, 일반적인 해결책은 원본에서 이미지를 (width, height, bytes_string) 튜플로 수동으로 “내보낸” 뒤, 이를 “가져와서” 대상 형식의 새 인스턴스를 생성하는 것입니다. 이 방식은 동작하기는 하지만, 필요 이상으로 보기 흉하고 느립니다.

때때로 사용되어 온 또 다른 “해결책”은 한 클래스에서 다른 클래스로 변환하는 특정 어댑터 및/또는 변환기를 만드는 것입니다(예를 들어 PIL은 PIL 이미지를 Tkinter 클래스와 호환되는 클래스로 변환하는 ImageTk 모듈을 제공합니다). 하지만 이 방식은 관련된 라이브러리의 수가 늘어날수록 잘 확장되지 않으며, 사용자에게는 여전히 번거롭습니다. 완벽하게 쓸 만한 이미지 객체를 가지고 있는데 왜 다음 메서드에 전달하기 전에 변환해야 하며, 왜 그 메서드는 제 이미지를 있는 그대로 받아들이지 못하는 것입니까?

이 문제는 결코 앞서 언급한 세 라이브러리에만 국한되지 않으며, 아마도 여러 원인이 있을 것입니다. 그중 제 생각에 문제를 해결하기 전에 반드시 이해해야 할 매우 중요한 원인 두 가지는 다음과 같습니다:

  • 오늘날의 컴퓨팅 세계에서 이미지는 특정 도메인에 엄격하게 묶여 있지 않은 기본 타입입니다. 이 때문에 앞서 언급한 세 라이브러리(PIL, wxPython, pygame)의 이미지 클래스들 사이에서는 명확한 승자가 결코 나오지 않을 것입니다. 이들은 서로 다른 도메인을 다루며 실제로 서로 경쟁하지 않습니다.
  • Python 표준 라이브러리는 서드파티 모듈이 채택하거나 모방할 수 있는 좋은 이미지 클래스를 제공한 적이 한 번도 없습니다. Tkinter.PhotoImage는 기본적인 RGB 기능을 제공하지만, 그중에서 단연 가장 느리고 가장 보기 흉하며, Tkinter 루트 창이 생성된 후에만 인스턴스화할 수 있습니다.

이 PEP는 네 가지 방법으로 이 상황을 개선하고자 합니다:

  1. (Python 측과 C 측 모두에서) 단순하고 Python다운 이미지 프로토콜/인터페이스를 정의합니다. 이 프로토콜/인터페이스는 표준 라이브러리 안팎의 기존 이미지 클래스들이 기존 사용자 기반과의 하위 호환성을 깨뜨리지 않고 받아들여 구현할 수 있기를 기대합니다.
  2. 표준 라이브러리에 세 가지 새 클래스를 포함할 것을 제안합니다:
    • ImageMixin은 새 프로토콜을 구현하는 데 필요한 거의 모든 것을 제공합니다. 이 클래스의 주된 목적은 기존 라이브러리가 이 인터페이스를 최대한 간단하게 지원할 수 있도록 하는 것이며, 경우에 따라서는 베이스 클래스 목록에 이 클래스를 추가하고 생성자를 약간 보완하는 것만으로 충분할 정도입니다.
    • Image는 ImageMixin의 서브클래스이며, 이미지의 크기를 조정하거나 서로 다른 픽셀 형식 간에 이미지를 변환할 수 있는(또는 둘 다 할 수 있는) 생성자를 추가할 것입니다. 이는 새 프로토콜의 빠르고 효율적인 기본 구현을 제공하기 위한 것입니다.
    • ImageSize는 부수적인 헬퍼 클래스입니다. 자세한 내용은 아래를 참조하십시오.
  3. Tkinter.PhotoImage는 (주로 ImageMixin 클래스를 통해) 새 프로토콜을 구현하며, 이미지를 받을 수 있는 모든 Tkinter 메서드는 이 인터페이스를 구현하는 모든 객체를 받아들이도록 수정됩니다. 덧붙여, 이 PEP의 저자는 가장 널리 쓰이는 외부 라이브러리의 개발자들과 협력하여 같은 목표(각자의 클래스에서 이 프로토콜을 지원하고, 이를 구현하는 모든 클래스를 받아들이는 것)를 달성할 것입니다.
  4. 새로운 PyImage_* 함수들이 CPython C API에 추가됩니다. 이 함수들은 프로토콜의 C 측을 구현하며, Image/ImageMixin 클래스의 인스턴스가 아니더라도 프로토콜을 지원하는 모든 객체를 첫 번째 매개변수로 받아들입니다.

최종 사용자에게 미치는 주된 효과는 서로 다른 라이브러리 간의 이미지 교환이 단순해진다는 것(모든 것이 잘 진행된다면, 어떤 Python 라이브러리든 다른 어떤 라이브러리의 이미지도 받아들이게 됩니다)과 새로운 Image 클래스를 별도 설치 없이 바로 사용할 수 있다는 것입니다. 새 클래스는 사진을 원하는 크기로 자르거나 크기를 조정한 뒤 창에 표시하기 위한 적절한 위젯에 전달하거나, 텍스처를 어둡게 만든 뒤 3D 라이브러리에 전달하는 것과 같이 단순하지만 흔한 사용 사례를 다루기 위한 것입니다.

Image 클래스는 PIL, Pythonmagick, NumPy의 기능 중 (아주 작은) 일부를 제공하기는 하지만, 이 세 라이브러리를 대체하거나 이들과 경쟁하기 위한 것이 아닙니다. 특히 PIL은 수십 개의 클래스, 필터, 변환, 파일 형식을 갖춘 매우 풍부한 이미지 조작 기능을 제공합니다. PIL(또는 그와 유사한 것)을 표준 라이브러리에 포함하는 것은 가치 있는 목표일 수도, 아닐 수도 있지만, 이는 이 PEP의 범위를 완전히 벗어납니다.

명세

imageop 모듈은 오랫동안 어느 정도 유사한 기능을 제공하는 함수들을 담아 왔기 때문에 새로운 클래스와 객체의 기본 위치로 사용되지만, 원한다면 새 모듈을 만들 수도 있습니다(예: 새로운 “image” 또는 “media” 모듈. 후자는 결국 다른 멀티미디어 클래스도 포함하게 될 수 있습니다).

MODES는 새로운 모듈 수준 상수로, Image 클래스가 지원하는 픽셀 형식의 집합입니다. 새 프로토콜을 구현하는 모든 이미지 객체는 이 모드 중 하나로 형식화되어 있음이 보장되지만, 이미지를 받아들이는 라이브러리는 그중 일부만 지원해도 됩니다.

이 모드들은 또한 각각 모듈 수준 상수로도 제공됩니다(예: imageop.RGB).

다음 표는 현재 지원되는 모드와 그 속성을 요약한 것입니다:

이름 구성 요소 이름 구성 요소당 비트 수 서브샘플링 유효 구간
L l (소문자 L) 8 없음 전체 범위
L16 l 16 없음 전체 범위
L32 l 32 아니요 전체 범위
LA l, a 8 아니요 전체 범위
LA32 l, a 16 아니요 전체 범위
RGB r, g, b 8 아니요 전체 범위
RGB48 r, g, b 16 아니요 전체 범위
RGBA r, g, b, a 8 아니요 전체 범위
RGBA64 r, g, b, a 16 아니요 전체 범위
YV12 y, cr, cb 8 1, 2, 2 16-235, 16-240, 16-240
JPEG_YV12 y, cr, cb 8 1, 2, 2 전체 범위
CMYK c, m, y, k 8 아니요 전체 범위
CMYK64 c, m, y, k 16 아니요 전체 범위

모드 이름이 숫자로 끝나면 그 숫자는 픽셀당 평균 비트 수를 나타냅니다. 그 밖의 모든 모드는 단순히 픽셀마다 구성 요소당 1바이트를 사용합니다.

팔레트 모드나 구성 요소당 8비트 미만인 모드는 지원하지 않습니다. 21세기에 오신 것을 환영합니다.

다음은 각 모드에 대한 간단한 설명과 이를 포함한 근거입니다. 모드는 네 그룹으로 나뉩니다.

  1. 그레이스케일 (L* 모드): 과학 계산에서 많이 사용됩니다(이 분야의 사용자에게는 매우 넓은 동적 범위와 높은 정밀도가 필요할 수도 있으므로, 구성 요소당 32비트를 사용하는 유일한 모드인 L32가 있습니다). 또한 컬러 이미지의 구성 요소 하나를 그레이스케일 이미지로 간주하는 것이 유용할 때도 있습니다(평면 이미지의 개별 평면이 이 방식을 사용합니다. 아래의 YV12를 참조하십시오). 구성 요소 이름('l', 소문자 L)은 휘도(luminance)를 뜻하며, 선택 사항인 두 번째 구성 요소('a')는 알파 값으로 픽셀의 불투명도를 나타냅니다. alpha = 0은 완전한 투명을 의미하고, alpha = 255/65535는 완전히 불투명한 픽셀을 나타냅니다.
  2. RGB* 모드: 흔히 볼 수 있는 일반적인 컬러 이미지입니다. 선택 사항인 알파 구성 요소는 그레이스케일 모드에서와 같은 의미를 가집니다.
  3. YCbCr, 일명 YUV(*YV12 모드)입니다. 이 모드들은 평면 방식입니다(즉, 한 픽셀의 모든 구성 요소가 연속된 바이트에 놓이는 일반적인 배치 대신, 각 구성 요소에 대한 모든 픽셀의 값이 연속된 메모리 영역에 저장됩니다). 또한 YCbCr 이미지에서 단연 가장 흔한 형식이기 때문에 1, 2, 2(다른 이름으로 4:2:0) 서브샘플링을 사용합니다(즉, 각 픽셀은 고유한 Y 값을 가지지만, Cb와 Cr 구성 요소는 인접한 2x2 픽셀 그룹이 공유합니다). V(Cr) 평면이 U(Cb) 평면보다 먼저 저장된다는 점에 유의하십시오.

    YV12는 MPEG2(DVD 포함), MPEG4(ASP/DivX와 AVC/H.264 모두), Theora 비디오 프레임에 흔히 사용됩니다. Y의 유효한 값은 range(16, 236)(236 제외) 범위에 있고, Cb와 Cr의 유효한 값은 range(16, 241) 범위에 있습니다. JPEG_YV12는 YV12와 비슷하지만, 세 성분이 256개 값 전체 범위를 가질 수 있습니다. 이는 거의 모든 JPEG/JFIF 파일과 MJPEG 비디오 프레임이 사용하는 기본 형식입니다. 이 두 모드가 지원되는 다른 모든 모드에 비해 “특이한” 까닭은 기존의 많은 라이브러리와 애플리케이션에서 이런 방식으로 널리 사용되기 때문입니다. 이 모드들을 포함한 이유도 여기에 있습니다(또한 YCbCr이 더 큰 색 공간이어서 손실 없이 RGB로 변환할 수 없다는 점도 이유입니다). 픽셀 값의 독특한 4:2:0 평면 배치는 대부분의 경우 세 평면을 세 개의 개별 그레이스케일 이미지로 간주할 수 있으므로 지원하기가 비교적 쉽습니다.

  4. CMYK* 모드 (시안, 마젠타, 옐로, 블랙)는 감산 색상 모드이며, 컬러 이미지를 종이에 인쇄할 때 사용됩니다. 전문 디자이너들은 이 모드 없이는 살 수 없는 척하기를 좋아하므로, 여기에 포함했습니다.

Python API

아래의 Examples를 참고하십시오.

Python 2.x에서는 여기에서 정의하는 새 클래스가 모두 새 스타일 클래스입니다.

모드 객체

모드 객체는 여러 유형의 이미지에 동작하는 범용 알고리즘을 구현하는 데 사용할 수 있는 여러 속성과 메서드를 제공합니다:

components

픽셀당 성분의 수입니다(예: RGBA 이미지의 경우 4).

component_names

문자열의 튜플입니다. 위 표의 “Component names” 열을 참고하십시오.

bits_per_component

8, 16 또는 32입니다. 위 표의 “Bits per component” 열을 참고하십시오.

bytes_per_pixel

components * bits_per_component // 8이며, 평면이 아닌 모드에서만 사용할 수 있습니다(아래 참고).

planar

불리언입니다. 이미지 성분이 각각 별도의 평면에 있으면 True입니다. 현재는 모드가 서브샘플링을 사용하는 경우에, 그리고 오직 그 경우에만 이렇게 됩니다.

subsampling

모드의 각 성분마다 수평 방향과 수직 방향의 다운샘플링 정도를 각각 나타내는 두 정수로 이루어진 튜플을 담고 있는 튜플입니다. 실제로는 YV12와 JPEG_YV12의 경우 ((1, 1), (2, 2), (2, 2))이고, 그 밖의 모든 모드에서는 ((1, 1),) * components입니다.

x_divisor

max(x for x, y in subsampling)입니다. 이 모드를 사용하는 이미지의 너비는 이 값으로 나누어떨어져야 합니다.

y_divisor

max(y for x, y in subsampling)입니다. 이 모드를 사용하는 이미지의 높이는 이 값으로 나누어떨어져야 합니다.

intervals

모드의 각 성분마다 두 정수, 즉 해당 성분의 유효한 최솟값과 최댓값으로 이루어진 튜플을 담고 있는 튜플입니다. 그 값은 YV12의 경우 ((16, 235), (16, 240), (16, 240))이고, 그 밖의 모든 모드에서는 ((0, 2 ** bits_per_component - 1),) * components입니다.

get_length(iterable[integer]) -> int

매개변수는 이미지의 너비와 높이라는 두 정수를 담은 이터러블이어야 합니다. 이 메서드는 이 모드로 해당 크기의 이미지를 저장하는 데 필요한 바이트 수를 반환합니다.

구현 세부 사항: 모드는 str의 서브클래스의 인스턴스이며, 값이 'I'인 L32를 제외하면 이름과 같은 값을 가집니다(예: imageop.RGB == 'RGB'). 이는 기존 PIL 사용자를 위한 하위 호환성만을 목적으로 합니다. 여기에서 제안하는 이미지 프로토콜을 사용하는 새 코드는 이 세부 사항에 의존해서는 안 됩니다.

이미지 프로토콜

이미지 프로토콜을 지원하는 모든 객체는 다음 메서드와 속성을 제공해야 합니다:

mode

이 이미지에 있는 픽셀의 형식과 배치입니다. MODES 집합에 있는 상수 중 하나입니다.

size

ImageSize Class의 인스턴스입니다. 두 정수로 이루어진 이름 있는 튜플로, 이미지의 너비와 높이를 픽셀 단위로 나타냅니다. 둘 다 >= 1이어야 하며 size의 width 및 height 속성으로도 접근할 수 있습니다.

buffer

0에서 255 사이의 정수 시퀀스입니다. 이 정수들은 이미지 데이터를 저장하는 데 쓰이는 실제 바이트입니다(즉, 이 값을 수정하면 이미지 픽셀이 바뀌고 그 반대도 마찬가지입니다). 데이터는 구성 요소당 비트 수가 8을 넘더라도 패딩이나 특별한 메모리 정렬 없이 행 우선(row-major)/C 연속(C-contiguous) 순서로 배치됩니다. 지원되는 메서드는 __len__, __getitem__/__setitem__(정수 인덱스와 슬라이스 인덱스 모두 지원), __iter__뿐이며, C 쪽에서는 버퍼 프로토콜을 구현합니다.

이는 이미지에 대한 상당히 저수준의 인터페이스이므로, 구성 요소당 8비트를 넘는 모드에서 올바른(네이티브) 바이트 순서를 사용하고 YV12 이미지에서 올바른 값 범위를 사용하는 것은 사용자의 책임입니다. 버퍼는 자신의 이미지에 대한 참조를 유지할 수도 있고 유지하지 않을 수도 있지만, 해당 이미지가 가비지 컬렉터에 의해 소멸된 후에도 버퍼를 사용하는 것은 (쓸모는 없을지라도) 여전히 안전합니다(이를 위해서는 wxPython의 이미지 클래스와, 경우에 따라 다른 라이브러리의 이미지 클래스도 변경해야 합니다). 구현 세부 사항: 이것은 array('B'), bytes() 객체 또는 특수한 고정 길이 타입일 수 있습니다.

info

이미지와 관련된 임의의 메타데이터(예: DPI, 감마, ICC 프로파일, 노출 시간 등)를 담을 수 있는 dict 객체입니다. 이 데이터의 해석은 이 PEP의 범위를 벗어나며, 아마도 이미지를 생성하거나 저장하는 데 사용된 라이브러리에 따라 달라질 것입니다. 이미지의 메서드가 새 이미지를 반환하는 경우, 자신의 info 속성에서 메타데이터를 복사하거나 조정할 수 있습니다(ImageMixin 구현은 항상 비어 있는 info 딕셔너리로 새 이미지를 생성합니다).
bits_per_component
bytes_per_pixel
component_names
components
intervals
planar
subsampling
대응하는 mode.* 속성의 단축 표현입니다.

map(function[, function...]) -> None

이미지의 모든 픽셀에 대해 각 구성 요소를 대응하는 함수로 매핑합니다. 함수가 하나만 전달되면 그 함수가 각 구성 요소에 반복해서 사용됩니다. 이 메서드는 이미지를 제자리에서 수정하며 일반적으로 매우 빠르지만(대부분의 경우 함수는 적은 횟수만 호출되며, 브랜치가 없는 단순한 함수라면 단 한 번만 호출될 수도 있습니다), 전달되는 함수에 다음과 같은 여러 제약을 부과합니다:
  • 함수는 하나의 정수 인자를 받아 숫자를 반환해야 합니다(필요한 경우 map은 결과를 가장 가까운 정수로 반올림하고 range(0, 2 ** bits_per_component) 범위로 잘라냅니다);
  • 함수는 인자에 대한 어떤 연산에서 발생하는 BaseException, Exception 또는 Exception의 알 수 없는 서브클래스도 가로채려 해서는 안 됩니다(구현은 속도를 최적화하기 위해 특이한 객체를 전달할 수 있으므로, 단순한 "if n == 10:"조차 예외를 일으킬 수 있습니다. 이런 예외는 그냥 무시하십시오. map이 처리합니다). 그 밖의 예외를 잡는 것은 괜찮습니다;
  • 함수는 부작용이 없어야 하며, 그 결과는 map을 한 번 호출하는 동안 바뀔 수 있는 (인자 이외의) 값에 의존해서는 안 됩니다.
rotate90() -> image
rotate180() -> image
rotate270() -> image
이미지를 중심을 기준으로 반시계 방향으로 90도, 180도 또는 270도 회전한 복사본을 반환합니다.

clip() -> None

YV12 이미지에서 유효하지 않은 구성 요소 값을 허용되는 최솟값 또는 최댓값으로 포화시킵니다(mode.intervals 참조). 다른 이미지 모드에서는 이 메서드가 아무 일도 하지 않으며 매우 빠릅니다. 중간 연산(예: map 메서드)이 유효 구간을 벗어난 값을 픽셀에 할당할 수 있으므로, YV12 이미지를 저장하거나 내보내는 라이브러리는 항상 이 메서드를 호출하는 것이 좋습니다.

split() -> tuple[image]

이미지의 개별 구성 요소에 대응하는 L, L16 또는 L32 이미지의 튜플을 반환합니다.

평면(planar) 이미지는 component_names에 정의된 것과 같은 이름의 속성도 지원합니다. 이 속성들은 대응하는 성분의 픽셀 값에 대한 뷰를 제공하는 그레이스케일(모드 L) 이미지를 담고 있습니다. 하위 이미지에 대한 모든 변경은 부모 이미지에 즉시 반영되며 그 반대도 마찬가지입니다(두 이미지의 버퍼는 같은 메모리 위치를 참조합니다).

비평면 이미지는 다음과 같은 추가 메서드를 제공합니다.

pixels() -> iterator[pixel]

이미지의 모든 픽셀을 순회하는 이터레이터를 반환합니다. 맨 위 줄부터 시작하여 각 줄을 왼쪽에서 오른쪽으로 훑어 갑니다. Pixel Objects에 대한 설명은 아래를 참조하십시오.

__iter__() -> iterator[line]

이미지의 모든 줄을 위에서 아래로 순회하는 이터레이터를 반환합니다. Line Objects에 대한 설명은 아래를 참조하십시오.

__len__() -> int

이미지의 줄 수(size.height)를 반환합니다.

__getitem__(integer) -> line

지정한 (y) 위치의 줄을 반환합니다.

__getitem__(tuple[integer]) -> pixel

매개변수는 정수 두 개로 이루어진 튜플이어야 합니다. 두 정수는 각각 이미지 안의 x 좌표와 y 좌표로 해석되며(0, 0이 왼쪽 위 모서리입니다), 픽셀 객체가 반환됩니다.

__getitem__(slice | tuple[integer | slice]) -> image

매개변수는 슬라이스이거나, 슬라이스 두 개 또는 정수 하나와 슬라이스 하나를 담은 튜플이어야 합니다. 이미지에서 선택된 영역이 복사되어 새 이미지가 반환됩니다. image[x:y:z]는 image[:, x:y:z]와 동등합니다.

__setitem__(tuple[integer], integer | iterable[integer]) -> None

지정한 위치의 픽셀을 수정합니다. 성분이 하나뿐인 이미지에서 image[x, y] = integer는 image[x, y] = (integer,)의 축약형입니다.

__setitem__(slice | tuple[integer | slice], image) -> None

__getitem__ 메서드의 대응하는 형태와 같은 방식으로 영역을 선택하고, 두 번째 인자로 주어진 이미지의 픽셀 복사본을 그 영역에 대입합니다. 두 번째 인자의 이미지는 이 이미지와 모드가 정확히 같아야 하고 크기는 지정한 영역과 같아야 합니다. 알파 성분이 있다면 그대로 복사될 뿐이며 이미지의 다른 성분에는 영향을 주지 않습니다(즉, 알파 합성은 수행되지 않습니다).

mode, size, buffer(buffer의 메모리 주소 포함)는 이미지가 생성된 뒤에는 절대 바뀌지 않습니다.

PEP 3118이 채택되면 모든 이미지 객체가 새 버퍼 프로토콜을 지원할 것으로 예상되지만, 이는 이 PEP의 범위를 벗어납니다.

Image와 ImageMixin 클래스

ImageMixin 클래스는 mode, size, buffer, info를 제외하고 위에서 설명한 모든 메서드와 속성을 구현합니다. Image는 이 네 가지 속성에 대한 지원을 추가한 ImageMixin의 서브클래스로, 다음 생성자를 제공합니다(생성자는 이미지 프로토콜의 일부가 아니라는 점에 유의하십시오):

__init__(mode, size, color, source)

mode는 MODES 집합에 있는 상수 중 하나여야 하고, size는 정수 두 개(새 이미지의 너비와 높이)로 이루어진 시퀀스입니다. color는 이미지의 성분마다 하나씩 대응하는 정수 시퀀스로, 모든 픽셀을 같은 값으로 초기화하는 데 사용됩니다. source는 적절한 크기와 형식을 갖춘 정수 시퀀스로서 새 이미지의 버퍼에 그대로 복사되거나, 기존 이미지일 수 있습니다. Python 2.x에서는 source가 str의 인스턴스일 수도 있으며, 이 경우 바이트 시퀀스로 해석됩니다. color와 source는 함께 쓸 수 없으며, 둘 다 생략하면 이미지는 투명한 검은색으로 초기화됩니다(버퍼의 모든 바이트 값은 YV12 모드에서는 16, CMYK* 모드에서는 255, 그 밖의 모든 모드에서는 0입니다). source가 주어졌고 그것이 이미지라면 mode와 size 중 하나 또는 둘 다를 생략할 수 있습니다. 이들을 지정했는데 원본의 모드나 크기와 다르다면 원본 이미지가 변환됩니다.

크기 조정과 색 공간 변환에 쓰이는 정확한 알고리즘은 Python 버전과 구현에 따라 다를 수 있지만, 항상 고품질의 결과를 냅니다(예: 업샘플링에는 3차 스플라인 보간을, 이미지 다운샘플링에는 안티앨리어스 필터를 사용할 수 있습니다). 모드 변환은 어떤 조합이든 지원하지만, CMYK* 모드로 변환하거나 그 모드에서 변환하는 데 쓰이는 알고리즘은 상당히 단순합니다. 장치의 정확한 색 프로파일을 가지고 있다면 LittleCMS 같은 좋은 색 관리 도구를 사용하는 것이 좋습니다. 새 이미지는 빈 info dict를 가집니다.

줄 객체

(예를 들어 이미지를 순회할 때 반환되는) 행 객체는 다음 속성과 메서드를 지원합니다:

mode

이 줄이 속한 이미지의 모드입니다.

__iter__() -> iterator[pixel]

줄의 모든 픽셀을 왼쪽에서 오른쪽으로 순회하는 이터레이터를 반환합니다. Pixel Objects에 대한 설명은 아래를 참조하십시오.

__len__() -> int

줄의 픽셀 수(이미지 너비)를 반환합니다.

__getitem__(integer) -> pixel

지정된 (x) 위치의 픽셀을 반환합니다.

__getitem__(slice) -> image

줄에서 선택한 부분을 복사하여 새 이미지를 반환합니다. 새 이미지의 높이는 항상 1입니다.

__setitem__(integer, integer | iterable[integer]) -> None

지정된 위치의 픽셀을 수정합니다. 구성 요소가 하나뿐인 이미지에서 line[x] = integer는 line[x] = (integer,)의 단축 표현입니다.

__setitem__(slice, image) -> None

줄의 일부를 선택하고, 두 번째 인자로 받은 이미지의 픽셀 복사본을 그 부분에 할당합니다. 이 이미지는 높이가 1이고, 너비가 지정된 슬라이스와 같으며, 이 줄과 모드가 같아야 합니다. 알파 구성 요소가 있으면 그대로 복사될 뿐이며 이미지의 다른 구성 요소에는 영향을 주지 않습니다(즉, 알파 합성은 수행되지 않습니다).

픽셀 객체

픽셀 객체(예를 들어 줄을 순회할 때 반환되는 객체)는 다음 속성과 메서드를 지원합니다:

mode

이 픽셀이 속한 이미지의 모드입니다.

value

구성 요소마다 하나씩 대응하는 정수들의 튜플입니다. 길이가 올바른 이터러블이라면 무엇이든 value에 할당할 수 있지만(자동으로 튜플로 변환됩니다), 모드의 구성 요소가 하나뿐이더라도 정수를 할당할 수는 없습니다. 이럴 때는 대신 pixel.l = 123처럼 사용하십시오.

r, g, b, a, l, c, m, y, k

각 성분의 정수 값입니다. 현재 모드에 해당하는 성분(mode.component_names에 있는 것)만 사용할 수 있습니다.
__iter__() -> iterator[int]
__len__() -> int
__getitem__(integer | slice) -> int | tuple[int]
__setitem__(integer | slice, integer | iterable[integer]) -> None
이 네 메서드는 픽셀 구성 요소마다 하나씩 정수가 들어 있는 고정 길이 리스트를 흉내 냅니다.

ImageSize 클래스

ImageSize는 명명된 튜플로, 다음 사항을 제외하면 tuple과 같은 클래스입니다:

  • 생성자는 너비와 높이, 두 정수만 받습니다. 생성자에서 이 값들을 각각의 __index__() 메서드로 변환하므로, 모든 ImageSize 객체에는 int(Python 2.x에서는 long일 수도 있음) 인스턴스만 들어 있음이 보장됩니다;
  • 튜플의 첫 번째 수와 두 번째 수에 각각 해당하는 width 프로퍼티와 height 프로퍼티가 있습니다;
  • __repr__ 메서드가 반환하는 문자열은 'imageop.ImageSize(width=%d, height=%d)' % (width, height)입니다.

일반적으로 최종 사용자가 ImageSize의 인스턴스를 직접 만들지는 않습니다. 하지만 size 속성은 ImageSize 인스턴스여야 하므로, 이미지 프로토콜을 구현하는 새 클래스를 만들 때 사용할 수 있습니다.

C API

사용 가능한 이미지 모드는 C 수준에서 PyObject * 타입의 PyImage_* 상수로 제공됩니다(예: PyImage_RGB는 imageop.RGB입니다).

다음 함수들은 모드 객체와 이미지 객체에 대한 C 친화적인 인터페이스를 제공합니다(모든 함수는 실패 시 NULL 또는 -1을 반환합니다):

int PyImageMode_Check(PyObject *obj)

객체 obj가 유효한 이미지 모드이면 참을 반환합니다.
int PyImageMode_GetComponents(PyObject *mode)
PyObject* PyImageMode_GetComponentNames(PyObject *mode)
int PyImageMode_GetBitsPerComponent(PyObject *mode)
int PyImageMode_GetBytesPerPixel(PyObject *mode)
int PyImageMode_GetPlanar(PyObject *mode)
PyObject* PyImageMode_GetSubsampling(PyObject *mode)
int PyImageMode_GetXDivisor(PyObject *mode)
int PyImageMode_GetYDivisor(PyObject *mode)
Py_ssize_t PyImageMode_GetLength(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
이 함수들은 대응하는 Python 속성이나 메서드와 동등합니다.

int PyImage_Check(PyObject *obj)

객체 obj가 Image 객체이거나 Image 타입의 서브타입 인스턴스이면 참을 반환합니다. 아래의 PyObject_CheckImage도 참조하십시오.

int PyImage_CheckExact(PyObject *obj)

객체 obj가 Image 객체이지만 Image 타입의 서브타입 인스턴스는 아니면 참을 반환합니다.
PyObject* PyImage_New(PyObject *mode, Py_ssize_t width, Py_ssize_t height)
투명한 검은색으로 초기화된 새 Image 인스턴스를 반환합니다(자세한 내용은 위의 Image.__init__을 참조하십시오).
PyObject* PyImage_FromImage(PyObject *image, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
image 객체의 내용으로 초기화된 새 Image 인스턴스를 반환합니다. 필요하면 그 내용은 크기가 조정되고 지정된 mode로 변환됩니다.
PyObject* PyImage_FromBuffer(PyObject *buffer, PyObject *mode, Py_ssize_t width, Py_ssize_t height)
buffer 객체의 내용으로 초기화된 새 Image 인스턴스를 반환합니다.

int PyObject_CheckImage(PyObject *obj)

객체 obj의 클래스가 ImageMixin 및/또는 Image의 서브클래스가 아니더라도, 아래에 정의된 함수들이 받아들일 수 있을 만큼 이미지 프로토콜의 충분한 부분집합을 구현하고 있으면 참을 반환합니다. 현재는 단순히 mode, size, buffer 속성이 존재하는지와 올바른지만 검사합니다.
PyObject* PyImage_GetMode(PyObject *image)
Py_ssize_t PyImage_GetWidth(PyObject *image)
Py_ssize_t PyImage_GetHeight(PyObject *image)
int PyImage_Clip(PyObject *image)
PyObject* PyImage_Split(PyObject *image)
PyObject* PyImage_GetBuffer(PyObject *image)
int PyImage_AsBuffer(PyObject *image, const void **buffer, Py_ssize_t *buffer_len)
이 함수들은 대응하는 Python 속성이나 메서드와 동등합니다. 이미지 메모리는 GIL과 이미지 또는 그 버퍼에 대한 참조를 보유한 상태에서만 접근할 수 있으며, 성분당 8비트를 초과하는 모드에서는 각별히 주의해야 합니다. 데이터는 네이티브 바이트 순서로 저장되며 2바이트 또는 4바이트 경계에 정렬되어 있지 않을 수 있습니다.

예시

새 Image 클래스와 프로토콜을 사용하는 일반적인 작업의 몇 가지 예제:

# create a new black RGB image of 6x9 pixels
rgb_image = imageop.Image(imageop.RGB, (6, 9))

# same as above, but initialize the image to bright red
rgb_image = imageop.Image(imageop.RGB, (6, 9), color=(255, 0, 0))

# convert the image to YCbCr
yuv_image = imageop.Image(imageop.JPEG_YV12, source=rgb_image)

# read the value of a pixel and split it into three ints
r, g, b = rgb_image[x, y]

# modify the magenta component of a pixel in a CMYK image
cmyk_image[x, y].m = 13

# modify the Y (luma) component of a pixel in a *YV12 image and
# its corresponding subsampled Cr (red chroma)
yuv_image.y[x, y] = 42
yuv_image.cr[x // 2, y // 2] = 54

# iterate over an image
for line in rgb_image:
    for pixel in line:
        # swap red and blue, and set green to 0
        pixel.value = pixel.b, 0, pixel.r

# find the maximum value of the red component in the image
max_red = max(pixel.r for pixel in rgb_image.pixels())

# count the number of colors in the image
num_of_colors = len(set(tuple(pixel) for pixel in image.pixels()))

# copy a block of 4x2 pixels near the upper right corner of an
# image and paste it into the lower left corner of the same image
image[:4, -2:] = image[-6:-2, 1:3]

# create a copy of the image, except that the new image can have a
# different (usually empty) info dict
new_image = image[:]

# create a mirrored copy of the image, with the left and right
# sides flipped
flipped_image = image[::-1, :]

# downsample an image to half its original size using a fast, low
# quality operation and a slower, high quality one:
low_quality_image = image[::2, ::2]
new_size = image.size.width // 2, image.size.height // 2
high_quality_image = imageop.Image(size=new_size, source=image)

# direct buffer access
rgb_image[0, 0] = r, g, b
assert tuple(rgb_image.buffer[:3]) == (r, g, b)

하위 호환성

이 PEP가 영향을 미치는 영역 중 하위 호환성을 고려해야 하는 영역은 세 가지입니다:

  • Python 2.6: 기존 모듈 내용은 건드리지 않고 imageop 모듈에 새 클래스와 객체가 추가됩니다. Tkinter.PhotoImage에 새 메서드와 속성이 추가되며, 그 __getitem__과 __setitem__ 메서드는 정수, 튜플, 슬라이스를 받아들이도록 수정됩니다(현재는 문자열만 받아들입니다). 모든 변경 사항은 기존 기능의 상위 집합을 제공하므로 큰 호환성 문제는 없을 것으로 예상됩니다.
  • Python 3.0: PEP 3108에 따라 imageop 모듈의 레거시 내용은 삭제됩니다. 이 제안에서 정의한 모든 것은 일반적인 2.x/3.0 차이점을 예외로 하면 Python 2.x에서와 똑같이 동작합니다(예: long 정수 지원과 str 인스턴스를 바이트 시퀀스로 해석하는 기능은 제거됩니다).
  • 외부 라이브러리: 표준 이미지 메서드와 속성의 이름과 의미는 이미지를 조작하는 일부 외부 라이브러리(적어도 PIL, wxPython, pygame 포함)가 기존 코드와의 호환성을 깨뜨리지 않고 자신의 이미지 클래스에 새 프로토콜을 구현할 수 있도록 신중하게 선택되었습니다. 이미지 프로토콜과 NumPy 배열 사이의 명백한 충돌은 size 속성의 값과 image[x, y] 표현식의 좌표 순서뿐입니다.

참조 구현

이 PEP가 채택되면, 저자는 새 클래스들의 참조 구현을 순수 Python으로(CPython, PyPy, Jython, IronPython에서 실행할 수 있도록) 제공하고, CPython 표준 라이브러리에 포함하기에 적합하도록 Python과 C로 속도를 최적화한 두 번째 구현도 제공할 것입니다. 저자는 필요한 Tkinter 패치도 제출할 것입니다. 모든 코드는 Python 2.x용 버전과 Python 3.0용 버전으로 제공될 것입니다(두 버전은 매우 비슷할 것으로 예상되며, Python 3.0 버전은 아마 거의 완전히 자동으로 생성될 것입니다).

감사의 글

이 PEP가 채택되면, 그 구현은 Google Summer of Code 프로그램을 통해 Google의 후원을 받습니다.