대형 이미지 변환시의 ImageMagick 설정 리눅스

ImageMagick은 이미지를 만들거나 고치는데 널리 사용되는 자유 소프트웨어이다. 그런데 일반적으로 사용되지 않는 아주 큰 이미지를 다루려고 하는 경우 제약이 있다. 일단 다음을 보자.

$ convert -size 5000x5000 xc:white canvas.png

위 명령어는 흰색으로 채워진 가로 세로 각 5000 픽셀의 canvas.png라는 이미지를 생성한다. 정확하게는 5000x5000의 흰 캔버스 이미지(xc:white)를 입력으로 하여 canvas.png란 파일로 출력하는데, convert 명령을 이용해 새 이미지를 만들 때 이렇게 한다. 실행해 보면 아마 별 문제가 없을 것이다. 그런데 필자의 경우, 여기서 크기를 더 키우면 문제가 발생했다. (문제가 발생하는 정확한 크기는 다를 수 있다)

$ convert -size 10000x10000 xc:white canvas.png
convert-im6.q16: DistributedPixelCache '127.0.0.1' @ error/distribute-cache.c/ConnectPixelCacheServer/244.
convert-im6.q16: cache resources exhausted `canvas.png' @ error/cache.c/OpenPixelCache/3945.
convert-im6.q16: memory allocation failed `canvas.png' @ error/png.c/WriteOnePNGImage/8756.

$ convert -size 30000x30000 xc:white canvas.png
convert-im6.q16: width or height exceeds limit `white' @ error/cache.c/OpenPixelCache/3802.
convert-im6.q16: no images defined `canvas.png' @ error/convert.c/ConvertImageCommand/3258.

원하는 크기로 10000x10000을 입력했더니, 뭔가 디스크에 쓰는 것 같더니 곧 실패해버리고 만다. 이어서 더 큰 크기인 30000x30000으로 설정해 보았더니, 이번엔 뭔가 하는 것 같지도 않고 바로 에러를 내보낸다. 메시지를 보면, 뭔가 기본적인 제약이 설정되어 있는 것 같다.

제약은 어떻게 확인할 수 있을까? identify라는 명령을 쓰면 된다.

$ identify -list resource
Resource limits:
  Width: 16KP
  Height: 16KP
  Area: 128MP
  Memory: 256MiB
  Map: 512MiB
  Disk: 1GiB
  File: 768
  Thread: 8
  Throttle: 0
  Time: unlimited

너비(Width) 및 높이(Height)를 보면 최대 16KP 까지로 제한되어있는데, 이 때문에 변환 가능한 이미지는 15999x15999 까지이다. 따라서 이 값을 수정하면 30000x30000 이미지도 문제없이 시도할 수 있다. 이 제약들은 policy.xml 이라는 설정파일에 저장되어 있는데, 우분투 17.04 기준 /etc/ImageMagick-6/policy.xml 이며 잘 모르겠으면 locate 명령을 이용해 파일을 검색하면 된다. (같은 제약들을 실행시 -limit 옵션으로도 설정할 수 있지만 policy.xml의 제약 이상으로 설정하지 못한다.)

그런데 처음에 시도한 10000x10000 이미지의 경우, 너비 및 높이 제한이 아니라 캐시가 남아있지 않아서 종료되었다. 이 경우 변환에 필요한 자원이 부족하다는 것인데, 이 자원이 어느정도인지 어떻게 알아낼 수 있을까?

다시 메시지를 살펴보자. 일단 convert-im6은 ImageMagick 6의 convert 명령이란 뜻이다. 뒤에 붙은 q16은 채널 심도(Channel Depth)라고도 하는 채널당 비트 수가 16인 버전임을 의미한다. 이것으로 변환에 필요한 자원을 추정할 수 있다. IM v6 Q16에서 1개의 픽셀은 RGBA 4개 채널로 이루어져 있고, 각 채널은 16비트씩을 사용하므로 1 픽셀당 8 바이트를 쓰게 된다(16 * 4 / 8). 이때 만일 100x100 크기의 이미지를 만든다면, 여기 소요되는 자원은 100 * 100 * 8 / 1000 이므로 80KB로 추정할 수 있다. (참고) 이를 10000x10000 이미지에 적용하면, 10000 * 10000 * 8 / 1e9  = 0.8 GB다. 그리고 convert 명령은 input과 output으로 이루어지는 변환이기 때문에 각각 자원이 필요하다. 그래서 0.8GB에 다시 2를 곱하면 1.6GB 가량이 된다.

convert 명령의 -debug 옵션을 통해 이것을 실제로 확인할 수 있다.

$ convert -debug "cache,resource" -size 10000x10000 xc:white canvas.png
2017-06-09T00:33:30+09:00 0:00.000 0.000u 6.9.7 Cache convert-im6.q16[10504]: cache.c/SetPixelCacheExtent/3737/Cache
  extend white[0] (/tmp/magick-105046WGN3DhKI7D6[3], disk, 800MB)
2017-06-09T00:33:30+09:00 0:00.000 0.000u 6.9.7 Cache convert-im6.q16[10504]: cache.c/OpenPixelCache/4044/Cache
  open white[0] (/tmp/magick-105046WGN3DhKI7D6[3], Disk, 10000x10000 800MB)
...
2017-06-09T00:37:48+09:00 0:05.200 1.990u 6.9.7 Cache convert-im6.q16[11872]: cache.c/OpenPixelCache/4044/Cache
  open canvas.png[0] (/tmp/magick-11872aGzR1M4h9kb3[3], Disk, 10000x10000 1GB)
...

흥미로운 것은 내보낼 파일의 포맷으로 JPG를 선택할 경우 예상대로 800MB(총 1.6GB)가 사용되는 반면, PNG 파일에는 로그에서처럼 25%의 추가적인 용량이 요구된다는 것이다. 아마 내부적으로 채널이 하나 더 추가된 CMYKA를 사용하는 것이 아닐까? 정확한 내용은 확인이 필요하다. 그래서 이 경우에는 총 1.8GB 가량이 필요하고, 실제로 Disk 값을 변경해 가며 테스트해 보면 1.6GiB(=1.72GB) 에서는 실패하고, 1.7GiB(=1.82GB) 에서는 성공한다. 이런 기준으로 30000x30000을 계산하면 필요한 자원 크기는 JPG의 경우 30000 * 30000 * 8 * 2 / 1e9 = 14.4GB, PNG의 경우 여기에  1.8GB(25%)가 추가된  16.2GB 이다. 이제 여기에 맞게 policy.xml 내용을 변경해주면 된다.

Tag :
,

Leave Comments