« 新型のiPhone13/13 Pro、iPad、iPad mini、Apple Watch 7が発表 | トップページ | iOS 15へのアップグレードをためらってます・・・ »

2021年9月18日 (土)

アナログメーターの画像から針の角度を読み取るやつ作ってみた(OpenCV)

会社でアナログメーターを撮影し、そこからメーターの値を自動で読み取りたい、という相談を受けました。

てことで、そういうものを早速、作ってみました。

なお、世の中にはこういうソリューション(物体検出を使用)もあるそうですが。

「アナログメーター解析AI」を創りました! | | 株式会社プライムキャスト(PRIMECAST)

ここでは敢えて、機械学習を使いません。数字はともかく、メーターの針の角度くらい、画像解析だけで行けるんじゃないか、と。

ということで、OpenCVのハフ変換というのを使います。

ここのサイトを、参考にしました。

 アナログメーター 1(直線検出)|uPyC|note

で、ハフ変換というのは、画像中の直線や円などの図形を読み取る手法、とでも思ってください。

その会社の方の相談で読み取りたいメーターは、ガスボンベのメーターでした。

自宅には手ごろなメーターがないので、ネットのメーカーのサイトやらなんやらやらを探って、テスト画像を手に入れてきました。

Meter2

このメーターの針の角度を、真下を0度として、時計回りに角度を読み取るというものを作りました。

コードは以下。

「meter_read.py」


# cv2.HoughLines() 関数
import cv2
import numpy as np
import statistics

img = cv2.imread('./meter.jpg')
# 画像の大きさを取得
height, width, channels = img.shape[:3]
# 二値化
threshold = 100
ret,img_thresh = cv2.threshold(img, threshold, 255, cv2.THRESH_BINARY)
# エッジ画像へ変換(ハフ変換で直線を求めるため)
edges = cv2.Canny(img_thresh,50,200,apertureSize = 3)
cv2.imwrite('houghlines2.jpg',edges)
cv2.imwrite('houghlines1.jpg',img_thresh)

# 自動的に直線が2本となるパラメータを検出
# minn:何点の点が並んでいたら、直線を引くか?のパラメーター値
for m in range(10,161,1):
    lines = cv2.HoughLines(edges,1,np.pi/180,m)
    if lines is None:
        break
    print(len(lines))

    if len(lines)==2:
        minn = m

print('minn = ', minn)
lines = cv2.HoughLines(edges,1,np.pi/180,minn)

theta_t = [] # 原点から直線に向かって下した法線と、水平線との角度 (ラジアン) を格納する配列
aa = []   # 直線の傾きを格納する配列
bb = []   # 直線の切片を格納する配列

i = 0

for i in range(len(lines)):
   for rho,theta in lines[i]:
        print('rho = ', rho)
        print('theta = ', theta)
        theta_t.append(theta)
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a*rho
        y0 = b*rho
        x1 = int(x0 + 1000*(-b))
        y1 = int(y0 + 1000*(a))
        x2 = int(x0 - 1000*(-b))
        y2 = int(y0 - 1000*(a))
        cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)

        # 2点を通る直線の式は、y = (y2-y1)/(x2-x1)x - (y2-y1)/(x2-x1)x1 + y1
        # 傾き a = (y2-y1)/(x2-x1) 、 b = y1 - (y2-y1)/(x2-x1)x1

        a0 = (y2 - y1) / (x2 - x1)
        b0 = y1 - (y2 - y1) / (x2 - x1)* x1

        aa.append(a0)
        bb.append(b0)

# 針が画像の左上、左下、右上、右下 のどこにいるかを、2直線の交点の位置で判断し、角度の式を変更
# なお、針の中心は画像の中心にあるとして、計算
# 交点の式は、((b[1] - b[0]) / (a[0] - a[1]) , (a[0] * b[1] - b[0] * a[1]) / (a[0] - a[1]) )

x_t = (bb[1] - bb[0]) / (aa[0] - aa[1])
y_t = (aa[0] * bb[1] - bb[0] * aa[1]) / (aa[0] - aa[1])

if x_t < width/2# 針が左上か左下にいるとき
    theta_hor = statistics.mean(theta_t)*180/np.pi
else# 針が右上か右下にいるとき
    theta_hor = 270 - (90 - statistics.mean(theta_t)*180/np.pi)

print(theta_hor)

cv2.imwrite('meter_line.jpg',img)

必要なライブラリは、たった2つ。OpenCV、Numpyです。

> pip install opencv-python numpy

で、使い方は、上のメーターの画像(ここではmeter.jpg)を同じフォルダ内に置いて、Windows PowerShellなどで

> python meter_read.py

と打ち込むだけ。

先のリンクではグレースケールの画像からエッジ処理をかけてましたが、ここでは二値化した画像からエッジを読み込ませてます。

その方が、安定していたので。

Houghlines2

で、ここから針の部分の直線の実を読み出すために、21行目の「for m in range(10,161,1):」以降の繰り返し分で最適なパラメータ値を決めています。

具体的には、2本の直線を抽出できた時のパラメータ値のみを探り出します。

で、ハフ変換を実行。

Meter2line

すると、こんな感じの直線が得られます。

ちなみに、この時の針の角度は、この2本の線の中間の角度から割り出してます。

この場合は約78度と返ってきました。真下が0度なので……いい感じに読み取れてます。

なお、針の角度を出すのには、ちょっと工夫が必要です。

実は針が左半分を向いているときと、右半分の方向を向いているかで、角度の式を変えないといけません。

具体的には、左下を向いているか、右上を向いているかが、このハフ変換から得られた直線の角度だけでは判定できないんです。

そこで、得られた2本の直線の交点が、右か左かどっちにあるかで、その式を分けるように工夫してます。

詳しくは、OpenCVのハフ変換のリファレンス、および上のコードをご覧ください。

なお、本当に針の向きが変わっても読み取れるのかが心配だったため、画像を回して確認してみました。

Meter23

例えばこの向きでも、

Meter23line

こういう画像が出力されて、角度は「348度」と返ってきます。

うん、いい感じですね。

メーターごとに角度→目盛り値の変換係数を定義してやれば、物体検出など無しに使えます。

それこそ、Raspberry Piでも動かせるほど軽いので、Raspberry Pi Zero + カメラだけで運用できます。

なお、針の向きが右か左かを判定する際に、画像の中心から右か左かで判定させてますが、本当ならこのメーターの中心点から見て右か左かを見ないといけないのですが、そこはさぼってますね。

本来なら、ハフ変換で円を抽出させ、そこから中心点を探すというのを入れた方がより精度が上がります。

この辺が、参考になります。

アナログメーター 2(円を検出)|uPyC|note

最近ですが、何でもかんでも物体検出、画像認識を使うよりも、まずこの手の画像処理を試すことから始めた方が良いことが多いです。

事前処理をした上で画像認識などを使う方が、認識率が上がることも多いですし。

もし仕事で、自動でアナログメーターを読ませたくなったら、ぜひご参考になさって下さい。


Raspberry Pi Zero W スターターセット (USB小型電源, 高速型32GB MicroSD, USBスイッチケーブル, ケース, HDMIケーブル, MicroUSB変換アダプター付き)

« 新型のiPhone13/13 Pro、iPad、iPad mini、Apple Watch 7が発表 | トップページ | iOS 15へのアップグレードをためらってます・・・ »

数値解析系」カテゴリの記事

コメント

すごいですね!
書かれていることは難しそうですが、実際に目に見えて役立っているのが
面白そうだと感じました。

困っていることを解決する手段のひとつとしての技術(プログラミングや
画像処理)を学ぶのは面白いですが、目的がないまま学んでも続かない
のは当たり前だなと改めて感じました。

灘高校の武藤氏はプログラミングを実際に役立てている(ぜんそくで呼吸困難
になっている子がスマホのボタン1つで症状やヘルプできるアプリ、リモートで
受験生同士がわからない部分を教え合うアプリ)のを思い出しました。

> souさん

ありがとうございます。最近、社内でこんなことばっかりやってます。役に立つような、立たないような……
ただ、Excelをpythonで処理するやつを社内で広めたら、意外なほど広まりました。マクロ使いほど食いついてきたんですが、実はマクロよりもpythonの方が速かったりします。役に立つと言うことは、その人の心に火を灯すだけの何か理由がある、と言うことなのですよね。灘高の先生の逸話を聞いて、そう思います。

コメントを書く

(ウェブ上には掲載しません)

« 新型のiPhone13/13 Pro、iPad、iPad mini、Apple Watch 7が発表 | トップページ | iOS 15へのアップグレードをためらってます・・・ »

当ブログ内検索

スポンサード リンク

ブログ村

無料ブログはココログ