matplotlib의 colormap을 사용하여 2D 데이터를 빠르게 이미지화

정리하면



2차원 데이터를 이미징하고 저장하려면 matplotlib.pyplot.imshow()가 느리므로 matplotlib의 Colormap를 사용하여 직접 저장할 수 있습니다.

문제 설정



어느 2차원 데이터가 (많이) 있고, 이미지화하고 싶다고 합니다.
import numpy as np
from matplotlib import pyplot as plt
data = np.arange(100).reshape(10,10)
plt.imshow(data)
plt.savefig("test.png")



하지만 아시다시피 matplotlib는 느립니다. 느린 것은 일일이 그래픽 렌더러를 불러 렌더링해 그 결과를 보존하고 있기 때문입니다.
하지만 하고 싶은 것은, data 를 어떠한 변환으로 RGB에 밀어 넣어 보존하는 것입니다.

컬러 맵을 사용할 수 있으면 기쁩니다.

이미지 데이터로 밀어넣기만 하면 다음과 같이 하면 됩니다.
import PIL.Image
def quantize(arr):
    arr = np.floor((arr - arr.min())  * 255 / (arr.max() - arr.min())).astype(np.uint8)

img = PIL.Image.fromarray(quantize(data))
img = img.resize([400,400], resample=PIL.Image.NEAREST) #拡大しているだけ
img.save("test.png")



컬러 이미지라면
img = np.zeros(data.shape+(3,), dtype=np.uint8) # キャンバスの用意
img[:,:,0] = 255 # 適当に1
img[:,:,1] =  255 - quantize(data) # 適当に2
img[:,:,2] = quantize(data) # 適当に3

img = PIL.Image.fromarray(img)
img = img.resize([400,400], resample=PIL.Image.NEAREST) #拡大しているだけ
img.save("test.png")

로 「적당」이라고 쓴 곳에 적당한 함수를 할당하면 됩니다만, 그 적당한 함수를 스스로 준비하는 것은 엄격한 것이 있다. 라고 할까 처음에 나타낸 imshow() 의 거리의 그림을 얻으려면 어떻게 하면 좋겠지요, 라고 하는 것이 됩니다.

Colormap 사용법



아시다시피 (?)matplotlib에는 컬러 맵이라는 개념이 있습니다.
값에서 색상 (RGB 값)으로 매핑합니다. plt.imshow(data, cmap=hoge) 등으로 지정할 수 있지요.

컬러맵의 변형에 대해서는 blog: matplotlib의 cmap (colormap) 매개 변수 목록입니다. 등 참조.

matplotlib 내에서 컬러맵은 matplotlib.colors.Colormap 객체로 표현됩니다 1
Colormap 객체는 2d-array를 받고 RGBA 값이 들어있는 3d (2d*4ch) array를 반환하는 함수(에 머리카락이 생긴 것)로 취급할 수 있습니다.matplotlib.pyplot.get_cmap() 에 컬러맵의 이름을 지정하면 Colormap 가 반환됩니다. 즉,
cmap = matplotlib.pyplot.get_cmap(NAME_OF_COLORMAP)
image = Colormap(raw_array)

라는 느낌으로, 화상화가 완성입니다.
이 이미지를 렌더러에 표시시키는 것이 plt.imshow() 그래서, 이미지를 원하면
PIL.Image.fromarray(image).save("filename.png")

쪽이 훨씬 빠르다는 것이 되네요. 예를 보여줍니다.
import matplotlib.pyplot as plt

def minmax(arr):
    arr = (arr - arr.min()) / (arr.max() - arr.min())
    return arr

cmap = plt.get_cmap("viridis")
data = minmax(data)
img = cmap(data, bytes=True)
img = PIL.Image.fromarray(img)
img = img.resize([220,220], resample=PIL.Image.NEAREST) #拡大しているだけ
img.save("test.png")



  • 주의점 1.
  • viridis는 matplotlib의 기본 컬러맵 이름입니다.


  • 참고 2.
  • 입력으로서는 [0.,1.] (float)인가 [0, 255] (uint)의 범위의 데이터가 기대되고 있으므로, 먼저 minmax 정규화에 의해 값의 범위를 조정하고 있습니다.


  • 참고 3.
  • bytes=True를 지정하면 [0, 255]의 데이터가 반환됩니다. 기본적으로 [0.,1.]의 데이터가 반환됩니다. PIL.Image에서는 RGBA는 밖에 [0, 255] 취급할 수 없기 때문에 조금 주의합니다.


  • 이상으로 plt.imshow()와 동등한 것이 매우 빨리 실현될 수 있습니다.

    참고: 자작 colormap의 만드는 방법이나 기존 컬러맵의 내용을 분석하는 방법에 대해서는 다음이 상세합니다.
  • matplotlib의 colormap의 RGB 정보 취득 및 관련 조작



  • 실제로는 Colormap 객체를 상속한 LinearSegmentedColormap 이나 ListedColormap 로서 표현되고 있습니다.

    좋은 웹페이지 즐겨찾기