Global lo0

One good turn deserves another. GPS×ImageProcess×...

Python メモリエラーの対処法

Python メモリエラーの対処法

f:id:kuri_megane:20180923185909p:plain

Pythonでメモリを気にすることはほとんどありませんが,時には考える場面に出くわすようです.


ざっくりまとめると...


目次


前提

直面した問題

動画を扱うプログラムをひたすら回していると,このようなエラーで死にました.

f:id:kuri_megane:20181224224121p:plain

原因を探る

Pythonでメモリを気にすることはほとんどありませんが,メモリがうまく解放できていないところを探します.

今回は,次のようなコードに着目しました.

  • 問題のあるモジュールの呼び出し
def func():

    # OpenCVを使って動画を開く
    cap = cv2.VideoCapture(self.input_data, cv2.CAP_FFMPEG)

    # 動画を1枚ずつ処理している
    while cap.isOpened():

        ret, frame = cap.read()

        # フレームの表示
        tools.view_result_img(
            show_img=frame,
            show_name="frame",
        )

        # フレームの保存
        tools.save_result_img(
            save_img=frame,
            save_name="frame.pnf"
        )
  • 問題のあるモジュール1
def save_result_img(save_img, save_name):
    """
    描画した画像を保存します.
    :param save_name: 保存ファイル名
    :type save_name: str
    :return: なし
    """

    cv2.imwrite(save_file_name, save_img)
  • 問題のあるモジュール2
def view_result_img(show_img, show_name):
    """
    描画した画像を表示します.
    :param show_img: 表示する画像
    :param show_name: 表示ウィンドウ名
    :return: なし
    """

    # ウィンドウに表示
    cv2.imshow(show_name, show_img)

    # ESCキー押下で終了
    if cv2.waitKey(30) & 0xff == 27:
        exit(0)

解決への考え方

Pythonでメモリの解放が行われるのは,メモリを使用しているオブジェクトの参照が無くなったときだそうです.

つまり,メモリがうまく解放されないときは,オブジェクトの宣言や参照を見直します.

解放法1

  • 代入により上書きする
a = "メモリを使うデータ1"

…

a = "メモリを使うデータ2"

メモリを使うデータ1 に用が無くなったら,同じオブジェクト名で上書きしてしまえば,メモリを使うデータ1のメモリは解放刺されます.

解決法2

import gc

a = "メモリを使うデータ1"

...

del a

...

gc.collect()

今回はこちらを使いました.

先のプログラムでは,OpenCVの画像データをメモリ上に保持し続けていると考え,次のように変更しました.

  • 問題のあるモジュール1
def save_result_img(save_img, save_name):
    """
    描画した画像を保存します.
    :param save_name: 保存ファイル名
    :type save_name: str
    :return: なし
    """

    cv2.imwrite(save_file_name, save_img)

    del save_img
    gc.collect()
  • 問題のあるモジュール2
def view_result_img(show_img, show_name):
    """
    描画した画像を表示します.
    :param show_img: 表示する画像
    :param show_name: 表示ウィンドウ名
    :return: なし
    """

    # ウィンドウに表示
    cv2.imshow(show_name, show_img)

    # ESCキー押下で終了
    if cv2.waitKey(30) & 0xff == 27:
        exit(0)

    del show_img
    gc.collect()

取り急ぎ,これでメモリ不足のエラーで死ぬことが無くなったので良しとしました.

最後に

今回は,OpenCVの画像データがメモリを食う問題でしたが,他にもいくつかの解決法がありそうです.

nonbiri-tereka.hatenablog.com

www.haya-programming.com

また,試してはいませんが,原因を調べる方法も紹介されています.

blog.amedama.jp