Python

画像 から 縦線 のみ取得してみた!【 Python OpenCV 】

Kuni.W

Python + OpenCV を使って、 画像 から 線 を取得をする場合、 縦線 のみ取得したいという場合もあると思います。
直線を取得する”HoughLinesP”の使い方の基本をおさえることが出来ていれば応用で可能となります。今回は縦線の取得に絞って紹介したいと思います。

縦線のみ取得はこちらから

[https://way2se.ringtrees.com/py_cv2-003/]

スポンサーリンク

サンプル画像

どこにでもあるような2次元表をサンプルとして取り上げます。
(サイズ → 横:313px 縦:132px)

使用するライブラリ説明

【HoughLinesP】

lines = cv2.HoughLinesP(img, rho, theta, threshold, minLineLength, maxLineGap)

  • img:取り込む画像。
  • rho:よくわかりませんが、デフォルト 1 で問題なさそう。
  • theta:ランダムに線を判断するための回転角。大きくすれば複雑な線を認識する。
  • threshold:文字などの非線を除外する閾値(の様なもの)
  • minLineLength:連なる点の集合の、最小値。この値以上を線としてみなす。
  • maxLineGap:どれだけ離れている点(線)を一つの線としてみなすか判断。

各引数をふった挙動事例は以下をご覧ください。

[https://way2se.ringtrees.com/py_cv2-004/]

サンプルソース(コード)

/* import設定 */
import os
import configparser as cfg
import glob
import cv2
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from operator import itemgetter

/* Config設定 */
cfg = cfg.ConfigParser()
cfg.read('config.ini', 'UTF-8')

/* File読み込みフォルダ指定 */
files = glob.glob(cfg.get('Folder', 'img_folder') + '/*.jpg', recursive=True)

/* File読み込み(複数可) */
for file in files:
    filename = os.path.basename(file)
    print('File:' + filename)
    img = cv2.imread(file, 0)
    height, width = img.shape
    minlength = height * 0.3
    gap = 5
    judge_img = cv2.bitwise_not(img)

    lines = []
    lines = cv2.HoughLinesP(judge_img, rho=1, theta=np.pi/360, threshold=100, minLineLength=minlength, maxLineGap=gap)

    line_list = []
    for line in lines:
        x1, y1, x2, y2 = line[0]
        /* 傾きを3px以内と判断 */
        if abs(x1 - x2) < 3:
            whiteline = 3
            lineadd_img = cv2.line(judge_img, (line[0][0], line[0][1]), (line[0][2], line[0][3]), (255, 255, 255), whiteline)
            x1 = line[0][0]
            y1 = line[0][1]
            x2 = line[0][2]
            y2 = line[0][3]
            line = (x1, y1, x2, y2)
            line_list.append(line)

    line_list.sort(key=itemgetter(0, 1, 2, 3))

    vt_line = 0
    vt_line_list = []
    x1 = 0
    for line in line_list:
        judge_x1 = line[0]
        if abs(judge_x1 - x1) < 2 and vt_line_list !=[]:
            x1 = judge_x1
        else:
            x1 = judge_x1
            vtl_line = vtl_line + 1
            vt_line_list.append(line)

    print('VT_line:' + str(vtl_line))
    line_list = pd.DataFrame(line_list)
    print('line_list_pd')
    print(line_list)
    plt.imshow(lineadd_img)

※コンフィグ設定(11~13行目)については、以下参照ください。

[https://way2se.ringtrees.com/py_cfg-001/]

縦線を取得する場合はコードは、たったの一行!(29行目)。

今回は、縦線取得が目的ですのですが、今回のような横長の図の場合は少しややこしいです。

ただ、引数を微調整し、後述するふるいにかけることで対応できます。

コツは、(今回の様な横長の図の場合でいえば)minLengthを小さくし、gapを大きくとることです。(24,25行目)
gapがなければ、交差している部分が切れていると認識し、破線と認識されるパターンが多いようです。
引数を調整して感度を確認しながら、微調整してください。

取得した線は、縦線だけでなく、横線も取得する場合がありますが、最終的に垂直な縦線のみ取り扱うため、ふるいにかけています。(31行目~)

垂直な縦線の定義は、x1とx2が同じ値となります。

取り扱う画像によって、多少の傾きが発生しますので、少し余裕を持たせて判断しています。(35行目)

ふるいにかけた線を取得・蓄積し、抽出完了です。(45行目)

上記、ソースを実行した際に結果です。
黄色の部分が線として認識している部分です。

取得した縦線のカウントも行ってみました。
図では判断できない場合も、容易に確認できると思います。(47行目~)

ぜひ、トライして、各パラメータを振って意図通りに取得できるようになりましょう!

 

ABOUT ME
記事URLをコピーしました