概要
matplotlib を使っていたら以下のようなエラーが発生した。
AttributeError: 'FigureCanvasAgg' object has no attribute 'tostring_rgb'
この時の対策について記す。
実行時の状況によって、対策は異なるものと思われる。
場合分け
1. 外部ライブラリで tostring_rgb
が使われていた場合
以下の記事に対策が書かれている。
matplotlib のバージョンを 3.7.0
に落とせば解決するらしい。
つまり、以下のようにすれば良い、ということだろう。
$ pip uninstall matplotlib
$ pip install -q matplotlib==3.7.0
自分はこの場合ではなかった。
2. 自分が書いたソースコードで tostring_rgb
を使っている場合
tostring_rgb
を buffer_rgba
に置き換えたら解決した。
先に前期の github の issue を見たので混乱してしまったが、呼び出すメソッドを変更すれば問題が解決に向かう。
以下の例を参考にされたい。
# 修正前
import matplotlib.pyplot as plt
fig: plt.Figure = plt.figure(figsize=(4, 4))
fig.canvas.draw()
# なんやかんや描画処理...
data = fig.canvas.tostring_rgb() # ここを変更する
# 修正後
import matplotlib.pyplot as plt
fig: plt.Figure = plt.figure(figsize=(4, 4))
fig.canvas.draw()
# なんやかんや描画処理...
# ❌ボツ対策
data = fig.canvas.tostring_argb() # これをすると、順番がおかしくなって結果的に色がおかしくなる
# ⭕️こっちがよかった
buffer = fig.canvas.buffer_rgba() # なので、こうする
ただし、このままだと buffer は上手く扱えない。
その続きを例えば以下のようにしたところ、自然な numpy 配列型のデータに変換された。
# 画像をバイト列で取得する。
# data = fig.canvas.tostring_argb() # 色がおかしくなる
buffer = fig.canvas.buffer_rgba()
# この時点では、画像の各ピクセルが1次元の配列として格納されている。
one_line_img: np.ndarray = np.frombuffer(buffer, dtype=np.uint8)
# 画像の大きさを取得する。
w, h = fig.canvas.get_width_height()
c = len(one_line_img) // (w * h) # channel 数
# numpy 配列に変換する
img = one_line_img.reshape(h, w, c)
参考
.buffer_rgba() について
fig.canvas.buffer_rgba()
を使うアイデアはこの記事に書かれていた。