記事一覧表示

書類仕分けを自動化したい

概要

バイト先の先輩が「毎年やっている書類仕分けが面倒臭いから自動化って出来ない?」って言ってきたのでちょっと考えてみた。

問題設定

書類を見てみると、特定の質問に対して答える欄があり、その答え(’有’ か ‘無’ のどちらかに丸を付けている)を判別すれば良いらしい。具体的な例は下の通り。

f:id:ooutimatuki:20180528235505p:plain

じゃあ、’無’ の辺りを上手く切り取った画像に丸があるかを判別すれば良いと思ったので、調べてみたら、OpenCV円検出の関数 cv2.HoughCircles が用意されているらしく、それを使ってみた。

やった事


- 画像の切り取り

まず、’無’ の辺りを上手く切るために、画像から任意の箇所のpixelを指定して切り取るプログラムを作った。
参考にしたのはここ

# coding: UTF-8
import cv2
import numpy as np
import sys

def cut(filepath):
    #画像を取得
    img = cv2.imread(filepath)

    #切り取り画像の左上のpixel
    x, y = 745, 4070

    #切り取り画像の幅と高さのpixel数
    w = 400
    h = 120

    #画像から切り取り実行
    roi = img[y:y+h, x:x+w]

    #切り取った画像をウィンドウで出力。
    cv2.imshow('detected circles',roi)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

if __name__ == "__main__":
    argvs = sys.argv

    if (len(argvs) <= 1):
      print('Usage: # python %s TestDataFilePath' % argvs[0])
      quit()
    cut(argvs[1])

結果はこんな感じ。

f:id:ooutimatuki:20180528235656j:plain


- 丸検出 cv2.HoughCircles に適切なパラメータを設定

丸検出 cv2.HoughCircles がどの程度の精度か分からなかったので、ひとまず、’無’ に丸が付いていない画像(’無’ のみの画像)で丸が検出されないギリギリのパラメータを振ろうと考えた。
こうしておけば、’無’ に丸が検出された結果の中に、’無’ に丸が付いていない画像(’無’ のみの画像)がなくなり、考慮するべき画像が、’無’ に丸が検出されなかった結果に絞られると考えたからである。
丸検出 cv2.HoughCircles のパラメータの意味はここを参照。
丸検出 cv2.HoughCircles のプログラムは以下の通り。

# coding: UTF-8
import cv2
import numpy as np
import sys

argvs = sys.argv

if (len(argvs) <= 1):
  print('Usage: # python %s TestDataFilePath' % argvs[0])
  quit()

#画像をグレースケール(,0)で取得。
img = cv2.imread(argvs[1],0)
#メディアンフィルタを適用。
img = cv2.medianBlur(img,3)
#色配列をグレースケールからBGR(OpenCV規格)へ変換。
cimg = cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)

#cv2.HoughCirclesで丸検出。
circles = cv2.HoughCircles(img,cv2.HOUGH_GRADIENT,1,20,
                            param1=22,param2=27,minRadius=10,maxRadius=0)

print(type(circles))
#丸が検出できた場合の処理。
if type(circles) is not type(None):
  circles = np.uint16(np.around(circles))
  #画像に丸を描写。
  for i in circles[0,:]:
      #丸の曲線を緑色で描写。
      cv2.circle(cimg,(i[0],i[1]),i[2],(0,255,0),2)
      #丸の中心点を赤色で描写。
      cv2.circle(cimg,(i[0],i[1]),2,(0,0,255),3)

  #結果をウィンドウで出力。
  cv2.imshow('detected circles',cimg)
  cv2.waitKey(0)
  cv2.destroyAllWindows()
メディアンフィルタ:変換するpixelを中心とした正方形(今回使ったのは縦横3pixelずつ)の範囲のpixelの中央値(数値を大きい順に並べた時に真ん中に来る数値)を求め、それを変換後のpixel値とする手法。こうする事によって、画像のエッジはそのままにノイズを減らす事が出来る。…アニメーションで説明したいね^_^;

プログラムの結果はこんな感じ。

f:id:ooutimatuki:20180529002129p:plain

結果

テストデータは以下の通り。

‘無’ に丸が付いている書類:
289
‘無’ に丸が付いていない書類:
35

そして、解析結果は以下の通り。

‘無’ に丸が検出された書類:
264 = 0[’無’ に丸が付いていない書類(誤検出)] + 264[‘無’ に丸が付いている書類]
‘無’ に丸が検出されなかった書類:
60 = 35[’無’ に丸が付いていない書類] + 25[‘無’ に丸が付いている書類(誤検出)]

狙っていた通り、丸が検出された書類には、丸が付いていない書類は無かった。
これで、仕分けするべき書類は丸が検出されなかった書類だけになる。
丸が付いていたにも関わらず、その丸が「丸として認識されなかった」書類が25だけだったのは、割りと良い結果だった。

考察

実際に丸が検出されなかった書類を見てみると、

f:id:ooutimatuki:20180528235138j:plain

変に細長かったり、

f:id:ooutimatuki:20180528235141j:plain

曲線っぽい所が無かったりする(曲がり具合が弱い、急な角度で曲がり、ほとんど楕円っぽく見える)と上手く認識されなかった。
これを解決するには、
丸と判定する時の丸っぽさの閾値cv2.HoughCircles の第6引数の数値)を下げたり、
丸を判断する為のエッジの量を増やす( cv2.HoughCircles の第5引数の数値を下げる)
等が考えられるが、これ以上弄ると ’無’ のみの画像で丸が付いていると判定されてしまう…
こんな感じで…。

f:id:ooutimatuki:20180528235144j:plain

いや、そもそも、画像内の下と左に見える黒枠を消せば良いんだけど、渡されたデータが、紙の書類を大量にコピー機で自動スキャンしてるらしく、30~50pixel位、縦横にずれているんだよな。
まあ、このブログを書いている時に、パターンマッチングすれば消せるんじゃんって思いついたから、後で書こうか。
と言うか、この書類を仕分けするのに、丸検出とは全く別の方法も思いついたので、それも後日で。