質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

2回答

2451閲覧

OpenCVで表の認識で意図しないものが認識される

softhonda

総合スコア14

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

0クリップ

投稿2020/02/18 15:52

以前の質問をもとに表の認識の実験をしております
https://teratail.com/questions/151317

そこで以下のソースで表の枠を認識してみました。

python

1import cv2 2import numpy as np 3 4img = cv2.imread('no300.jpg') 5 6# BGR -> グレースケール 7gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 8# エッジ抽出 (Canny) 9edges = cv2.Canny(gray, 1, 100, apertureSize=3) 10cv2.imwrite('edges.png', edges) 11# 膨張処理 12kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) 13edges = cv2.dilate(edges, kernel) 14cv2.imwrite('edges2.png', edges) 15# 輪郭抽出 16contours,hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 17# 面積でフィルタリング 18rects = [] 19for cnt, hrchy in zip(contours, hierarchy[0]): 20 if cv2.contourArea(cnt) < 2000: 21 continue # 面積が小さいものは除く 22 if hrchy[3] == -1: 23 continue # ルートノードは除く 24 # 輪郭を囲む長方形を計算する。 25 rect = cv2.minAreaRect(cnt) 26 rect_points = cv2.boxPoints(rect).astype(int) 27 rects.append(rect_points) 28 29# x-y 順でソート 30rects = sorted(rects, key=lambda x: (x[0][1], x[0][0])) 31 32# 描画する。 33for i, rect in enumerate(rects): 34 cv2.drawContours(img, rects, i, (0,0,255), 2) 35 36cv2.imwrite('img.png', img)

###入力画像
イメージ説明

###結果画像
イメージ説明

###解決したいこと
住所、氏名、生年月日のあたりに意図しない斜めになった四角の枠が認識されています。
これを認識しない方法はございませんでしょうか。

試したこと

cv2.contourArea(cnt) < 2000:
の部分を調整してみますが、大きくしすぎると欲しいデータまで消えてしまいます。

よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2020/02/19 02:00

斜めの物が出てくる原因の確認くらいはご自身でされるべきではないでしょうか. (やっているならその情報を書いた方が良いし.) 特にこれ系の処理方法の検討中なのであれば,各処理(このコードで言えば,エッジ検出や膨張処理,輪郭抽出等)の結果を可視化しておくくらいのことは必須でしょう. 最終結果だけを見て場当たり的に面積閾値でどうこうするのではなく,まずは各段階の結果が想定と異っていないか等を見るべきです.
softhonda

2020/02/19 03:41

ご指摘ありがとうございます。 各工程、edges.png、edges2.pngで確認し、膨張処理のgetStructuringElementのパラメーターなど変えて試していました。 その結果を受けて面積でフィルタリングしております。 その部分の試したことは記入漏れでした。 途中の工程のpng画像を見ても、その周辺に私の目では輪郭となるような部分があるように思えませんでした。 そこで質問させていただいた次第です。 よろしくお願いします。
fana

2020/02/19 03:57

輪郭形状に対してminAreaRectを使ったら,斜めのやつが出てくるという現象になっている →では,該当の輪郭形状とは一体どうなっているのか? 欲しい枠の形をしているのかいないのか.  →もし,枠の形をしているのにminAreaRectの結果が斜めになるという話なら,minAreaRectはあなたの目的に対しては使えないという話になる.  →変な形の輪郭になっているのなら,その原因は何か?という話になる.   findContoursへの入力画像を見て,その要因を探る.   →本当に変な形が存在するならばそれ→その形ができる原因は…?   →変な形は無いのに何故か変な形のcontourが得られるという話なら,相応のことを考えねばならない.
fana

2020/02/19 04:11 編集

minAreaRectの結果矩形の4つの辺は与えた輪郭の一部に重なっているハズなので,例えばその結果例の斜めになっている結果群のうちの一番右上に位置している横方向にでかいやつ(左側が「殿」を含んでいるやつ)とかは,輪郭自体が変な形に取れてしまっているのでは?と思える.
tiitoi

2020/02/19 05:55

斜めの長方形が抽出された原因はわかりませんが、抽出結果から斜めの輪郭を弾くなら、cv2.minAreaRect() で輪郭に外接する回転した長方形が得られるので、その回転角度で判定すればよいと思います。
softhonda

2020/02/19 07:03

fana様 おそらく輪郭と認識する情報が入っているからと思われますので、エッジ抽出のあたりから考え直す必要があると考えております。 tiitoi様 フィルタリング対象に角度も追加することで少なくとも今回のパターンでの誤認識が減ると思います。 ご回答ありがとうございます。
fana

2020/02/19 08:15

cannyフィルタの後でdilateを入れているのは, (1)単純に短距離のエッジの途切れを埋める目的 の他に, (2)絵の上での太い線に対して(その線の両側に)2重にエッジが出るようなものをくっつける(太い1本にする:= → ━)という役割 があるのかな?等と勝手に推測しますが,このとき,結果としてくっつく部分とくっつかない(隙間がある状態になる)部分とが存在すると,変な輪郭形状が生まれるような気もします. これ,cannyとか必要なんですかね? (この例だけもしれませんが,割と綺麗な絵ですし)2値化して即輪郭抽出とかの方が,やりたいことに対して正直な実装だったりしませんか?
softhonda

2020/02/19 10:32

私が初心者ということもあり、以下をそのまま使わせていただいています https://teratail.com/questions/151317 ただ、2値化しての実験もしてみましたが、 im = cv2.imread('no300.jpg') im_gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) retval, im_bw = cv2.threshold(im_gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU) contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) 個人番号部分の線が繋がらなかったりしました。 そこで以前の質問があったものを見て、やってみたらうまくつながった次第です。
softhonda

2020/02/19 12:30

2値化したものの線が途切れていました。 やり方次第で線が繋がるかもしれません。 線を太くするなどの工夫が必要です。 >>2値化して即輪郭抽出とかの方が,やりたいことに対して正直な実装だったりしませんか? そのように思います。 ありがとうございました。
fana

2020/02/19 12:37

> 2値化したものの線が途切れていました。 2値化の閾値を自分で与えるとか,閾値決定方法を変えるとか.adaptiveThresholdをブロックサイズでかめで(例えば,画像サイズの半分とか1/4とかそういうレベルで)かけてみるとか. あとは(cannyのときと同様に)途切れがちょびっとだけならモルフォロジで塞ぐとか.
guest

回答2

0

ベストアンサー

「追記・修正の依頼」の側にいろいろ書きましたが,
それはそれとして,現状の結果に対してすぐにやれそうなこととしては…

rect = cv2.minAreaRect(cnt)

  • cntの面積とrectの面積

あるいは

  • cntの周長とrectの周長

あたりの差とか比率あたりを見てみたらどうでしょうか.
上手くいっているやつとダメなやつとの間で優位に値の差があるならば,それを利用してダメなやつを棄却できるでしょう.

投稿2020/02/19 04:19

編集2020/02/19 04:20
fana

総合スコア11658

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

softhonda

2020/02/19 10:33

もう少し、私の方で前段階のやり方を考える必要があると思います。 色々と考えをいただきましたので、ベストアンサーとさせていただきます。
fana

2020/02/19 12:23

一応,回答内容の補足: 輪郭形状が四角形(欲しいデータ)のときには,そこから求めたminAreaRectの結果はその輪郭とほぼ重なるような結果になるのだから,周長や面積といった値はほぼ同一になることが期待でき, 何かぐちゃくちゃっとした輪郭(要らないデータ)の場合には,大きく異なる値になると期待できる, …んじゃない? という話.
fana

2020/02/19 12:29

あと,これ系は経験上,前処理次第では,例えば紙の縁のところとかに,めちゃくちゃ細長い領域とかが出てくるとかもあり得ると思う. そういう領域はこの回答の内容を突破してくることもあるし,面積自体も相応の値だったりもするので,そういう場合はまた別の棄却ルールが必要かもしれない(minAreaRectの結果矩形の長軸の長さに最大値制限を設けるとか,縦横比を見るとか).
guest

0

Cannyを使わない方法の方が綺麗にできそうだという仮定のもと進めていきました。
2値化する際のしきい値を調整し、枠線が切れないようにしました。
膨張を大きくしすぎると文字が連続して大きな図形に見えているようでしたので、あまり膨張しないようにしました。
以下のコードにて意図したものになりましたので報告いたします。
本当にありがとうございました。

python

1import cv2 2import numpy as np 3 4im = cv2.imread('no300.jpg') 5im_gray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) 6retval, im_bw = cv2.threshold(im_gray, 220, 255, cv2.THRESH_BINARY_INV) 7cv2.imwrite('result1.png', im_bw) 8 9kernel = np.ones((2,2),np.uint8) 10im_bw = cv2.dilate(im_bw,kernel,iterations = 1) 11cv2.imwrite('result2.png', im_bw) 12 13contours, hierarchy = cv2.findContours(im_bw, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) 14 15rects = [] 16#繰り返し処理で面積の小さいものは除外する 17for cnt, hrchy in zip(contours, hierarchy[0]): 18 if cv2.contourArea(cnt) < 2000: 19 continue # 面積が小さいものは除く 20 if hrchy[3] == -1: 21 continue # ルートノードは除く 22 rect = cv2.minAreaRect(cnt) 23 rect_points = cv2.boxPoints(rect).astype(int) 24 rects.append(rect_points) 25 26im_con = im.copy() 27cv2.drawContours(im_con, rects, -1, (0,0,255), 1) 28cv2.imwrite('result3.png', im_con)

投稿2020/02/20 15:35

softhonda

総合スコア14

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問