画像ファイルgraph.jpgをPythonで読み取りたい（１）

# 画像ファイルのグラフをPythonで数値として読み取りたい

何かDIYなどで工作する時，部品カタログを見ますよね．
そのpdfファイルは容易にWebからダウンロードできたりします．

ドキュメントの中に，製品の仕様を示すグラフが掲載されていたりするのですが，性能などを計算するのに数値データとして欲しい場面がたくさんあります．

**＜グラフサンプル＞**

![graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1979979/3e68d693-114b-45ae-87c3-99c97d7e90e5.jpeg)

この画像ファイル（graph.jpg）から，数値データとしてゲットできないかなと思って，Pythonプログラムを試してみることにしました．

残念ながら，このプログラムでは，グラフの数値をうまく取得することができなかった．

引き続き，別のやり方を探っていこうと思っているので，今後のために，うまく取得できなかったプログラムや結果も残しておこうと思い，記事にしました．

**強調**

```read_graph.py
# -*- coding: utf-8 -*-
import cv2
import numpy as np
from matplotlib import pyplot as plt
import japanize_matplotlib
![スクリーンショット 2025-03-09 10.05.56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1979979/664174af-705a-450a-a97a-3ead1fec6d6c.jpeg)
![スクリーンショット 2025-03-09 10.05.56.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1979979/a5c4d67c-e753-42a1-b52e-7087ebe320c5.jpeg)
![read_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1979979/ea55f152-e802-4d6b-9cca-5fae73d1978a.jpeg)

# 画像の読み込み
image_path = 'graph.jpg'
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)

if image is None:
    print("Error: 画像が見つかりません。パスを確認してください。")
else:
    print("画像が正常に読み込まれました。")

    # エッジ検出を使用してグリッド線と外枠を検出
    edges = cv2.Canny(image, 50, 150)

    # 垂直線と水平線を検出
    lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=100, maxLineGap=10)
    mask = np.ones_like(image) * 255

    if lines is not None:
        for line in lines:
            x1, y1, x2, y2 = line[0]
            cv2.line(mask, (x1, y1), (x2, y2), 0, 2)  # グリッド線と外枠を黒で描画

    # グリッド線と外枠を削除した画像を作成
    image_without_grid = cv2.bitwise_and(image, mask)

    # 画像の前処理（ノイズ除去）
    blurred = cv2.GaussianBlur(image_without_grid, (3, 3), 0)
    _, threshold = cv2.threshold(blurred, 150, 255, cv2.THRESH_BINARY_INV)

    # 輪郭の検出
    contours, _ = cv2.findContours(threshold, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # トルク曲線部分のデータポイントの抽出
    torque_points = []
    for contour in contours:
        # 輪郭の周囲の長さを計算
        perimeter = cv2.arcLength(contour, True)
        approx = cv2.approxPolyDP(contour, 0.02 * perimeter, True)
        
        # 曲線部分を判定するために頂点数が多いものを選択
        if len(approx) > 5 and cv2.contourArea(contour) < 1000:  # 閾値を調整
            # 指定されたx軸、y軸範囲内の点のみを抽出
            for point in contour:
                x, y = point[0]
                if 500 <= x <= 6500 and 95 <= y <= 155:
                    # 赤色の曲線を無視（色判定は実装が難しいため、位置情報で判定）
                    if (x + y) % 2 != 0:  # 例として偶数座標を無視
                        torque_points.append((x, y))

    # x, y座標のリストとして表示
    torque_x_coords = [point[0] for point in torque_points]
    torque_y_coords = [point[1] for point in torque_points]

    print("Torque Curve X座標:", torque_x_coords)
    print("Torque Curve Y座標:", torque_y_coords)

    # オリジナルの画像を表示
    plt.figure(figsize=(10, 6))
    plt.imshow(image, cmap='gray')
    plt.title('Original Image')
    plt.grid(False)
    plt.show()

    # 抽出したトルク曲線を表示
    plt.figure(figsize=(10, 6))
    plt.scatter(torque_x_coords, torque_y_coords, color='blue', marker='o', label='Torque Curve')
    plt.title('Scatter Plot of Extracted Torque Curve (Ignoring Grid Lines and Outline)')
    plt.xlabel('X座標')
    plt.ylabel('Y座標')
    plt.legend()
    plt.grid(True)
    plt.show()
```

**＜読み取ったｘ，ｙ値をプロット＞**
![read_graph.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1979979/04a3cd07-d6b6-48d3-a1fa-704901473b8b.jpeg)

noteの記事に書きましたが，

https://note.com/docp3/n/n5c4e9a79585a

やはり，こういった自分でグラフをなぞるといった一手間をかまさないと
OCR的に値を取得するのは難しいのでしょうか．．．

今後も，細々と改良を検討していこうと思っています．．．