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:
번역·라이선스 안내
이 비공식 한국어 번역은 원문 Copyright 절의 Public Domain 조건에 따라 제공합니다. 원저자와 공식 원문은 그대로 표시합니다. 수정되지 않은 기준 원문 · 공식 최신판
초록
Python 세계에서 현재 이미지 저장 및 조작 상황은 매우 파편화되어 있습니다. 이미지 객체를 사용하는 거의 모든 라이브러리가 다른 라이브러리의 클래스와 호환되지 않고 대개 Python답지도 않은 자체 이미지 클래스를 구현하고 있습니다. 표준 라이브러리에 기본적인 RGB 이미지 클래스(Tkinter.PhotoImage)가 존재하지만, Tkinter 프로그래밍 외의 용도로는 사실상 사용할 수 없으며 사용되지도 않습니다.
이러한 파편화는 개발자들의 귀중한 정신적 공간을 차지할 뿐만 아니라, 서로 다른 라이브러리 간의 이미지 교환도 필요한 수준보다 느리고 복잡하게 만듭니다.
이 PEP는 기존 사용자 기반과의 하위 호환성을 깨뜨리지 않고 표준 라이브러리 안팎의 기존 이미지 클래스가 받아들이고 구현할 수 있기를 기대하는 단순하고 Python다운 이미지 프로토콜/인터페이스를 정의하여 이러한 상황을 개선할 것을 제안합니다. 실제로 이는 최소한의 이미지와 유사한 객체가 어떻게 보이고 동작해야 하는지를 정의하는 것으로, 파일과 유사한 객체의 read() 및 write() 메서드와 비슷한 방식입니다.
새로운 프로토콜을 구현하면서 기본적인 이미지 조작 기능을 제공하는 클래스를 표준 라이브러리에 포함하는 방안도, 기존 이미지 클래스에 프로토콜 지원을 추가하는 데 도움이 되는 믹스인 클래스와 함께 제안합니다.
PEP 연기
이 PEP에서 다루는 개념에 대한 추가 검토는 PEP의 목표를 추진하고 피드백을 수집 및 반영하는 데 관심이 있는 현재의 주도자가 없고, 이를 효과적으로 수행할 충분한 시간도 부족하여 연기되었습니다.
근거
Python 표준 라이브러리에 포함할 고품질 모듈을 준비하는 좋은 방법은 경쟁하는 외부 라이브러리 사이에서 자연 선택이 이루어져 유용한 기능과 많은 사용자 기반을 갖춘 명확한 승자가 나오기를 단순히 기다리는 것입니다. 그러면 사실상의 표준을 표준 라이브러리에 포함하여 공식적으로 승인할 수 있습니다.
안타깝게도 이러한 접근 방식은 Python 세계에서 지배적인 이미지 클래스를 만드는 데는 제대로 작동하지 않았습니다. 이미지를 필요로 하는 거의 모든 서드파티 라이브러리가 다른 라이브러리의 이미지 클래스와 호환되지 않는 자체 클래스를 만들기 때문입니다. 이는 프로그램이 PIL(Python Imaging Library)을 사용하여 이미지를 생성하고 조작한 다음 wxPython이나 pygame을 사용하여 표시하는 것이 전적으로 합리적이기 때문에 실제 문제입니다. 그러나 이러한 라이브러리는 서로 다르고 호환되지 않는 이미지 클래스를 사용하며, 일반적인 해결책은 소스에서 이미지를 수동으로 “(width, height, bytes_string) 튜플”로 “내보낸” 다음 대상 형식으로 새 인스턴스를 생성하여 “가져오는” 것입니다. 이 방식은 작동하지만, 필요한 것보다 더 보기 흉하고 느립니다.
때때로 사용되는 또 다른 “해결책”은 한 클래스에서 다른 클래스로 변환하는 특정 어댑터 및/또는 변환기를 만드는 것입니다(예를 들어 PIL은 PIL 이미지를 Tkinter 이미지 클래스와 호환되는 클래스로 변환하는 ImageTk 모듈을 제공합니다). 그러나 이 접근 방식은 관련된 라이브러리 수가 늘어날수록 잘 확장되지 않으며 사용자에게 여전히 번거롭습니다. 완벽하게 쓸 만한 이미지 객체가 있다면 다음 메서드에 전달하기 전에 왜 변환해야 하며, 왜 다음 메서드가 제 이미지를 있는 그대로 받아들일 수 없는 것입니까?
이 문제는 언급된 세 라이브러리에 국한되지 않으며 아마도 여러 원인이 있습니다. 그중에는 해결하기 전에 이해하는 것이 매우 중요하다고 생각하는 두 가지 원인도 포함됩니다.
- 오늘날의 컴퓨팅 세계에서 이미지는 특정 영역에 엄격히 묶이지 않은 기본 타입입니다. 따라서 위에서 언급한 세 라이브러리(PIL, wxPython 및 pygame)의 이미지 클래스 중 명확한 승자는 결코 나오지 않을 것입니다. 이들은 서로 다른 영역을 다루며 실제로 서로 경쟁하지 않기 때문입니다.
- Python 표준 라이브러리는 서드파티 모듈이 채택하거나 모방할 수 있는 좋은 이미지 클래스를 제공한 적이 없습니다.
Tkinter.PhotoImage은 기본적인 RGB 기능을 제공하지만, 여러 클래스 중 단연 가장 느리고 조잡하며 Tkinter 루트 윈도우가 생성된 후에만 인스턴스화할 수 있습니다.
이 PEP는 네 가지 방식으로 이러한 상황을 개선하려고 합니다.
- Python 측과 C 측 모두에서 단순하고 Python다운 이미지 프로토콜/인터페이스를 정의하며, 기존 사용자 기반과의 하위 호환성을 깨뜨리지 않고 표준 라이브러리 안팎의 기존 이미지 클래스가 이를 받아들이고 구현할 수 있기를 기대합니다.
- 표준 라이브러리에 다음 세 가지 새 클래스를 포함할 것을 제안합니다.
ImageMixin은 새 프로토콜을 구현하는 데 필요한 거의 모든 것을 제공합니다. 주된 목적은 기존 라이브러리가 이 인터페이스를 최대한 간단하게 지원하도록 하는 것이며, 경우에 따라서는 기본 클래스 목록에 추가하고 생성자에 약간의 내용을 추가하는 것만으로 충분합니다.Image는ImageMixin의 서브클래스이며, 서로 다른 픽셀 형식 간에 이미지 크기를 조정하고 변환할 수 있는 생성자를 추가합니다. 이는 새 프로토콜의 빠르고 효율적인 기본 구현을 제공하기 위한 것입니다.ImageSize는 간단한 보조 클래스입니다. 자세한 내용은 아래를 참조하십시오.
Tkinter.PhotoImage은 새 프로토콜을 구현하며(주로ImageMixin클래스를 통해 구현함), 이미지를 받을 수 있는 모든 Tkinter 메서드가 인터페이스를 구현하는 모든 객체를 받아들이도록 수정됩니다. 덧붙여 이 PEP의 작성자는 가장 많이 사용되는 외부 라이브러리의 개발자들과 협력하여 동일한 목표를 달성할 예정입니다(해당 라이브러리의 클래스에서 프로토콜을 지원하고 이를 구현하는 모든 클래스를 받아들이도록 함).- 새로운
PyImage_*함수가 CPython C API에 추가됩니다. 이 함수들은 프로토콜의 C 측을 구현하며, 첫 번째 매개변수로 이를 지원하는 any객체를 받습니다. 해당 객체가Image/ImageMixin클래스의 인스턴스가 아니더라도 마찬가지입니다.
최종 사용자가 얻게 될 주요 효과는 서로 다른 라이브러리 간 이미지 교환이 간소화된다는 점(모든 것이 순조롭게 진행되면 모든 Python 라이브러리가 다른 모든 라이브러리의 이미지를 받아들임)과 새로운 Image 클래스가 별도의 설정 없이 제공된다는 점입니다. 새로운 클래스는 사진을 원하는 크기로 자르거나 크기를 조정한 다음 창에 표시할 적절한 위젯에 전달하거나, 텍스처를 어둡게 만든 다음 3D 라이브러리에 전달하는 것과 같은 단순하지만 일반적인 사용 사례를 지원하도록 설계되었습니다.
Image 클래스는 PIL, Pythonmagick 또는 NumPy를 대체하거나 이들과 경쟁하도록 설계된 것이 아니며, 이 세 라이브러리 기능의 일부(매우 작은 부분)만 제공하더라도 마찬가지입니다. 특히 PIL은 dozens개의 클래스, 필터, 변환 및 파일 형식을 갖춘 매우 풍부한 이미지 조작 기능을 제공합니다. PIL(또는 이와 유사한 무언가)을 표준 라이브러리에 포함하는 것이 가치 있는 목표일 수도 있고 아닐 수도 있지만, 이는 이 PEP의 범위를 완전히 벗어납니다.
명세
imageop 모듈은 새로운 클래스와 객체의 default 위치로 사용됩니다. 오랫동안 어느 정도 유사한 기능을 제공하는 함수들을 호스팅해 왔기 때문입니다. 다만 원한다면 새로운 모듈을 만들 수도 있습니다(예: 새로운 “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세기에 오신 것을 환영합니다.
다음은 모드와 모드를 포함한 근거에 관한 간단한 설명이며, 모드는 네 그룹으로 나뉩니다.
- grayscale (
L*모드): 과학 컴퓨팅에서 많이 사용됩니다(이러한 사용자는 매우 넓은 동적 범위와 높은 정밀도도 필요로 할 수 있으므로, 구성 요소당 32비트를 사용하는 유일한 모드인L32가 이에 해당합니다). 또한 색상 이미지의 단일 구성 요소를 그레이스케일 이미지로 간주하는 것이 유용할 때도 있습니다(이는 평면 이미지의 개별 평면에 사용됩니다. 아래의YV12를 참조하십시오). 구성 요소의 이름('l', 소문자 L)은 휘도를 나타내며, 두 번째 선택적 구성 요소('a')는 알파 값으로 픽셀의 불투명도를 나타냅니다. alpha = 0은 완전한 투명성을 의미하고, alpha = 255/65535는 완전히 불투명한 픽셀을 나타냅니다. - RGB* modes: 일반적인 색상 이미지입니다. 선택적 알파 구성 요소는 그레이스케일 모드에서와 같은 의미를 가집니다.
- 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의 유효한 값은 범위 16~236(236 제외)에 있으며, Cb 및 Cr의 유효한 값은 범위 16~241에 있습니다.JPEG_YV12는YV12와 유사하지만, 세 구성 요소가 256개 값의 전체 범위를 가질 수 있습니다. 이는 거의 모든 JPEG/JFIF 파일과 MJPEG 비디오 프레임에서 사용하는 고유 형식입니다. 다른 모든 지원 모드와 관련하여 이 두 모드가 보이는 “특이함”은 기존 라이브러리와 애플리케이션에서 이 방식으로 널리 사용된다는 사실에서 비롯됩니다. 이것이 이 모드들이 포함된 이유이기도 하며, YCbCr이 더 큰 색 공간이기 때문에 RGB로 무손실 변환할 수 없다는 사실도 그 이유입니다. 픽셀 값의 특이한 4:2:0 평면 배열은 대부분의 경우 세 평면을 서로 별개의 그레이스케일 이미지로 간주할 수 있으므로 비교적 쉽게 지원할 수 있습니다. - 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의 서브클래스 인스턴스이며 이름과 동일한 값을 가집니다(예: imageop.RGB == 'RGB'). 단, L32의 값은 'I'입니다. 이는 기존 PIL 사용자를 위한 하위 호환성만을 위한 것이며, 여기서 제안하는 이미지 프로토콜을 사용하는 새 코드는 이 세부 사항에 의존해서는 안 됩니다.
이미지 프로토콜
이미지 프로토콜을 지원하는 모든 객체는 다음 메서드와 속성을 제공해야 합니다.
mode
이 이미지의 픽셀 형식과 배열이며,MODES집합에 있는 상수 중 하나입니다.
size
ImageSize Class의 인스턴스이며, 두 정수로 이루어진 네임드 튜플입니다. 이미지 픽셀 단위의 너비와 높이를 나타내며, 둘 다 >= 1이어야 하고size의width및height속성으로도 액세스할 수 있습니다.
buffer
0에서 255 사이의 정수 시퀀스이며, 이미지 데이터를 저장하는 데 실제로 사용되는 바이트입니다(즉, 값을 수정하면 이미지 픽셀에 영향을 주고 그 반대도 마찬가지입니다). 데이터는 패딩과 특수 메모리 정렬 없이 행 우선/C 연속 순서로 저장되며, 구성 요소당 8비트를 초과하는 경우에도 그러합니다. 지원되는 유일한 메서드는__len__, 정수 및 슬라이스 인덱스를 사용하는__getitem__/__setitem__,__iter__이며, C 측에서는 버퍼 프로토콜을 구현합니다.이는 이미지에 대한 상당히 저수준의 인터페이스이며, 사용자는 구성 요소당 8비트를 초과하는 모드에 올바른 (네이티브) 바이트 순서를 사용하고
YV12이미지에 올바른 값 범위를 사용해야 합니다. 버퍼는 이미지에 대한 참조를 유지할 수도 있고 유지하지 않을 수도 있지만, 해당 이미지가 가비지 컬렉터에 의해 소멸된 후에도 버퍼를 사용하는 것은 (쓸모없을 수는 있어도) 여전히 안전합니다(wxPython의 이미지 클래스와 다른 라이브러리의 이미지 클래스에도 변경이 필요합니다). 구현 세부 사항: 이는array('B'),bytes()객체 또는 특수한 고정 길이 타입일 수 있습니다.
info
이미지와 관련된 임의의 메타데이터(예: DPI, 감마, ICC 프로필, 노출 시간 등)를 포함할 수 있는dict객체입니다. 이 데이터의 해석은 이 PEP의 범위를 벗어나며, 이미지를 생성하거나 저장하는 데 사용된 라이브러리에 따라 달라질 수 있습니다. 이미지의 메서드가 새 이미지를 반환하는 경우 자체info속성의 메타데이터를 복사하거나 조정할 수 있습니다(ImageMixin구현은 항상 빈info딕셔너리를 가진 새 이미지를 생성합니다).
bits_per_componentbytes_per_pixelcomponent_namescomponentsintervalsplanarsubsampling해당mode.*속성에 대한 단축 표기입니다.
map(function[, function...]) -> None
이미지의 각 픽셀에 대해 각 구성 요소를 해당 함수에 통과시켜 매핑합니다. 함수 하나만 전달하면 각 구성 요소에 반복해서 사용합니다. 이 메서드는 이미지를 in place로 수정하며 일반적으로 매우 빠르지만(대부분의 경우 함수가 적은 횟수만 호출되고, 분기가 없는 단순한 함수의 경우 한 번만 호출될 수도 있습니다), 전달되는 함수에는 여러 제한이 적용됩니다.
- 함수는 단일 정수 인자를 받아 숫자를 반환해야 합니다(
map은 필요한 경우 결과를 가장 가까운 정수로 반올림하고range(0, 2 ** bits_per_component)로 잘라냅니다).- 함수는 인자에 대한 어떤 연산에서 발생한
BaseException,Exception또는Exception의 알려지지 않은 서브클래스도 가로채려고 해서는 안 됩니다. 구현에서는 이상한 객체를 전달하여 속도를 최적화하려고 시도할 수 있으므로, 단순한"if n == 10:"도 예외를 발생시킬 수 있습니다. 이러한 예외는 그냥 무시하십시오.map이 처리합니다. 그 밖의 예외를 포착하는 것은 괜찮습니다.- 함수는 부작용이 없어야 하며, 그 결과는
map을 한 번 호출하는 동안 변경될 수 있는 값(인자 제외)에 의존해서는 안 됩니다.
rotate90() -> imagerotate180() -> imagerotate270() -> image이미지 중심을 기준으로 시계 반대 방향으로 90도, 180도 또는 270도 회전한 이미지의 복사본을 반환합니다.
clip() -> None
YV12이미지의 잘못된 구성 요소 값을 허용되는 최솟값 또는 최댓값(mode.intervals참조)으로 포화시키며, 다른 이미지 모드에서는 아무 작업도 하지 않고 매우 빠르게 완료됩니다.YV12이미지를 저장하거나 내보내는 라이브러리에서는 이 메서드를 항상 호출하는 것이 좋습니다. 중간 연산(예:map메서드)이 유효한 구간을 벗어난 픽셀 값을 할당할 수 있기 때문입니다.
split() -> tuple[image]
이미지의 개별 구성 요소에 해당하는L,L16또는L32이미지의 튜플을 반환합니다.
평면 이미지도 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와 같은 우수한 색상 관리 도구를 사용하는 것이 좋습니다. 새 이미지에는 빈infodict가 있습니다.
라인 객체
라인 객체는 다음 속성과 메서드를 지원합니다(예를 들어 이미지를 순회할 때 반환됩니다):
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)이 함수들은 이에 대응하는 파이썬 속성이나 메서드와 동등합니다.
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객체의 내용을 필요한 경우 지정된mode로 재조정하고 변환하여 초기화한 새Image인스턴스를 반환합니다.
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)이 함수들은 이에 대응하는 파이썬 속성이나 메서드와 동등합니다. 이미지 메모리는 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 차이(예를 들어long정수 지원 및str인스턴스를 바이트 시퀀스로 해석하는 기능이 제거되는 것)를 제외하면 Python 2.x에서와 동일하게 동작할 것입니다. - external libraries: 표준 이미지 메서드와 속성의 이름 및 의미는, 이미지를 다루는 일부 외부 라이브러리(최소한 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의 후원을 받습니다.
Copyright
This document has been placed in the public domain.