![見出し画像](https://assets.st-note.com/production/uploads/images/97698716/rectangle_large_type_2_419e952388ccc66599923c0d649171fb.png?width=1200)
Vision API OCRを使った保険証マスキングシステムの紹介
こんにちは、プロダクト開発部のyuyaです。
今回は昨年から今年にかけて社内で開発した、年齢確認書類の自動マスキングシステムについて紹介させていただきます。
初の記事投稿になるので簡単に自己紹介をします。
私のエンジニア歴は約4年、Newbeesに在籍してからはもうすぐ2年になります。
現在はおもに、年齢確認書類の画像解析プロジェクトの開発をしています。
入社当初は、マッチングサービスのWebとiOS開発をしていました。
しばらくして、大学院で画像解析の研究をしていた経歴と研究開発がしたいという私の希望から、画像解析プロジェクトを担当することになりました。
今後は、解析の精度向上や新規サービス開発への足がかりとして、人工知能技術に取り組んでいきたいと考えています。
最近は防寒にハマってます。
今年は足専用のヒーターと発熱する手袋を買いました。
これで冬もぽかぽか開発できます。皆さんにもおすすめです。
1. システムの目的
みなさんご存知かもしれませんが、マッチングアプリは、18歳以上でないと利用することができません。
そのため、年齢確認を行う必要があり、ユーザーから運転免許証や健康保険証などを提供していただいています。
サービス運営企業では、提供いただいた重要な個人情報を保護するために、年齢確認に不要な部分を手動でモザイク加工しています。
作業自体は5分程度で終わるものなのですが、サービスグロースに伴い、ユーザーから1日に大量の健康保険証がアップロードされるようになり、手動でのモザイク加工が運営企業(管理ユーザー)の負担となっておりました。
そこで管理ユーザーの負担軽減を目的とした、自動マスキングシステムを開発することになりました。
2. ワークフロー
本システムのワークフローは、以下のようになっています。
ユーザーから受信した年齢確認画像をAWS S3へアップロード
S3へのアップロードをトリガーに、画像をマスキングするAWS Lambdaを実行
Lambdaでマスキングした画像をS3へアップロード
管理ユーザーに表示
![](https://assets.st-note.com/img/1671618632382-8iOAvM1ac3.jpg?width=1200)
今回Lambdaを利用した背景としては、以下の2点が挙げられます。
マッチングシステム本体がモノリシックになりつつあったので、自動マスキングシステムを切り出したかった
自動マスキングを疎結合にすることで、他プロジェクトへも容易に導入するようにしたかった
また、本システムの開発言語には、保守性とセキュリティの観点からGoを使用しました。
3. マスキング成功の条件
本システムのマスキング対象の書類は健康保険証です。
赤枠で囲った5箇所(記号、番号、枝番、保険者番号、QRコード)の自動マスキングに成功し、それ以外の領域を誤ってマスキングしない場合を成功と定義しました。
![](https://assets.st-note.com/img/1671610883660-whqi91ndhs.jpg?width=1200)
検出対象は、テキストとQRコードの2種類に分けることができます。
4. テキストのマスキング方法
健康保険証は、市や組合によってデザインが異なります。
テキストの配置も様々で、マスキングの対象は数字のみではなく、漢字やアルファベットを含むケースもあります。
![](https://assets.st-note.com/img/1671610269442-aCzGDAD4JL.jpg?width=1200)
このような様々な形式の保険証に対応するためには、人間が保険証を確認する手順をシステムのロジックに落とし込む必要があります。
管理ユーザーが手動マスキングする際は、「記号」「番号」などの表記を見つけ、その横や下にある数字をマスキングしています。
その手順をシステムに組み込むために、画像内から文字を検出するサービスを利用しました。
4-1. Vision API OCRについて
画像内のテキスト検出(OCR)には、Google CloudのVision APIを使用しました。
Vision APIは、機械学習を用いて画像内から物体検出や分類を行うライブラリです。
Vision APIを選んだ理由は、日本語の検出に対応していたためです。
AWSにもOCRはあるのですが、日本語の検出は現在未対応でした。
Vision API OCRでは、画像内のテキストをブロック、段落、単語ごとに区切って認識され、検出された単語を囲む矩形座標と確信度が取得できます。
![](https://assets.st-note.com/img/1671618005512-LTNENBY3YH.jpg?width=1200)
4-2. OCRを用いたマスキング手順
以下の手順でOCRを用いてマスキングを行いました。
OCRで画像内のテキスト情報を取得
「記号」「番号」「枝番」「保険者番号」の単語を探す
右方向、下方向にあるテキスト領域にモザイク処理をかける
![](https://assets.st-note.com/img/1671618824760-g9BBXvxKjn.jpg?width=1200)
この手順のみでは、保険証の形式や写真の撮り方によってマスキングが失敗してしまうことがありました。
次章では、その一例である背景文字を含む保険証の問題と、画像解析を用いた解決方法を紹介します。
4-3. 背景文字を含む保険証
保険証には、背景に文字を含むケースがあります。
この保険証をOCRにかけても、背景文字のローマ字のNEWBEESを黒文字よりも優先して認識され、抽出したい黒文字をうまく読み取ることができないという問題がありました。
そのため、画像処理により背景文字を消す手法を考えました。
![](https://assets.st-note.com/img/1671618839193-7sZIAvmZCH.jpg?width=1200)
4-4. 固定値による2値化の問題
背景文字の削除には、2値化という処理を用います。
2値化とは、ある値以上の画素値を白、ある値未満の画素値を黒に変換することで、画像を白と黒の2階調に変換する処理です。
はじめに、入力されたカラー画像を8bitのグレースケール化します。
各ピクセルの画素値は8bitのため、0~255の範囲に変換されます。
このグレースケール画像に対し、本文のみを抽出することができる閾値である画素値110で2値化を行いました。
背景文字が消えていることがわかると思います。
![](https://assets.st-note.com/img/1671621399948-Ac8cnxQBjJ.jpg?width=1200)
しかし、他の画像で同様に閾値110で2値化処理すると、本文も消えてしまいます。
この2例目の画像は光が当たっており、先ほどの画像より輝度が高いためです。
このように、最適な2値化の閾値は画像によって異なることがわかります。
そこで今回は、動的に2値化の閾値を決定する方法を模索しました。
![](https://assets.st-note.com/img/1671618242378-BRNymwErjz.jpg?width=1200)
4-5. 動的閾値決定による2値化手法
閾値を動的に決めるため、まずは保険証の本文領域と背景文字領域を分類します。
OCRで検出された単語から、類似度の高い単語の集団を背景文字、それ以外の数字や漢字を本文とし、単語を分類しました。
![](https://assets.st-note.com/img/1671618854840-2xVTdK3Qyi.jpg?width=1200)
2つの領域を見比べると、黒文字が含まれる本文領域の方が、暗い画素が多いことがわかります。
そこで画素の情報から、背景と本文を分ける閾値を決められるのではないかと考えました。
画素の情報を可視化するために、2つの領域のヒストグラムを作成します。
ヒストグラムとは、横軸を画素値、縦軸を各画素値が全体の何%を占めるかを示したグラフです。
横軸の画素値は低いほど黒く、高いほど白い画素を表します。
グラフの赤い領域が本文、青い領域が背景文字のヒストグラムです。
![](https://assets.st-note.com/img/1671614268143-p9RgMQJWyz.jpg?width=1200)
本文領域のグラフを見ると50~110の低い画素に一定の割合が含まれており、これが本文の黒文字であると推測できます。
背景領域のグラフは黒文字がない分、背景の画素である130~150付近の割合が大きくなっていることがわかります。
そこで、本文の黒い画素値と背景を切り離すことができるであろう2つのグラフの交差点を、2値化の閾値としました。
![](https://assets.st-note.com/img/1671621488076-b8L4D87uiB.jpg?width=1200)
このように、画像ごとに本文領域と背景領域のヒストグラムの交差点を求めることで、背景文字を削除できる最適な二値化の閾値を動的に求めます。
今回の動的閾値決定手法による背景文字の削除の結果がこちらです。
先程の固定値の2値化で失敗した画像も、本文のみ抽出できていることがわかります。
![](https://assets.st-note.com/img/1671618283966-NuSrooao8m.jpg?width=1200)
今回の背景文字の削除処理によって、背景文字が原因で失敗した画像において90%(53/58)の改善に成功しました。
システムの処理の流れは、OCRから取得したテキスト情報から背景文字が検出された場合に、背景文字削除処理をし、再びOCRをかけて4-2で述べた検出とマスキング処理を行います。
![](https://assets.st-note.com/img/1671618299065-DLbHsKnUmM.jpg?width=1200)
5. QRコードのマスキング方法
QRコードのマスキングにはVision APIと独自の検出手法を併用しました。
5-1. Vision APIのオブジェクト検出
Vision APIのオブジェクト検出は、画像内にある物体の認識ができます。
QRコードの認識にも対応していたため、このAPIを利用しました。
5-2. 独自の検出手法
リリース当初はVision APIのみでQRコードの検出をしていたのですが、検出率がおよそ70%とあまりいい精度ではありませんでした。
また、Vision APIのような外部APIは、ブラックボックスであるため改善することはできません。
そこで、独自の検出手法を導入し、Vision APIと併用することで検出率を70%から96%に上げることができました。
次章で独自の検出手法について解説します。
5-2-1. 検出フロー
検出のフローは、以下のようになっています。
はじめに、2値化によりQRコードを含む関心領域を黒(画素0)、非関心領域を白(画素255)に分けることで、関心領域を限定します。
次に、OCRで検出されたテキスト領域を白く塗りつぶすことで、QRコード以外の領域を削減します。
最後に、クロージング処理をした後に各領域ごとの特徴量を抽出し、最もQRコードの特徴に近い領域にマスキングをかけます。
クロージング処理と特徴量抽出について、次章で詳しく説明します。
![](https://assets.st-note.com/img/1671618325026-QVppnC314h.jpg?width=1200)
5-2-2. クロージング処理
クロージング処理とは、近い領域同士を結合する画像処理です。
QRコードの領域を1つにまとめる目的で使用しました。
処理前の黒い点が密集しているQRコードが、処理後の画像で黒く塗りつぶされており、正方形のシルエットになっていることがわかります。
この処理は、この後の特徴量抽出でQRコードの領域を決定するために必要な処理です。
![](https://assets.st-note.com/img/1671618345991-FPPL1nb6uh.jpg?width=1200)
5-2-3. 各領域の特徴量抽出
クロージング処理後、画像の各領域から特徴量として面積、縦横比、密集度を算出します。
縦横比は1に近いほど正方形を表します。
密集度は1に近いほど内部が黒で満ちています。
表を見ると、QRコードの領域であるAは面積が大きく、縦横比は1で正方形に近く、密集度が高いことがわかります。
対して、Bのハンコの領域は面積と縦横比はAと似ていますが、密集度が低いことからQRコードではないと考えられます。
Cの領域は、縦横比と密集度は高いですが、面積が小さいためQRコードではないと考えられます。
![](https://assets.st-note.com/img/1671612640725-PRKJsGluJn.jpg?width=1200)
このように全ての領域の特徴量を抽出し、最もQRコードの特徴に近い領域にモザイクをかけます。
このフローにより、多くの画像でQRコードの検出に成功しました。
5-2-4. 影がある写真の対策
しかし、ユーザーから提供される画像はすべてキレイに撮影されたものではなく、こちらのように影がかかっている場合もあります。
この場合は5-2-1の検出フローにおける二値化の段階で、影と一緒にQRコードが塗りつぶされ、検出失敗となってしまいます。
![](https://assets.st-note.com/img/1671618417482-RWDdR21xP7.jpg?width=1200)
そこで影の除去を試みます。
手順としては、まずグレースケール化した画像と、それをぼかした画像を用意します。
その2つの画像の差分画像を生成することで影を取り除きます。
差分画像は、画像間で異なる部分のみを抽出できます。
![](https://assets.st-note.com/img/1671618432028-6KBRJxJ8nU.jpg?width=1200)
少しわかりづらいかと思うので、画像中央の影の境界部分を拡大します。
![](https://assets.st-note.com/img/1671618446877-e1r6T1ZsLb.jpg?width=1200)
ぼかし処理をした画像Bは、画素の勾配が大きい部分ほど、処理前の画像Aと異なります。
文字周辺部分を見ると、Bで文字がぼかされており、Aの画素と大きく変わっているため、差分画像で文字部分が抽出されていることがわかります。
対して、文字以外の影の部分は、AとBで画素値の変化がほとんどないため、差分画像では影が抽出されていないことがわかります。
QRコードも、文字と同様に強くぼかされているため、差分画像で抽出されます。
このようにして、画像から影のみを除去することができました。
影を除去した差分画像を5-2-1のフローにかけることで、影がかかっているQRコードの検出に成功できました。
![](https://assets.st-note.com/img/1671618464078-dfAvyeE1F3.jpg?width=1200)
6. 結果
Vision APIと独自の画像解析手法を組み合わせることで、95%の保険証の自動マスキングに成功することができ、開発当初の目標であった90%以上を達成できました。
単純計算にはなりますが、手動のマスキング作業を約5%に負担軽減でき、1画像あたりの手動マスキングに5分掛かる場合、1日100件(500分)としても475分(≒8時間)の削減が行えたことになります。
約1人日になるので、運営企業の方にも、本来取り組むべきビジネスに時間を使っていただけるようになりました。
7. 考察
7-1. Vision APIを使ってみて
今回、機械学習サービスであるVision APIを使ってみて、メリットと注意した方がよいと感じたことをまとめました。
7-1-1. メリット
メリットの1つ目は専門知識が不要であることです。
誰でも簡単に扱うことができます。
2つ目はスピード感です。
もし、Vision APIを使わずに、文字検出から開発していたら年単位で時間がかかっていたと思います。
このような開発時間の短縮は、ビジネス視点で大きなメリットになります。
7-1-2. 注意点
注意点の1つ目は、事前にそのAPIができること、できないことを把握する必要があるということです。
本システムで把握する必要があったのは、日本語検出が可能か、黒い文字のみ検出可能か、という点です。
そして、APIができない部分を、自分で対処できるのかを検討する必要があります。
例えば、今回Vision APIには、黒い文字のみ検出するという機能がなかったため、背景文字の削除処理を実装しました。
2つ目は、APIはブラックボックスであるため、失敗した場合に原因の特定が困難、かつ改善が不可能であることです。
今回、QRコードの検出率が思ったより良くなかったように、実際に試してみると、それだけではうまくいかない場合もあります。
その場合に、自主開発した検出ロジックを組み込むなど、補助的な処理が必要になります。
7-2. 検出システムで大事なこと
最後に検出システムで大事だと思うことをまとめました。
7-2-1. 対象の画像の分析
システム開発の初期段階において、どういった画像があるかを分析し、カテゴライズすることが重要です。
本システムでは、数百枚の画像を分析し、システムのロジックの方針を立てました。
数十枚のみの分析でロジックの方針を立ててしまうと、一部の形式の画像にしか対応できないシステムになってしまう可能性があります。
様々な形式の画像に対応するために、なるべく多くの画像を分析することが重要です。
7-2-2. システムの汎用性の向上
未知の失敗パターンに対応できるように、システムの汎用性を上げることも重要です。
例えば、OCRで「枝番」が「核番」や「枝香」と誤って認識されたときに、このエラーが出るということは「核」「香」というパターンも出ると考えられます。
未知のパターンの「核香」に対応するために、文字を総当たりで検索するようにシステムを組んでおきました。
今回のようなユーザーが撮影した写真を対象としたシステムの場合は、全く同じ画像を受信することがないため、未知の失敗パターンに対応できるようにしておくことが大事です。
7-2-3. 誤検出を抑えつつ検出成功率を上げる
検出システムで最も大事なことは、誤検出を抑えつつ検出成功率を上げることです。
そのためには様々な工夫が必要ですが、一例として本システムではロジックの順序を工夫しました。
システムの検出フローの中で、はじめに誤検出率の低い検出方法を行い、それでも検出できなかった場合のみ、誤検出の可能性がやや高くても検出率を上げる方法を実行するようにしました。
具体的には、最初に表記と番号をセットで検出するようにし、それでも見つからなかった場合のみ、表記がなくても位置情報から対象の数字を探すロジックを組みました。
このようにすることで、写真の写りの問題でOCRが表記を認識できず、数字のみしか認識できなかった場合にも対応することができます。
![](https://assets.st-note.com/img/1671618481661-wTXkM8xNz4.jpg?width=1200)
8. 終わりに
今回は、Vision APIと画像解析手法を用いた自動マスキングシステムを紹介させていただきました。
今後も弊社では、画像解析や機械学習を用いて業務の課題を解決していく予定です。
Newbeesでは一緒に働く仲間を募集しています
フルリモート勤務を導入し、場所にとらわれない自由な仕事のやり方が可能です。詳細は以下をご覧ください。
※記載されているロゴ、システム名、製品名は各社及び商標権者の登録商標あるいは商標です。
※身分証画像は全て弊社で当記事専用に作成した架空のものとなります。