LoginSignup
2
6

More than 3 years have passed since last update.

【Python】(線)グラフの画像から値を抽出する

Last updated at Posted at 2020-11-05

はじめに

グラフの画像はあるけどデータはない...ってことありませんか?
画像があるなら抽出しましょう。

image.png
pandas 0.7.3 documentation -Plotting with matplotlib
            $\downarrow$
array([-0.4028436 , -0.09518499, 0.21247362, ..., 39.12322275, 39.12322275, 39.12322275])
            +
image.png

これだけ細かいとあまり精度は望めない。。。

処理の流れ

色域選択で目的のグラフを取得
  ↓
縦軸方向に平均
  ↓
欲しいサンプル数分補間
  ↓
スケール調節
  ↓
出力

実装

こちらからColabで実行出来ます

import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact
import requests
from PIL import Image
import io

画像データの取得、とりあえずアルファは無視

path = "画像のパス"
im = plt.imread(path)
if im.shape[2] == 4:im = im[:,:,:-1]
if im.max() > 1:im /= 255
h, w, _ = im.shape
plt.imshow(im[::-1])

後のスケール調節用にグラフの範囲のみトリミング

@interact(x_min=(0, w), x_max=(0, w), y_min=(0,h), y_max=(0,h))
def Plot(x_min=0, x_max=w, y_min=0, y_max=h):
    global imag
    plt.figure(figsize=(7, 7))
    imag = im[min(y_min,y_max-1):max(y_min+1, y_max), min(x_min,x_max-1):max(x_min+1, x_max)]
    plt.imshow(imag[::-1])

色域選択で抽出したいグラフを選択, Threshold(閾値)を調節して無駄な部分が入らないようにする

@interact(x=(0, imag.shape[1]), y=(0,imag.shape[0]), thresh=(1,10))
def Plot(x, y, thresh):
    global p
    p = ((imag - imag[y, x]) ** 2).sum(axis=2) < (1 / (1<<thresh))
    print(p.sum())
    plt.imshow(p[::-1])
    plt.plot([x, x], [0, imag.shape[0]], color="r")
    plt.plot([0, imag.shape[1]], [imag.shape[0]-y, imag.shape[0]-y], color="r")

縦軸方向に平均を取る

p = np.pad(p, 1, "constant")
sx = np.arange(len(p[0]))[p.argmax(axis=0)!=0]
sy = []

for i in p.T:
    j = np.where(i!=0)[0]
    if j.tolist():
        sy.append(j.mean())

サンプル数の選択, 移動平均(畳み込み)でノイズの除去

@interact(sample=(5, 1250), conv_size=(1, 21, 2))
def fit(sample, conv_size):
    global y
    x = np.linspace(sx.min(), sx.max(), sample)
    y = np.convolve(np.pad(np.interp(x, sx, sy), (conv_size-1)//2, "edge"), np.ones(conv_size) / conv_size, "valid")
    plt.plot(x, y)
    plt.xlim(0,len(p[0]))
    plt.ylim(0, len(p))

最初に切り取ったグラフの範囲を入力

yl = list(map(int,input("Y-range of trimmed graph?         ").split(",")))

スケール調整, 出力

y_out = y * (yl[1] - yl[0]) / p.shape[0] + yl[0]
y_out

グラフ出力

plt.plot(y_out)
plt.ylim(*yl)

↑ はJupyter用のプログラムなので分けてある部分でセルを区切らないと実行出来ません

さいごに

HTMLとか使ってトリミングとか色域選択をもっとインタラクティブにしたら使い勝手が良くなりそう。

2
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
6