【Python】matplotlibでグラフ画像をポチポチ数値化→CSV出力まで

この記事で解決するお悩み
  • グラフの画像はあるけど数値データがほしい!
  • グラフ画像をPythonで数値データ化させたい!

はじめに

論文やネット記事など、「グラフの画像はあるけれど、数値データがない」なんてことはよくありますよね。そんなグラフ画像からでも数値データを抽出し、CSVファイルとして出力させるPythonのコードを紹介します。

matplotlibをメインに簡単なライブラリしか使っていないので、Python初心者にも大変優しい内容となっています。

動作フローとイメージ

  1. 背景にグラフ画像を出す
  2. 軸の基準となる座標(原点・x軸基準点・y軸基準点)をクリック動作で記録
  3. 数値化したいデータの座標をクリック動作で記録(右クリックで記録終了)
  4. 記録した座標→数値データに変換
  5. グラフ画像と記録データプロットを重ね合わせて精度確認
  6. 数値化データをCSVファイルとして保存

下記動画を見ていただくと、非常に分かりやすいかと思います。

Python全スクリプト

それでは実際のPythonコードを紹介していきます。早速全コードは以下の通りです。

from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
%matplotlib qt
#jupyter notebook上で別ウィンドウでfigure出力させる


###初期設定(ファイルごとに変更必要あり)
# 写真ファイル名の入力
name = 'ENG_TN'
typ = 'png'

# 座標の定義
# [x0, y0], [x1,y0],[y1,x0]の定義
x0=0
y0=130
x1=6000
y1=200
###初期設定ここまで


#軸の基準となる座標(原点・x軸基準点・y軸基準点)をクリックで記録
def onclick_frame(event):
    global ix_frame, iy_frame, coords
    ix_frame, iy_frame = event.xdata, event.ydata
    print ('x_frame = %d, y_frame = %d'%(ix_frame, iy_frame))    
    frame_data.append((ix_frame, iy_frame))

    if len(frame_data) == 3:
        fig.canvas.mpl_disconnect(cid)
        print('基準点の座標取得が完了しました。\n数値化したいデータを左クリックしてください。右クリックで終了します。')
        cid_data = fig.canvas.mpl_connect('button_press_event', onclick_point)             
    return frame_data, coords


#数値化したいデータをクリックで記録
def onclick_point(event):
    if event.button == 1:   
        global ix, iy, coords
        ix, iy = event.xdata, event.ydata
        print ('x = %d, y = %d'%(ix, iy))
        coords.append((ix, iy))
    
    elif event.button == 3:        
        print('データ取得が完了しました。')
        fig.canvas.mpl_disconnect(cid)
        plt.close(fig)
        date_set(frame_data, coords, x0, y0, x1, y1)
    return coords


#記録した座標→数値データに変換
def date_set(frame_data, coords, x0, y0, x1, y1):
    #座標変換
    frame_array = np.array(frame_data)
    data_array = np.array(coords)

    #figureにおけるx軸位置・y軸位置を算出
    x0_im=(frame_array[0,0]+frame_array[2,0])/2 #x軸左端の座標計算
    x1_im=frame_array[1,0] #x軸右端の座標計算

    y0_im=(frame_array[0,1]+frame_array[1,1])/2 #y軸左端の座標計算
    y1_im=frame_array[2,1] #y軸右端の座標計算

    #1座標当たりの変化量
    dx = (x1-x0)/(x1_im-x0_im)
    dy = (y1-y0)/(y1_im-y0_im)

    #座標→数値データに変換
    data_value = np.empty_like(data_array)
    data_value[:,0] = x0+(data_array[:,0]-x0_im)*dx
    data_value[:,1] = y0+(data_array[:,1]-y0_im)*dy
    plot_check(data_value, x0_im, x1_im, y0_im, y1_im)
    return


#グラフ画像と記録データプロットを重ね合わせて精度確認    
def plot_check(data_value, x0_im, x1_im, y0_im, y1_im):
    #チェック用のfig作成
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(1, 1, 1)
    
    #取得したデータをプロット
    plt.plot(data_value[:,0],data_value[:,1], color="b",marker="x", markersize=12, markeredgecolor="r",)
    plt.xlim(x0,x1)
    plt.ylim(y0,y1)

    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    #グラフ画像を重ね合わせる
    im = Image.open(picname)
    im_crop=im.crop((x0_im, y1_im, x1_im, y0_im))
    ax.imshow(im_crop, extent=[*xlim, *ylim], aspect='auto', alpha=0.3)
    plt.show()
    fig.savefig(piccheckname) #チェック用の画像を保存

    np.savetxt(outputname, data_value, delimiter=',') #数値データをcsvにして保存
    print(data_value)
    return


###以下メイン実行###
#ファイル名の定義
picname = name + '.' + typ
piccheckname = name + '_check.png'
outputname = name + '.csv'

#空のリストを作成
frame_data = []
coords = []


fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(1, 1, 1)

# 背景に画面イメージを出す
im = Image.open(picname)
ax.imshow(im)


print('原点→x軸基準点→y軸基準点の順に左クリックしてください')
cid = fig.canvas.mpl_connect('button_press_event', onclick_frame)

使い方およびコードの解説

STEP. 0 事前準備

今回はサンプルとしてこんな画像を使ってみます。トヨタのエンジンに関するグラフでYahooニュースからもってきました。画像のグラフはエンジンの性能を表すものですが、数式も数値もありません。青色の方のグラフに着目してグラフ画像を数値化していきましょう。

それでは、まず初期設定の項目を埋めていきましょう。必要な項目は以下の通りです。

  • ファイル名 & 拡張子(jpg & pngに対応)
  • 原点、x軸基準点、y軸基準点の数値(画像内の赤点の座標)

スクリプトで書くと今回のケースはこのようになります。

###初期設定(ファイルごとに変更必要あり)
# 写真ファイル名の入力
name = 'ENG_TN'
typ = 'png'

# 座標の定義
# [x0, y0], [x1,y0],[y1,x0]の定義
x0=0
y0=130
x1=6000
y1=200
###初期設定ここまで

STEP. 1 背景にグラフ画像を出す

Imageというライブラリを使ってfigureに画像を表示させます。

fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(1, 1, 1)

# 背景に画面イメージを出す
im = Image.open(picname)
ax.imshow(im)

STEP. 2 軸の基準となる座標をクリック動作で記録

グラフ軸の基準となる座標を、「canvas.mpl_connect」というライブラリを使って左クリックで記録していきます。
クリック順は、①原点→②x軸基準点→③y軸基準点です。
3点記録し終えると自動的に記録が止まるので、3回クリック後は特に何もしなくてよいです。

スクリプトは以下の通りです。

#軸の基準となる座標(原点・x軸基準点・y軸基準点)をクリックで記録
def onclick_frame(event):
    global ix_frame, iy_frame, coords
    ix_frame, iy_frame = event.xdata, event.ydata
    print ('x_frame = %d, y_frame = %d'%(ix_frame, iy_frame))    
    frame_data.append((ix_frame, iy_frame))

    if len(frame_data) == 3:
        fig.canvas.mpl_disconnect(cid)
        print('基準点の座標取得が完了しました。\n数値化したいデータを左クリックしてください。右クリックで終了します。')           
    return frame_data, coords

STEP. 3 数値化したいデータの座標をクリック動作で記録

表示されている画像グラフに沿って、ポチポチと左クリックで座標を記録していきます。STEP.2と同様の手法です。記録が終了したら、右クリックで終了させてください。

クリプトは以下の通りです。

#数値化したいデータをクリックで記録
def onclick_point(event):
    if event.button == 1:   
        global ix, iy, coords
        ix, iy = event.xdata, event.ydata
        print ('x = %d, y = %d'%(ix, iy))
        coords.append((ix, iy))
    
    elif event.button == 3:        
        print('データ取得が完了しました。')
        fig.canvas.mpl_disconnect(cid)
        plt.close(fig)
    return coords

STEP. 4 記録した座標→数値データに変換

STEP. 3で記録した座標を数値データに変換していきます。
ポイントは「figure上の1座標あたりの数値データ変化量を求める」ことで「figureの座標スケールからグラフ画像の座標スケールを変換してあげる」ことです。

詳細なスクリプトは以下の通りです。

#記録した座標→数値データに変換
def date_set(frame_data, coords, x0, y0, x1, y1):
    #座標変換
    frame_array = np.array(frame_data)
    data_array = np.array(coords)

    #figureにおけるx軸位置・y軸位置を算出
    x0_im=(frame_array[0,0]+frame_array[2,0])/2 #x軸左端の座標計算
    x1_im=frame_array[1,0] #x軸右端の座標計算

    y0_im=(frame_array[0,1]+frame_array[1,1])/2 #y軸左端の座標計算
    y1_im=frame_array[2,1] #y軸右端の座標計算

    #1座標当たりの変化量
    dx = (x1-x0)/(x1_im-x0_im)
    dy = (y1-y0)/(y1_im-y0_im)

    #座標→数値データに変換
    data_value = np.empty_like(data_array)
    data_value[:,0] = x0+(data_array[:,0]-x0_im)*dx
    data_value[:,1] = y0+(data_array[:,1]-y0_im)*dy
    return

STEP. 5 グラフ画像と記録データプロットを重ね合わせて精度確認

STEP. 4までで記録した座標や変換した数値が正しいか確認すべく、もともとのグラフ画像と今回数値化したデータを重ね合わせていきます。イメージはこの画像の通りです。今回のケースでは、グラフ画像をうまく再現できているようです。ちなみに、この重ね合わせた画像もチェック用に保存する仕様となっています。

スクリプトは以下の通りです。このスクリプト内でCSV出力も行なっています。

#グラフ画像と記録データプロットを重ね合わせて精度確認    
def plot_check(data_value, x0_im, x1_im, y0_im, y1_im):
    #チェック用のfig作成
    fig = plt.figure(figsize=(8, 6))
    ax = fig.add_subplot(1, 1, 1)
    
    #取得したデータをプロット
    plt.plot(data_value[:,0],data_value[:,1], color="b",marker="x", markersize=12, markeredgecolor="r",)
    plt.xlim(x0,x1)
    plt.ylim(y0,y1)

    xlim = ax.get_xlim()
    ylim = ax.get_ylim()

    #グラフ画像を重ね合わせる
    im = Image.open(picname)
    im_crop=im.crop((x0_im, y1_im, x1_im, y0_im))
    ax.imshow(im_crop, extent=[*xlim, *ylim], aspect='auto', alpha=0.3)
    plt.show()
    fig.savefig(piccheckname) #チェック用の画像を保存

    np.savetxt(outputname, data_value, delimiter=',') #数値データをcsvにして保存
    print(data_value)
    return

STEP. 6 数値化データをCSVファイルとして保存

正確にはSTEP. 5ですでにCSVとして保存していますが、ここでは出力された結果について確認していきましょう。
出力結果は以下のようになります。

このように、A列にx座標、B列にy座標が記録されます。

コメントを残す

メールアドレスが公開されることはありません。