5.32. 저장 및 압축¶
지금까지의 모든 페이지는 캠 위에서 이미지를 다루었습니다. 즉 프레임 버퍼로 캡처하거나 MicroPython 힙에 할당하고, image 모듈 메서드를 통해 조작한 다음, IDE 미리보기에 표시하거나 같은 스크립트 내의 다음 단계로 전달했습니다. 대부분의 애플리케이션은 어느 시점에 그 반대의 작업을 해야 합니다. 즉 현재 RAM에 있는 이미지를 어딘가 영구적인 곳에 두어야 합니다 – SD 카드, USB 호스트, 네트워크 등 – 카메라가 아닌 다른 무언가가 그것을 읽을 수 있도록 말입니다.
image 모듈은 이 작업을 위한 두 가지 경로를 제공합니다. save 경로는 이미지를 파일시스템의 파일로 기록하며, 파일 형식은 확장자에 따라 선택되고 인코딩 세부 사항은 메서드가 처리합니다. to-format 경로는 인코딩된 바이트 스트림을 담은 Image 객체를 반환하며, 파일시스템을 전혀 거치지 않고 스트리밍 또는 네트워킹 호출에 넘기기에 적합합니다. 각 경로는 서로 다른 애플리케이션에 맞으며, 둘 다 내부적으로 동일한 압축 엔진을 기반으로 합니다.
5.32.1. 파일로 저장하기¶
save() 는 이미지를 파일시스템의 지정된 경로에 기록합니다:
img.save("/sdcard/capture.jpg")
img.save("/sdcard/capture.bmp")
img.save("/sdcard/region.jpg", roi=(40, 60, 200, 150), quality=85)
형식은 파일 확장자에서 선택됩니다. 다섯 가지 확장자가 인식됩니다. .bmp 는 Windows 비트맵 을 기록하고(무손실, 무압축, 캡처된 픽셀을 바이트 단위 그대로), .pgm 은 portable graymap 을 기록하며(무손실, 그레이스케일 전용), .ppm 은 portable pixmap 을 기록하고(무손실, RGB), .jpg 와 .jpeg 는 둘 다 JPEG를 기록합니다(손실, 압축). 수신 이미지는 선택한 컨테이너에 맞는 색상 형식이 이미 갖추어져 있어야 합니다 – 색상 이미지를 .pgm 으로 저장하는 것은 오류입니다.
roi 는 다른 모든 image 모듈 메서드의 roi 키워드와 마찬가지로 저장을 이미지의 하위 사각형으로 제한합니다. 기본값은 전체 이미지입니다. 이 키워드는 JPEG로 압축된 이미지를 저장할 때는 무시되는데, 디스크상의 형태가 이미 전체 프레임을 포함하고 있으며 크롭을 거쳐 재인코딩하는 것은 기존의 압축된 바이트를 저장하는 의미를 무색하게 만들기 때문입니다.
quality 는 0 부터 100 까지의 JPEG 압축 품질이며 출력이 JPEG일 때만 의미가 있습니다(무손실 형식에서는 이 키워드가 무시됩니다). 기본값 50 은 대부분 의 애플리케이션에 적절한 균형점입니다. 70 에서 85 는 더 높은 시각적 품질을 위한 구간이고, 30 에서 50 은 작은 썸네일과 대역폭 제약이 있는 전송에 적절한 범위이며, 90 이상은 이미지를 수동으로 검사하거나 압축 아티팩트에 민감한 다운스트림 알고리즘에 통과시키는 경우를 위해 예약되어 있습니다.
수신 이미지가 반환되므로 호출을 연쇄할 수 있습니다: img.save("/sdcard/x.jpg").draw_string(0, 0, "saved"). 반환된 객체는 동일한 인메모리 이미지이며, 저장은 부수 효과입니다.
전형적인 사용 사례는 캡처-및-기록(capture-and-log) 패턴입니다. 트리거가 발생하고(블롭이 검출되거나, 버튼이 눌리거나, 타이머가 만료됨), 스크립트가 프레임을 캡처하고, 파일명에 타임스탬프를 덧붙인 다음, save() 를 호출하여 이미지를 SD 카드로 보냅니다. IDE 미리보기는 계속 실행되고, 다음 트리거가 발생하며, 저장된 파일이 쌓여 갑니다.
5.32.2. 메모리로 인코딩하기¶
대상이 파일시스템이 아니라 네트워크 연결, 시리얼 포트, 또는 다른 모듈의 입력일 때, 애플리케이션은 인코딩된 바이트 스트림을 디스크가 아니라 메모리에 두어야 합니다. to_jpeg() 와 to_png() 가 정확히 그것을 생성합니다:
encoded = img.to_jpeg(quality=80, copy=True)
bytes_to_send = encoded.bytearray()
sock.send(bytes_to_send)
기본 동작은 제자리(in-place) 변환입니다. 수신 이미지가 JPEG(또는 PNG) 이미지로 변환되고 동일한 객체가 반환됩니다. copy=True 를 사용하면 변환 결과가 새로 할당된 힙 객체에 기록되고, copy_to_fb=True 를 사용하면 출력이 프레임 버퍼에 들어갑니다. 이 선택은 다른 모든 변환 메서드가 제공하는 것과 동일합니다 – 기본적으로 제자리에서, 원본이 이후에도 필요하면 복사로 처리합니다.
quality 와 subsampling 은 save 경로가 제공하는 것과 동일한 JPEG 튜닝 노브입니다. subsampling 은 크로마 서브샘플링 방식을 선택합니다. image.JPEG_SUBSAMPLING_AUTO 는 선택한 품질에 가장 적합한 것을 고르고, image.JPEG_SUBSAMPLING_444 는 크로마를 전체 해상도로 유지하며(가장 큰 파일, 최고의 색상 정확도), image.JPEG_SUBSAMPLING_422 와 image.JPEG_SUBSAMPLING_420 은 한 축 또는 두 축에서 크로마 해상도를 절반으로 줄입니다(더 작은 파일, 일반적인 시청 거리에서는 보이지 않는 약간의 색상 부드러워짐). 애플리케이션에 특별한 요구가 없는 한 기본값 AUTO 가 올바른 선택입니다.
to_png() 를 통한 PNG는 무손실 이지만 인코딩이 더 느리고 사진 콘텐츠의 경우 JPEG보다 더 큰 파일을 생성합니다(사진 콘텐츠는 PNG의 예측 방식 아래에서 압축이 잘 되지 않습니다). 이미지가 라인 아트, 스크린샷이거나 캡처된 프레임 위에 그려진 선명한 모서리의 그래픽을 포함할 때 PNG를 사용하십시오 – 무손실 인코딩은 JPEG라면 부드럽게 만들었을 날카로운 에지를 보존합니다. 그 외에는 JPEG가 올바른 기본값입니다.
to_jpeg() 와 to_png() 는 둘 다 다른 변환 메서드가 취하는 것과 동일한 그리기 스타일의 위치 인수 및 스케일 키워드를 받습니다 – x_scale, y_scale, roi, rgb_channel, alpha, color_palette, alpha_palette, hint – 따라서 같은 호출로 원본의 스케일 조정, 크롭, 또는 팔레트 매핑된 버전을 한 단계로 인코딩할 수 있습니다. compress() 는 to_jpeg() 의 레거시 표기입니다. 두 메서드는 동일한 인수를 받고 동일한 결과를 생성합니다.
5.32.3. 압축이 가져다 주는 것¶
JPEG 대 raw의 트레이드오프 뒤에 있는 수치는 한 번쯤 짚어볼 가치가 있습니다.
320x240 RGB565 프레임은 153,600바이트입니다(QVGA에서 캡처한 한 프레임). 640x480 프레임은 614,400바이트이고, 1280x960 프레임은 2,457,600바이트입니다. 이들 중 어느 것도 데스크톱이나 휴대폰 디스플레이에 비하면 크지 않지만, 총 RAM이 수 MB뿐이고 쓰기 대역폭이 유한한 SD 카드, 그리고 일반적으로 USB CDC, UART, 또는 무선 모듈을 통해 적당한 속도로 동작하는 호스트 링크를 가진 캠의 맥락에서는 상당한 크기입니다.
quality=50 의 JPEG는 일반적으로 사진으로 캡처한 프레임을 10배에서 20배까지 압축합니다. 그 614 KB짜리 640x480 프레임은 30에서 60 KB의 인코딩된 바이트 스트림이 됩니다. quality=85 에서는 압축이 5배에서 10배로 떨어집니다(동일 프레임에 대해 60에서 120 KB). quality=10 에서는 – 아티팩트가 많지만 여전히 알아볼 수 있으며 – 압축이 30배에서 50배에 이릅니다(12에서 20 KB).
이 수치들은 저장된 프레임으로 무엇을 하는 것이 실용적인지를 결정합니다. 10 MB/s를 유지하는 SD 카드 경로는 quality=50 JPEG로 인코딩된 VGA 콘텐츠를 초당 30프레임으로 여유 있게 처리합니다(약 1에서 2 MB/s). 동일한 콘텐츠를 압축 없이 저장하려면 18 MB/s 이상이 필요한데, 이는 캠의 파일시스템 경로가 카드로 유지할 수 있는 한계를 넘어섭니다. CDC를 통해 1 MB/s로 JPEG 인코딩된 프레임을 가져오는 USB 호스트는 30에서 60 KB 프레임을 대략 초당 15에서 30프레임으로 수신합니다. 동일한 속도로 raw 프레임을 가져오면 초당 한두 프레임을 얻습니다.
요컨대, 압축 메서드는 단지 저장을 위한 편의 기능이 아닙니다. 그것은 캡처된 프레임을 애플리케이션이 중요하게 여기는 프레임 속도로 캠 외부에서 사용 가능하게 만들어 주는 것입니다. 올바른 압축을 선택하는 것 – 일반적인 기록에는 JPEG 품질 50, 품질 작업에는 80, 라인 아트 캡처에는 PNG – 은 자명하지 않은 모든 캠 애플리케이션의 일상적인 작업의 일부입니다.