Open amay077 opened 4 years ago
scrapingOccurrenceStatus.pyと比較しての話になりますが 各値を個別にピクセル指定していない点や、OCR結果を検算している点など とても良さように見えます。 ただし、画像サイズや縮小率など過去いろいろとあったのに加え、 そもそも合計値が合わない結果が公表されたりしたことも考慮すると OCRに任せきりにすることは難しいのではないかと考えますがいかがでしょうか? プルリクエストをマージする時点で正しい値かどうかを検証することを運用ルールにするればいいかな?
OCRに任せきりにすることは難しいのではないかと考えます
これは同意です。
理想は以下の形です。
OCR が期待通り動作していない事が確認できたら、これまで通り手動でスプレッドシートに記入して、運用が回せます。
ただ、「2. Google スプレッドシートに新規行を追加する」は、難易度が高い(面倒な)ので、スプレッドシートの代わりに covid19 repo の main_summary_history.csv を手動メンテの対象とするのが良いと考えます。
以下、その流れです。
運用面で代わるのは、
です。
Google スプレッドシートも生かすのもアリですが、スプレッドシートからの差分更新を行う必要も出てくるので使用や仕組みが煩雑になるかなと思います。
今日のデータは失敗しているようなのでBGR調整をループ回して実験しているのでもう少しお待ちください。
画像調整しながら同じデータがn回以上でれば採用という形でもいいかもしれません。
(既に同日の行がある場合は追加しない)
同日同時刻時点として修正データが発表されます。 追加はしないけど更新はするという意味で大丈夫でしょうか?
あとは現時点で取得できていない備考についてです。 特に再感染者数は変更されていくでしょうから取得が必要だと考えますがいかがでしょうか? 前回値を引き継いで変更があったら手動でCSVを編集するって方法もありますけど。
(既に同日の行がある場合は追加しない)
同日同時刻時点として修正データが発表されます。 追加はしないけど更新はするという意味で大丈夫でしょうか?
既に同日の行がある場合、追加も更新もしない、の意でした。 理由は、「OCRによる自動取得よりも、CSV の手動追記を優先としたい」ためです。
が、「同日同時刻時点として修正データが発表される」は考慮できてなかったので、再考が必要ですね。
再考案としては、
あたりでしょうか。 いずれも一長一短あって悩ましいので、実装が楽な 2 ですかねえ。
特に再感染者数は変更されていくでしょうから取得が必要だと考えますがいかがでしょうか? 前回値を引き継いで変更があったら手動でCSVを編集するって方法もありますけど。
備考も取りたいところですが、実装難易度と精度次第ですかね。 現実的には、「前回値を引き継いで…」な気もします。
@amay077 さんはご存じかとは思いますが scrapingOccurrenceStatus.pyを作っていただいていた時の落ちとしては main_summaryの項目数などを考慮すると 目視で精査したり修正が必要な程度のOCRをブラッシュアップするよりは 愛知県がオープンデータを公開してくれるまで手動で入力したほうがいいですね ということになったんですよね。
そうですね、当時の結論を翻意する理由としては、
ですかね。
もちろん「愛知県がオープンデータを公開してくれる」「スクレイピングしやすい形式で公開してくれる」ことが望ましいので、 @imabari さんも、その点ご留意いただきつつ、可能な限りでご協力いただけたら幸いです(特に急がないです)。
調整を行いましたあくまでも補助だと思っています使えるようだったらご利用ください https://github.com/imabari/covid19-data/blob/master/aichi/aichi_ocr.ipynb
目視後GitHub Actions で手動トリガーで取り込むようにするのはどうなのでしょうか? https://qiita.com/proudust/items/51599abd2b107b708e1e
https://github.com/code4nagoya/covid19-aichi-tools/issues/44 で imabari さんの OCR スクリプトを組み込んでみました。
OCRに任せきりにすることは難しいのではないかと考えますがいかがでしょうか?
については改めて、以下のような戦略でいかがでしょうか?
こうしておけば、OCR が正しくないとしても、Google スプレッドシートにその日のデータを追加することで、それが採用されます。
https://github.com/code4nagoya/covid19-aichi-tools/issues/44 は、上記の「3. 」までを実装したものです。 この流れで問題なさそうなら、4~ も実装しようと思います。
ご検討ありがとうございます。
C と D をマージする → E とする(D が C に既存の場合、その日の行を上書きする)
D(スプレッドシート)は、B(OCR)やA(前回csv)を上書きしたい際に、上書きしたい日の行を追加して記載するのですよね。例えば8/1-19はBの値で対応していて、8/20の備考が変更になった場合、Dに8/1-19の行は追加せず、8/20の行のみ追加して記載(備考だけでなく、全項目)するのですね。
翌8/21はDに記載せず、最新8/21のBと、備考のみはAの前日値(8/20)をCの8/21に採用し、以後その備考が引き継がれていく事になるのですね。
例えば8/1-19はBの値で対応していて、8/20の備考が変更になった場合、Dに8/1-19の行は追加せず、8/20の行のみ追加して記載(備考だけでなく、全項目)するのですね。 翌8/21はDに記載せず、最新8/21のBと、備考のみはAの前日値(8/20)をCの8/21に採用し、以後その備考が引き継がれていく事になるのですね。
はい、そのとおりです。 D(スプレッドシート)は、手動で上書きしたい日の行のみ(列は全項目)を用意します。
理解しました。全体的な流れについて賛同します。
OCR精度にもよるのでしょうが、Bに取り込む際に、チェック機能(行単位の和チェック[累計陽性者数と入院])を設けてはいかがでしょうか。チェック結果が不整合ならエラーか警告を出して、D(スプレッドシート)での修正を促します。プレビューの確認は残るようですので、その手助けにもなると考えます。
PR をマージして GitHub Actions で動作させてみました。
https://github.com/code4nagoya/covid19/pull/874/files
https://github.com/code4nagoya/covid19-aichi-tools/issues/44#issuecomment-674996873 の
です。 現在の画像が「(注) 検査実施人数には…について掲」で切れているので、そこだけ文として不完全になってます。
ありがとうございます。数値は問題なく取り込めていますね。素晴らしいです。注記は「また、再感7人については、含めていない。」で再感 "染" が抜けていますが問題無いレベルと考えます。
まず当面はこのままOCR結果のテストを続けるのでしょうか。8/14から画像の解像度が良くなっており、担当者が代わって画質が落ちる可能性はあります。実際、検査陽性者の状況がこの形となった8/12と8/13は低解像度でした。過去には改ページプレビュー状態の画像もありました。
あと注記は「OCRに任せる」方針ですか?それとも「A(前回csv)の前日値を引き継ぐ」方針ですか?検査実施件数は現在掲載していませんし、中国人渡航者はもう出ないでしょうし、となると修正する必要があるのは再感染者数だけで更新頻度は少なくD(スプレッドシート)での修正で十分で「前回csvの前日値を引き継ぐ」でも良いと考えています。
これはアプリ側に記載する内容かもしれませんが「現在陽性者数」の定義の注記も必要です。他アプリ含め「注記はスプレッドシートから」というのは便利なのかもしれませんね。
チェック機能(行単位の和チェック[累計陽性者数と入院])を設けてはいかがでしょうか。チェック結果が不整合ならエラーか警告を出して
チェック機能は入れましょう。 ただ、現状、警告をわかりやすく通知しつつ処理を継続させる方法が未確立なため、当面は以下のようにしようと思います。
つまり、OCR がスキップされてそれ以外の処理は正常終了します。 「OCRが正常に機能したか?」は、UPDATE DATA のプルリクの内容に B(main_summary_recognized.csv ) が存在するかで判断します。 B が存在しなければ、Google スプレッドシート に当日のデータを記入し、再度定時処理を手動で re-run させます。
まず当面はこのままOCR結果のテストを続けるのでしょうか。
上記の 1~7 までの流れは、とりあえず本番の運用に乗せて、 OCR結果のテストは運用しながら行おうかと思います。 前述の通り OCR が失敗しても処理は続行され、代替としてこれまで通りの Google スプレッドシート 入力が使えるので、支障は無いと思います。
あと注記は「OCRに任せる」方針ですか?それとも「A(前回csv)の前日値を引き継ぐ」方針ですか?検査実施件数は現在掲載していませんし、中国人渡航者はもう出ないでしょうし、となると修正する必要があるのは再感染者数だけで更新頻度は少なくD(スプレッドシート)での修正で十分で「前回csvの前日値を引き継ぐ」でも良いと考えています。
注記も、上記の 1~7 の流れに従います。 なので、通常は「OCRに任せる」で、OCRの問題に気づいた時のみ、スプレッドシートの備考列に記述をします。 記述内容は画像に描かれている事を忠実に再現しなくてもよいと思います。
「スプレッドシート入力停止」=「OCR本番移行」なのですね。現時点で未対応のアプリが有り、当方のスプレッドシート入力が間に合わなかった場合(県HPの更新が10時直前で当方のスプレッドシート入力は間に合わず、OCRには間に合うケースもありえます)も本番移行となってしまうのが怖いですが、反映前に確認いただいているので、了解です。
県HPの注記は「OCRに任せる」で了解しました。当サイトとしての注記はアプリ側に記載ですね。
認識一致しているかと思いますが、チェック機能(行単位の和チェック)は以下2点の認識です。 1.陽性患者数=入院+入院調整+施設入所+自宅療養+調整+退院+死亡 2.入院=軽症無症状+中等症+重症 検査実施人数だけ和チェックに入れられません。
8/18 20:00は「入院」セルがOCR時に読み飛ばされたのか、以降の右側が全て1セルずつ左にズレて、右端の「死亡」3320は次の行先頭の「陽性者数」が入ってますね。「入院」セルの左上にExcelでのエラー表示が有るのがOCRに影響しているのでしょうか。
エラー表示が有るのがOCRに影響しているのでしょうか
その可能性はあると思います。
https://github.com/code4nagoya/covid19-aichi-tools/runs/1002200014?check_suite_focus=true#step:3:594
で、OCR直後のテキストが見られますが、入院 の枠内だけ飛ばされてますね。 これは推測ですが、画像内の矩形を認識して、その中のテキストを抽出しているのではないか、 そのためエラーの緑三角があると、矩形認識に失敗して、テキスト抽出できていないのではないかと思われます。
担当によりExcelの設定が異なるのか、作業手順が異なるのか分かりませんが、Excelのエラー表示は結構な頻度で表示されますし、同じファイルを使いまわしているならエラーを無視とかしない限りずっと出ますね。
少し遡ってみましたが、8/5のクラスター情報の入院等でエラー表示が付いてました。
このパターン(読み込めずに飛ばされる)はチェック機能でひっかかるのでOCR結果は接続はされませんが、本問題が解消出来ないとスプレッドシートへの入力回数が増えてしまいますね。
認識一致しているかと思いますが、チェック機能(行単位の和チェック)は以下2点の認識です。 1.陽性患者数=入院+入院調整+施設入所+自宅療養+調整+退院+死亡 2.入院=軽症無症状+中等症+重症 検査実施人数だけ和チェックに入れられません。
はい、認識あっています。 https://github.com/code4nagoya/covid19-aichi-tools/pull/63/files#diff-0360629f98b5c76df7fa16b2108e6d8aR69-R74 のあたりで実装してます。
今回の OCR 認識不良も、このチェックで引っかかって OCR結果CSV は出力されず、前回CSV+スプレッドシート が採用されたでしょう。
このパターン(読み込めずに飛ばされる)はチェック機能でひっかかるのでOCR結果は接続はされませんが、本問題が解消出来ないとスプレッドシートへの入力回数が増えてしまいますね。
そうですね、県職員さん頼むよ…という気持ちですが、 頻発するようなら、OCR 側で何らかの対応ができるか検討したいと思います。
「スプレッドシート入力停止」=「OCR本番移行」なのですね。現時点で未対応のアプリが有り、当方のスプレッドシート入力が間に合わなかった場合(県HPの更新が10時直前で当方のスプレッドシート入力は間に合わず、OCRには間に合うケースもありえます)も本番移行となってしまうのが怖いですが、反映前に確認いただいているので、了解です。
OCR結果の CSV は、現状と互換性を維持するようにしますので、「現時点で未対応のアプリ」は無くなります。 残りの不安は、
等でしょうか、クリティカルなサイトではないのでエイヤで恐る恐る開始してみようかと思います。
解析モード(PSM)を変更してみた ら、EXCELエラーセルもテキスト抽出できるようになりました。 「xx人」の文字列を収集しているので、ゴミ文字列が混入しても問題ないです。
検査実施|陽性者数| 入院 | 軽症・ 入院 施設 自宅
人数※1 | ※2 無症状 | 中等症| 重症 調整 入所 療状 調整 |退院等| 死亡
41111人| 3896人 「 335人 | 240人 | 79人 16人 30人 53人 | 712人 | 57人 |2661人| 48人
img = cv2.inRange(src, (150, 120, 130), (255, 255, 255)) txt = pytesseract.image_to_string(img_crop, lang="jpn", config="--psm 3").replace(".", "").replace(",", "")
こちらの方が認識がよさそうです
psm 11 Sparse text: 不特定の順序でできるだけ多くのテキストを探す
とのことで、psm11 は確かに文字の認識精度は高かったですが、「不特定の順序で」とあるので、表1行の順序が保証されるのか不安ですかね。
psm 3 でも EXCEL セルエラーの問題は解消できましたので https://github.com/code4nagoya/covid19-aichi-tools/issues/44#issuecomment-676895717 を採用させていただきました。
画像の認識精度は必要十分を確保できそうです。 OCR の結果は、現状の出力と同じ仕様であり Webアプリに影響はあたえない(つもり)なので、 ここらあたりで https://github.com/code4nagoya/covid19-aichi-tools/pull/63 をマージして、運用しながら確認してみます。
こちらのOCR SPACEのFREEのAPIを利用はできないでしょうか? 昨日から試していますがかなりきれいに取り込めます https://ocr.space/
def ocr_space_url(url, overlay=False, api_key="xxxxxxxxxx"", language="eng"):
payload = {
"url": url,
"isOverlayRequired": overlay,
"apikey": api_key,
"language": language,
}
r = requests.post("https://api.ocr.space/parse/image", data=payload,)
return r.json()
test_url = ocr_space_url(url="https://www.pref.aichi.jp/uploaded/image/242724.jpg", language="jpn")
print(test_url['ParsedResults'][0]['ParsedText'])
結果
0検査陽性者の状況 2020年8月19日20時現在 検査実施陽性者数 入院 軽症・ 入院 施設 自宅 人数※1※2 無症状 中等症 重症 調整 入所 療養 調整 退院等 死亡 41,111人3,896人 335人 240人 79人 16人 30人 53人 712人 57人 2,661人 48人
スクレイピングからOCRまでのサンプルです https://imabari.hateblo.jp/entry/2020/08/21/091155
情報ありがとうございます。 確かに精度が高いですね。 https://ocr.space/OCRAPI を見ると Free プランでもこのサイトでは必要十分な利用制限のようです。
現状は、Tesseract で必要な情報抽出ができているので、当面はこれで運用してみたいと思います。 何か課題が生じたら OCR SPACE も検討したいとおもいます。
私も https://ocr.hblab.ai/ というのを試していましたが、Tesseract や OCR SPACE の方が期待した結果が得られました。
そこまでコード数が増えないので両方実行してチェックするのはどうですか?
データ追加について https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.update.html unionで追加分のindexを追加してから上書き
def df_update(df1, df2):
df = df1.reindex(df1.index.union(df2.index))
df.update(df2)
return df
8/22 の画像は OCR 失敗しました。 https://github.com/code4nagoya/covid19-aichi-tools/runs/1017879032?check_suite_focus=true
左:8/21、右:8/22 の画像とその情報ですが、
あたりが主な違いと見られます。
結構悲惨な認識結果になってます(汗
講和数| 入院 | 科- 入院 | 放設 | 邊
数1 天2 押症状 | 中等症| 重定 | 油尺| 入所 | 矢務| 避束|骨院幸| 到選
[95669人 197和| 356人 | 212人 | 118A | 26A | z3A | 59A | 549A | 69A |2ge6A| 54和人
こちらの方が精度は高いですが、「35石人」の誤認など、自動で成功といえるまでではないようです。
****** Result for Image/Page 1 ******
0検査陽性者の状況
2020年8月22日20物現在
検査実施陽性者数入院
軽症・
入院
施設
自宅
無症状
中等症
重症
調整
入所
療養
調整
退院等
死亡
人数※1※2
45,669人4,137人35石人
212人
11g人
26人
23人
59人
510人
69人
2966人
54人
7月~
性者入院
軽症
無症状
中等症
重症
調整
入所
療養
調整
退院等
死亡
3,618人356人
212人
118人
26人
23人
59人
610人
69人
2481人
20人
※1検査実施人数については、発表時点での把振数。なお、検査件数は、5み2件。
2陽性者数については、中国人航者2人を除くらまた、再感染7人については、含めていない
(注)検査実施人数には県内において疑い例または患者の澱厚接触者として検査を行ったものについて掲
昨日の画像がないので確かめれないのですが今日の分は とりあえず画像拡大して以下の設定で数値部分は取得できました
src = cv2.imread(str(jpg_path))
height = src.shape[0]
width = src.shape[1]
tmp = cv2.resize(src, (int(width * 2), int(height * 2)))
img = cv2.inRange(tmp, (145, 125, 110), (255, 255, 255))
こちらの線画抽出を利用 https://qiita.com/pashango2/items/145d858eff3c505c100a#%E7%B7%9A%E7%94%BB%E6%8A%BD%E5%87%BA
8/21の画像だとかなり鮮明に取り込めます
from PIL import Image, ImageChops, ImageOps, ImageFilter
import pytesseract
jpg_path = get_file(link)
img = Image.open(jpg_path)
gray = img.convert("L")
gray2 = gray.filter(ImageFilter.MaxFilter(5))
senga_inv = ImageChops.difference(gray, gray2)
senga = ImageOps.invert(senga_inv)
img_width, img_height = senga.size
img_crop = senga.crop((0, 0, img_width, img_height / 2.5))
img_2x = img_crop.resize((int(img_crop.width * 2), int(img_crop.height * 2)))
img_2xb = img_2x.point(lambda x: 0 if x < 150 else x)
img_2xb
txt = pytesseract.image_to_string(img_2xb, lang="jpn", config="--psm 6").replace(".", "").replace(",", "")
print(txt)
@imabari さん、すごい、ありがとうございます。 試してみます。
ちなみに、
昨日の画像がないので
は、GitHub Actions のログ↓ から
https://github.com/code4nagoya/covid19-aichi-tools/runs/1017879032?check_suite_focus=true
Artifacts の zip から取り出せます。
両方確認しましたがpsmは6より3の方が結果がよさそうです
txt = pytesseract.image_to_string(img_2xb, lang="jpn", config="--psm 3").replace(".", "").replace(",", "").replace(" ", "")
いくつかのパターンで実行して、最初に妥当性チェックが OK になった結果を採用するようにした方が良さそうですね。
8/24付けの画像も OCR は NG でした。
https://github.com/code4nagoya/covid19-aichi-tools/runs/1024328186?check_suite_focus=true
検査実施人数以外はあっていると思うのですが 147113, 4230, 361, 224, 115, 22, 24, 63, 532, 43, 3150, 57 検査実施人数を別のところで確認できないのでしょうか?
縦線を誤認識しないように調整するのは難しそう 縦線だけでも削除できたらいいのですが
検査実施人数を別のところで確認できないのでしょうか?
https://www.pref.aichi.jp/site/covid19-aichi/kansensya-kensa.html に 「※【参考】疑い例または患者の濃厚接触者として検査実施した人数は計47,890人。」 と載るのですが、画像と公開日がズレるので、できたら画像から取得したいところです。
lang="eng", config="--psm 3"
で OCR 実行46351A 4187 | 362A | 215A | 121A | 26A | 28A | 644A | 605A | SOA 3021A 57TA
「50人」 が 「SOA」になってる以外は OK.
さらにこの画像を https://ocr.space/ で language=English, Use OCR Engine2 で実行。
****** Result for Image/Page 1 ******
46,351A 4,187A 362A 215A 121A 26A 28A 64A 605A 50A 3.021A 57A
これは良さそう。 加工を python で行って API に投げるパターンも良さそうです。
昨日こちらの枠線を消すのを試していたのですがあまりいい結果にはなりませんでした https://qiita.com/tifa2chan/items/d2b6c476d9f527785414
47i13人14230人|361人|224人|115人|22人。24人3人532人43人3450人|57人|
gray3 = copy.deepcopy(contour)
edges = cv2.Canny(contour, 100, 200, apertureSize=3) minLineLength = 30 maxLineGap = 25 lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 60, minLineLength, maxLineGap) for line in lines: x1, y1, x2, y2 = line[0] cv2.line(gray3, (x1, y1), (x2, y2), (255,255, 255), 3)
cv2_imshow(gray3)
毎日今日ぐらいの解像度なら大丈夫なんだけどな 画像サイズ確認してはじいた方がはやいのかも
2020年8月25日18時現在
〇検査陽性者の状況 検査実施|陽性者数|入院|軽症・入院施設自宅 人数※1|※2無症状|中等症|重症調整入所|療養調整|退院等|死亡 47890人|4273人|369人|232人|116人|21人22人57人|395人|43人|3329人|58人
画像を作る人によって解像度が変わるみたいなので、、、高解像度を維持してほしいですね。
https://github.com/code4nagoya/covid19-aichi-tools/pull/65
で、複数の認識処理を、成功するまで繰り返すようにしました。
です。 また、更新日付、数値群、注記 の認識を分けたので、それぞれで複数パターンの認識処理をはめ込むことができます。
精度の低い画像では顕著ですが、 数値群は 検査実施人数 以外は Validation できるのでまだ成否の判定がしやすいが、 注記はまともな文章とならず諦めたほうが良いかもしれません。
OCR SPACE などの API に投げるパターンも追加して、更に精度をあげることは可能とは思います。
下のプログラムで白黒化した画像がいまのところ一番きれいなのでこちらの画像ファイルを利用に変更してみてもらえますか?
from PIL import Image, ImageChops, ImageOps, ImageFilter
img = Image.open(jpg_path)
gray = img.convert("L")
gray2 = gray.filter(ImageFilter.MaxFilter(5))
senga_inv = ImageChops.difference(gray, gray2)
senga = ImageOps.invert(senga_inv)
img_2x = senga.resize((int(senga.width * 2), int(senga.height * 2)))
# 濃くする
img_2xb = img_2x.point(lambda x: 0 if x < 150 else x)
img_2xb.save("main.png")
cv2だとこちらで
neiborhood24 = np.array(
[
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
[1, 1, 1, 1, 1],
],
np.uint8,
)
gray = cv2.imread(str(jpg_path), 0)
height = gray.shape[0]
width = gray.shape[1]
dilated = cv2.dilate(gray, neiborhood24, iterations=1)
diff = cv2.absdiff(dilated, gray)
contour = 255 - diff
gray2 = cv2.resize(contour, (int(width * 2), int(height * 2)))
th, im_th = cv2.threshold(gray2, 180, 255, cv2.THRESH_BINARY)
cv2.imwrite("main.png", im_th)
OpenCVでの表のセルの認識方法 https://teratail.com/questions/151317
親の1番最初の表を抽出して下部23%を切り出しでリサイズしなくても大丈夫そう
img = cv2.imread(str(jpg_path))
# BGR -> グレースケール
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# エッジ抽出 (Canny)
edges = cv2.Canny(gray, 1, 100, apertureSize=3)
cv2.imwrite("edges.png", edges)
# 膨張処理
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
edges = cv2.dilate(edges, kernel)
# 輪郭抽出
contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 面積でフィルタリング
rects = []
for cnt, hrchy in zip(contours, hierarchy[0]):
if cv2.contourArea(cnt) < 3000:
continue # 面積が小さいものは除く
if hrchy[3] == -1:
# 輪郭を囲む長方形を計算する。
rect = cv2.minAreaRect(cnt)
rect_points = cv2.boxPoints(rect).astype(int)
rects.append(rect_points)
# x-y 順でソート
rects = sorted(rects, key=lambda x: (x[0][1], x[0][0]))
# senga
kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(5,5))
dilated = cv2.dilate(gray, kernel, iterations=1)
diff = cv2.absdiff(dilated, gray)
contour = cv2.bitwise_not(diff)
rect = rects[0]
# 座標
x_max, y_max = np.amax(rect.T, axis=1)
x_min, y_min = np.amin(rect.T, axis=1)
# print(i, y_min, y_max, x_min, x_max)
y_crop = int((y_max - y_min) * 0.23)
# 切り出し
dst = contour[y_max - y_crop : y_max - 3, x_min + 3 : x_max - 3]
cv2.imwrite(f"yousei.png", dst)
# 縦線消去
edges = cv2.Canny(dst, 100, 200, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 2, 15)
for line in lines:
for rho, theta in line:
if theta == 0:
a = np.cos(theta)
# b = np.sin(theta)
x0 = int(a * rho)
x1, x2 = x0, x0
y1, y2 = 100, -100
cv2.line(dst, (x1, y1), (x2, y2), (255, 255, 255), 3)
txt = pytesseract.image_to_string(dst, lang="eng", config="--psm 3").replace(".", "").replace(",", "")
print(txt)
data = list(map(int, re.findall("\d+", txt)))
print(data)
別の方式試してたらこちらの方がよかったので
文字抽出を利用して日付と表を抽出 https://stackoverflow.com/questions/23506105/extracting-text-opencv
import cv2
import numpy as np
# 画像枠対策に周囲2ドット削除
gray = cv2.imread(str(jpg_path), cv2.IMREAD_GRAYSCALE)[2:-2, 2:-2]
# 線画抽出
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
dilated = cv2.dilate(gray, kernel, iterations=1)
diff = cv2.absdiff(dilated, gray)
contour = cv2.bitwise_not(diff).copy()
# ノイズのグレー除去
contour[contour > 200] = 255
# 画像を2倍に拡大
img = cv2.resize(contour, None, fx=2, fy=2)
# 楕円形カーネル
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
# モルフォロジー勾配(物体の境界線)
grad = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
# 二値化
_, bw = cv2.threshold(grad, 0.0, 255.0, cv2.THRESH_BINARY | cv2.THRESH_OTSU)
# 矩形カーネル
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 1))
# ノイズ除去
connected = cv2.morphologyEx(bw, cv2.MORPH_CLOSE, kernel)
# cv2.RETR_EXTERNAL 輪郭
# cv2.CHAIN_APPROX_NONE 輪郭全点の情報を保持
contours, hierarchy = cv2.findContours(
connected.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE
)
mask = np.zeros(bw.shape, dtype=np.uint8)
rects = []
for idx in range(len(contours)):
# 外接矩形
x, y, w, h = cv2.boundingRect(contours[idx])
mask[y : y + h, x : x + w] = 0
# 輪郭を描画
cv2.drawContours(mask, contours, idx, (255, 255, 255), -1)
# 面積割合
r = float(cv2.countNonZero(mask[y : y + h, x : x + w])) / (w * h)
# 抽出条件 面積、縦・横の長さ
if r > 0.45 and w > 20 and h > 8:
# cv2.rectangle(large, (x, y), (x + w - 1, y + h - 1), (0, 255, 0), 2)
# color = np.random.randint(0, 255, 3).tolist()
# cv2.putText(large, str(idx), (x, y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, color, 3)
rects.append((x, x + w, y, y + h))
# 矩形の左下でソート
rects = sorted(rects, key=lambda x: (x[3], x[0]))
# 日付
# 座標 タイトルと日付のY座標が変わるため右側にあるのを採用
x1, x2, y1, y2 = rects[0] if rects[0][0] > rects[1][0] else rects[1]
# 切り出し
dst = img[y1:y2, x1:x2]
cv2_imshow(dst)
txt = (
pytesseract.image_to_string(dst, lang="jpn", config="--psm 6")
.strip()
.replace(".", "")
.replace(",", "")
.replace(" ", "")
)
txt
# 座標
x1, x2, y1, y2 = rects[2]
y_crop = int((y2 - y1) * 0.23)
# 切り出し
dst = img[y1:y2, x1:x2][-y_crop:-5, 5:-5]
cv2_imshow(dst)
txt = pytesseract.image_to_string(dst, lang="jpn", config="--psm 6").strip()
txt
cv2.imwrite(f"yousei.png", dst)
cv2_imshow(dst)
edges = cv2.Canny(dst, 100, 200, apertureSize=3)
lines = cv2.HoughLines(edges, 1, np.pi / 2, 15)
# 縦線除去
for line in lines:
for rho, theta in line:
if theta == 0:
a = np.cos(theta)
# b = np.sin(theta)
x0 = int(a * rho)
x1, x2 = x0, x0
y1, y2 = 100, -100
cv2.line(dst, (x1, y1), (x2, y2), (255, 255, 255), 3)
cv2_imshow(dst)
txt = (
pytesseract.image_to_string(dst, lang="jpn", config="--psm 6")
.strip()
.replace(".", "")
.replace(",", "")
)
print(txt)
data = list(map(int, re.findall("\d+", txt)))
print(data)
# 検査実施人数
# 座標
x1, x2, y1, y2 = rects[8]
# 切り出し
dst = img[y1:y2, x1:x2]
cv2_imshow(dst)
txt = (
pytesseract.image_to_string(dst, lang="jpn", config="--psm 6")
.strip()
.replace(".", "")
.replace(",", "")
)
txt
https://github.com/code4nagoya/covid19/issues/429#issuecomment-627992667
で、 @imabari さんが OCR で自動取得するスクリプトを提示されたので、この issue に引き継ぎます。