HOWTO · Matplotlib

Matplotlib에서 플롯을 표시하지 않고 이미지 파일로 저장하는 방법

비대화형 백엔드와 savefig(), 올바른 저장 후 닫기 순서를 사용해 창을 열지 않고 Matplotlib Figure를 저장합니다.

이 페이지의 내용

GUI 창을 열지 않고 Matplotlib 플롯 전체를 이미지로 저장하려면 show()를 호출하지 말고, 특정 Figure를 savefig()로 저장한 뒤에 닫으세요. 디스플레이가 없는 환경에서는 pyplot을 가져오기 전에 비대화형 백엔드 Agg를 선택하세요.

Matplotlib 플롯을 표시하지 않고 저장하기

GUI 창을 열지 않고 완전한 Matplotlib Figure를 저장하려면 show()를 호출하지 말고, 특정 Figure에서 savefig()를 실행한 다음 파일이 기록된 뒤에만 close()를 호출합니다. 서버, CI 작업, 컨테이너처럼 명시적인 헤드리스 백엔드가 필요한 환경에서는 matplotlib.pyplot을 가져오기 전에 정적 Agg 백엔드를 선택합니다.

따라서 핵심 순서는 백엔드를 선택하고, Figure를 만들고, 저장한 뒤 닫는 것입니다. Figure 참조를 유지하면 저장 대상이 명확하며 pyplot이 현재 활성 상태로 간주하는 Figure에 의존하지 않습니다.

Agg를 명시적으로 선택하는 방식은 디스플레이 서버가 없는 컴퓨터에서도 스크립트가 일관되게 동작해야 할 때 유용합니다. 단지 파일을 저장한다는 이유만으로 항상 필요한 것은 아닙니다. Matplotlib이 적합한 비대화형 백엔드를 자동으로 선택할 수도 있습니다. 편집할 수 없는 명령에는 MPLBACKEND=Agg 환경 변수를 사용할 수 있습니다. Matplotlib은 우선순위를 적용해 마지막으로 해당하는 설정을 사용하므로, 이유 없이 여러 선택 방법을 함께 쓰지 마십시오.

헤드리스 Agg 백엔드로 Figure 저장하기

다음 검증된 예제는 show()를 호출하지 않고 사인파 플롯을 sine-wave.png로 저장합니다. Agg는 비대화형 백엔드이며 래스터 출력을 기록하므로 화면 창이 필요하지 않습니다.

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

output = Path("sine-wave.png")
x = np.linspace(0, 2 * np.pi, 200)

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(x, np.sin(x), color="#2166ac", linewidth=2)
ax.set(title="Sine wave", xlabel="x", ylabel="sin(x)")
ax.grid(alpha=0.25)

fig.savefig(output, dpi=150, bbox_inches="tight")
plt.close(fig)

saved = plt.imread(output)
assert output.is_file() and saved.size > 0
print("backend:", matplotlib.get_backend())
print("saved:", output.name)
print("valid PNG:", True)

검증된 출력은 다음과 같습니다.

backend: Agg
saved: sine-wave.png
valid PNG: True

레이블이 지정된 축과 격자가 있는 저장된 Matplotlib 사인파 Figure.

이미지는 프로세스의 현재 작업 디렉터리를 기준으로 기록됩니다. 스케줄러나 서비스가 다른 위치에서 스크립트를 시작할 수 있다면 절대 Path 또는 알려진 프로젝트 디렉터리에서 만든 경로가 더 안전합니다.

어설션은 생성된 PNG를 다시 열어 파일이 존재하고 이미지 데이터가 들어 있는지 확인합니다. 이 검사는 출력이 없거나 읽을 수 없는 경우를 찾아내며, 위 미리보기는 예상한 곡선, 레이블, 격자를 확인해 줍니다. 빈 내보내기나 잘못된 위치가 큰 문제가 되는 운영 환경에서도 이처럼 구체적인 검증 단계를 선택하십시오.

이미지 형식과 savefig() 옵션 선택하기

Figure.savefig()는 일반적으로 파일 이름 확장자에서 형식을 추론합니다. 널리 지원되는 래스터 출력에는 PNG, 웹에서 확대 가능한 그래픽에는 SVG, 벡터 문서에는 PDF를 사용합니다. 파일 이름에 적절한 확장자가 없다면 format 인수를 명시합니다.

래스터 파일에서 dpi는 출력 해상도를 제어하지만 SVG나 PDF의 벡터 경로를 더 선명하게 만들지는 않습니다. bbox_inches="tight"는 주변의 여백을 줄이고, transparent=True는 별도 색상을 지정하지 않은 Figure와 Axes의 배경을 투명하게 만듭니다. 이 옵션들은 내보낸 파일에 영향을 주며 창 표시 여부와는 무관합니다. 사용 가능한 형식은 설치된 백엔드와 선택적 라이브러리에 따라 달라질 수 있습니다.

사용처에 맞춰 형식을 선택하십시오. 문서나 웹 페이지에 넣는 차트에는 PNG가 적합하고, 브라우저에서 선과 텍스트를 확대할 때는 SVG가 선명하며, 인쇄 작업에는 PDF가 편리합니다. 높은 dpi는 래스터 크기와 파일 용량을 늘리므로 무조건 가장 큰 값을 쓰지 말고 표시 또는 인쇄 요구 사항에 맞추십시오.

ioff(), show()와 저장 순서 이해하기

plt.ioff()는 pyplot의 대화형 모드를 끄지만 범용 헤드리스 전환 기능은 아닙니다. 특히 노트북 프런트엔드는 대화형 모드가 꺼져 있어도 셀의 마지막 Figure를 자동으로 표시할 수 있습니다. GUI 없는 렌더러가 필요할 때 Agg를 사용하고, 노트북에서는 마지막 Figure 값을 억제하거나 저장한 뒤 명시적으로 닫으십시오.

반대로 savefig()를 호출하는 데 대화형 모드나 show()가 필요하지는 않습니다. show()는 대화형 백엔드로 Figure를 표시하기 위한 것이며 내보내기 전용 스크립트에서는 생략하는 것이 일반적입니다. 따라서 대화형 모드를 끄는 것과 파일 렌더링 백엔드를 선택하는 것은 서로 관련되지만 다른 문제를 해결합니다.

close(fig)를 호출하기 전에 저장하십시오. 먼저 닫으면 pyplot에서 해당 Figure 참조가 제거되므로 이후의 상태 기반 plt.savefig()가 다른 Figure 또는 새 Figure를 대상으로 할 수 있습니다. show() 문서도 블로킹 show() 뒤에 저장하면 빈 Figure가 생성될 수 있다고 설명합니다. 먼저 저장하거나 Figure 객체를 유지한 채 그 객체의 savefig() 메서드를 호출하십시오.

반복문에서 Figure 해제하고 메모리에 저장하기

Pyplot은 인터페이스를 통해 만든 Figure 참조를 유지합니다. 많은 플롯을 만드는 배치 작업은 저장에 성공할 때마다 close(fig)를 호출해 Figure와 관련 메모리를 해제해야 합니다. 후속 처리에서 오류가 발생할 수 있다면 try/finally 정리 패턴을 사용하십시오.

다른 API에 이미지 바이트가 필요하고 디스크 파일은 필요 없다면 파일 경로 대신 io.BytesIO 객체를 fig.savefig()에 전달합니다. 읽거나 업로드하기 전에 seek(0)으로 버퍼를 되감고 저장 후에는 Figure도 닫으십시오.

출력 디렉터리가 없을 때 처리하기

savefig()는 이미지 파일을 만들지만 존재하지 않는 상위 디렉터리는 만들지 않습니다. 다음 검증된 경계 예제는 이 실패를 처리하고 모든 경우에 Figure를 닫습니다.

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt

output = Path("missing") / "plot.png"
fig, ax = plt.subplots()
ax.plot([1, 2, 3], [2, 4, 3])

try:
    fig.savefig(output)
except FileNotFoundError:
    print("Create the parent directory before savefig().")
finally:
    plt.close(fig)
Create the parent directory before savefig().

실제 내보내기에서는 먼저 output.parent.mkdir(parents=True, exist_ok=True)로 디렉터리를 만드십시오. 프로세스에 쓰기 권한이 있는지도 확인해야 합니다.

수치 배열에는 imsave() 사용하기

입력이 Axes, 레이블, 범례와 다른 artist를 포함한 플롯 Figure가 아니라 2D 또는 RGB(A) 수치 배열이라면 matplotlib.pyplot.imsave()를 사용합니다. 이 함수는 배열 값을 이미지 픽셀에 매핑하며 완전한 플롯을 저장하는 Figure.savefig()를 대체하지 않습니다.

from pathlib import Path

import matplotlib

matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

pixels = np.array([[0.0, 0.5, 1.0], [1.0, 0.5, 0.0]])
output = Path("array-image.png")

plt.imsave(output, pixels, cmap="gray", vmin=0, vmax=1)

saved = plt.imread(output)
assert output.is_file() and saved.shape[:2] == pixels.shape
print("saved array:", output.name)
print("pixel grid:", saved.shape[0], "x", saved.shape[1])
saved array: array-image.png
pixel grid: 2 x 3