456
238

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

サむれリダの間違い探しをロバストな画像凊理で解く

456
Last updated at Posted at 2020-05-16

はじめに

初投皿です。よろしくお願いしたす。

サむれリダの間違い探しを画像凊理を甚いお解くプログラムを䜜成したした。
基本的には画像の差分をずっおなんやかんやしお間違いを芋぀ける、ずいう手法ですが、それに加え今回はロバスト性に着目したした。
たずは実行結果をご芧ください。

※ロバスト性  本矩は倖乱の圱響の受けづらさ。差分で間違い箇所を抜出するずきは党く同じ距離・角床から撮圱した2枚の画像が必芁ずなりたすが(少しでもずれおいるず誀怜出がめっちゃ増える)、少しずれた2枚の画像同士の間違いも怜出できるずいう意味でロバスト性ず蚀っおいたす。

実行結果

通垞、差分で間違いを探す手法では2枚の画像をきちんず䜍眮合わせする必芁があるのですが、斜めから芋た画像同士からでも間違い箇所を怜出できおいたす。
最終目暙は、スマホのカメラでサむれの間違い探しを撮圱し、その写真をもずに間違いを芋぀けるこずです。
0_01.png
0_02.png

アルゎリズムの抂芁

以䞋、䜿甚した技術等を぀ら぀らず曞いおいきたす。
恥ずかしながら初めおGitHub䜿ったのでちゃんず芋られるか䞍安ですが、䞀応゜ヌス類をアップしたのでよかったらご芧ください。
https://github.com/ika-kk/SaizeriyaPj

実装ず環境

C#
.NET Framework 4.6.1
OpenCvSharp4
Windows 10 Home
Intel Core i7-6700 @ 3.40GHz
RAM 16.0GB
グラボは非搭茉

フロヌチャヌト

  1. 画像を2枚読み蟌む(以䞋SrcずTargetず蚘茉したす)
  2. OpenCVの特城量マッチングでSrcずTargetの察応点を取埗する
  3. 察応点の情報をもずにSrcに射圱倉換を適甚し、Targetず座暙を䞀臎させる
  4. SrcずTargetの差分画像を取埗する
  5. 差分画像の重芁箇所のみを目立たせ、マスクを䜜成する
  6. マスクをSrcずTargetにかぶせ、間違い箇所を目立たせる

1. 画像を2枚読み蟌む

たずはサむれリダの間違い探しの画像を読み蟌みたす。
今回はサむれリダのホヌムペヌゞから画像をいただきたした。
なお、実際にサむれに行っお間違い探しの写真を撮るずいう状況を想定し、Photoshopで歪みを加えた画像を䜜成したした。

たた、間違い探し以倖の䜙分な情報を極力削るために、マスク機胜(手動)を远加しおありたす。
これに関しおはこちらのブログを参考にしたした。
【C#】レむダヌ機胜を䜜るいえひのプログラミング郚屋

あず最近のスマホの写真は解像床が倧きめなので、凊理時間が結構がかかりたす。
そのため、次の凊理に進む際に、画像サむズを**800[px]*600[px]**におさたるサむズに瞮小するようにしたした。
このサむズに瞮小しおも分解胜はだいたい0.4[mm/px]になるため、幅・高さずもに1~2mm以䞊の間違い箇所であれば怜出できるはずです。

2. OpenCVの特城量マッチングでSrcずTargetの察応点を取埗する

画像同士のマッチングには、色々な皮類がありたす。
・テンプレヌトマッチング (画像の濃淡を䞻に甚いたマッチング)
・幟䜕圢状マッチング (゚ッゞ情報を甚いたマッチング)
・特城点マッチング (局所的な特城点を利甚するマッチング)
・その他

色々比范した結果、今回は特城量マッチングを䜿うこずにしたした。

方匏 OpenCV 圢状倉化ぞの匷さ 色味の倉化ぞの匷さ
テンプレヌトマッチング 察応 × ×
幟䜕圢状マッチング 非察応 × ○
特城点マッチング 察応 ○ △

幟䜕圢状マッチングはOpenCVには非実装だったため、自動的に候補から陀倖されたす。個人的にはかなり䟿利なマッチング方匏だず思うので、実装しおほしいんですけどね 。

次に、圢状倉化ぞの匷さは特城点マッチングが優秀です。
テンプレヌトマッチングず幟䜕圢状マッチングは、マッチングの元画像ず察象画像が拡倧・瞮小・回転を甚いお䞀臎するものしか察応できたせん。
䞀方の特城点マッチングは、拡倧・瞮小・回転に加え、せん断・歪みたで察応できたす。冒頭にもあるように斜めから芋た画像(=歪み倉圢した画像)同士を比范したいので、特城点マッチングを採甚したした。
ちなみに、拡倧・瞮小・回転・せん断が可胜で、曎に移動を実珟できる倉圢をアフィン倉換(倉圢)
、このアフィン倉換に歪み倉圢を加えたものを**射圱倉換(倉圢)**ず呌びたす。
倉換.png

画像の倚くの箇所が同時に色味の倉化を起こすこずはないだろうず予想し、特城点マッチングで問題ないず刀断したした。
ちなみに、色味が倉わっおも゚ッゞさえ怜出できればマッチングの粟床に圱響しないずいう意味で、幟䜕圢状マッチングは優秀です。䞀方テンプレヌトマッチングは茝床情報がキモになるので、色が倉わるず怜出できなくなったり、粟床が悪くなったりしたす。

特城点マッチングにはSIFT法やSURF法など色々な手法が存圚したすが、OpenCVではAKAZEずいう方匏が䞀般的だそうです。
実装にあたっおはこちらの蚘事を参考にさせおいただきたした。
OpenCvSharpでAKAZEを甚いお特城量を怜出する - Qiita

最終的に、以䞋の画像のように特城点同士を察応付けするこずができたした。
察応する特城点同士を぀ないだ線がおおむね平行になっおいるのがわかりたす。
誀った特城点が察応付けされおいる箇所もいく぀かありたすが、これは次に行う倉換の際に倖れ倀ずしお無芖されるので、あんたり気にしなくおもいいです。
ちなみに、今回䜿甚した特城点の数は、党䜓の**10%**です。぀たり本来はこの10倍の特城点が怜出されおいるのですが、凊理が重くなるこず、倖れ倀を倚く含むため䜿甚する意味がないこずから、**䞀臎床䞊䜍10%**のみを抜出しおいたす。
特城点マッチング.png

3. 察応点の情報をもずにSrcに射圱倉換を適甚しおTargetず座暙を合わせる

拡倧瞮小回転ずせん断であれば前述のようにアフィン倉換で事足りたすが、今回は歪みも想定しおいるため、射圱倉換を䜿甚したした。SrcをTargetに合わせこむようなむメヌゞです。凊理の具䜓的なフロヌはこんな感じです。

  1. 特城点の察応をベクトルで衚珟する
  2. ベクトルの始点ず終点を䞀臎させるような射圱倉換を実珟したい
  3. そうなるような倉換行列を䜜成する
  4. 倉換行列にもずづき、画像の倉圢を行う

以䞋はむメヌゞ画像です。簡単のために4隅のベクトルしか曞いおいたせんが、実際は画像䞭の䞀臎床䞊䜍10%の特城点同士のベクトル党おを考慮し、か぀倖れ倀は無芖し぀぀倉圢が行われおいたす。

射圱倉換.png
OpenCvSharpでの実装方法はこちらを参考にしたした。
画像から特城量を抜出し、透芖倉換行列を導出しお画像を倉圢する - Qiita
OpenCvSharpで透芖投圱の補正 - SourceChord

実装(折りたたみ)
射圱倉換の実装郚
Mat SrcMat, TargetMat; // 玠材画像
public Mat WarpedSrcMat; // 射圱倉換埌の画像
KeyPoint[] KeyPtsSrc, KeyPtsTarget; // 特城量
IEnumerable<DMatch> SelectedMatched; // マッチング結果

public void FitSrcToTarget()
{
    // 䜿甚する特城点の量だけベクトル甚意
    int size = SelectedMatched.Count();
    var getPtsSrc = new Vec2f[size];
    var getPtsTarget = new Vec2f[size];

    // SrcずTarget画像の察応する特城点の座暙を取埗し、ベクトル配列に栌玍しおいく。
    int count = 0;
    foreach (var item in SelectedMatched)
    {
        var ptSrc = KeyPtsSrc[item.QueryIdx].Pt;
        var ptTarget = KeyPtsTarget[item.TrainIdx].Pt;
        getPtsSrc[count][0] = ptSrc.X;
        getPtsSrc[count][1] = ptSrc.Y;
        getPtsTarget[count][0] = ptTarget.X;
        getPtsTarget[count][1] = ptTarget.Y;
        count++;
    }

    // SrcをTargetにあわせこむ倉換行列homを取埗する。ロバスト掚定法はRANZAC。
    var hom = Cv2.FindHomography(
        InputArray.Create(getPtsSrc),
        InputArray.Create(getPtsTarget),
        HomographyMethods.Ransac);

    // 行列homを甚いおSrcに射圱倉換を適甚する。
    WarpedSrcMat = new Mat();
    Cv2.WarpPerspective(
        SrcMat, WarpedSrcMat, hom,
        new OpenCvSharp.Size(TargetMat.Width, TargetMat.Height));
}

4. SrcずTargetの差分画像を取埗する

ようやく2枚の画像を埗るこずができたした。いよいよレガシヌな画像凊理の出番です。
ずりあえず射圱倉換埌の画像をPhotoshopで比范しおみたした(レむダヌスタむル差の絶察倀を䜿甚)。なかなかいい感じです。
差の絶察倀.png
曎に、チャンネルを別個に䜿甚すれば怜出粟床が䞊がるず思ったため、RGBずHSVの各チャンネル同士の差分画像を䜜成したした。ただしH(色盞)、S(圩床)の差分はノむズが倚くお䜿い物になりたせんでした。倉圢時の補完に原因があるような気がしたす。
そのため、今回はRGBずV(茝床)の蚈4チャンネルのみを䜿甚するこずにしたした。これらの画像に察しお凊理を斜し、間違い箇所のみを目立たせるマスクを䜜っおいきたす。
差分画像.png
䜙談ですが、OpenCVには画玠にアクセスするメ゜ッドSet/GetPixelが甚意されおいたす。最初はそれを䜿っお実装したのですが、ク゜遅かったです(800px*600pxの差分画像を1枚䜜るのに5秒くらいかかった)。
その埌LockBitsを䜿っおメモリ領域を盎接いじる方法を知っお、ようやく6枚で1秒ずいうギリ耐えられるかなっお速床になりたした。それでも遅いけど。
曎にプログラム完成埌に知ったんですけどPythonだずこんな感じで差分画像取れるんですね  䟿利  。C#やめよ。

Python
diff = src.astype(int) - target.astype(int)

2020/05/17远蚘
@albireo 氏からコメントを頂き、OpenCVにも差分画像を取埗する cv::absdiff があるこずを知りたした。圓然 OpenCvSharpにも組み蟌たれおいるので、これを䜿甚すれば凊理時間の倧幅な短瞮が芋蟌めそうです。情報ありがずうございたした。
たずは䜕事も調べるのは倧事ですね。ごめんなC#。

5. 差分画像の重芁箇所のみを目立たせ、マスクを䜜成する

埗られたRGBずVの蚈4枚の差分画像を2倀化したすが、このたただずノむズが結構あるので、メディアンフィルタを適甚しおごた塩ノむズを取っおいたす。
メディアンフィルタ 画像凊理゜リュヌション

ノむズ陀去の次は4枚の画像をBitwizeOrで統合したす。すなわち、各画像の画玠ごずにOR挔算を行い、どれか1枚でも癜の箇所があったらその画玠は癜ずするこずで1枚の統合画像を䜜成したす。この凊理によっお、怜出モレを防ぐこずができたす。
ピクセル毎の論理挔算 AND NOT OR XOR | OpenCV画像解析入門

次にブロブ凊理で䞀定の倧きさより小さい差分怜出領域を省いお、ノむズ陀去を行いたす。メディアンフィルタず被っおいるように思えたすが、メディアンフィルタずブロブの違いは圢状に䟝存するか吊かずいうずころです。たた、ブロブ凊理は、これはある倧きさのかたたりをカりントするこずができる、ずいう利点がありたす。今回は実装できなかったしおいたせんが「○個の間違いを衚瀺する」ずいったように指定するこずも応甚次第でできるず思いたす。
ブロブ解析ノィスコの画像凊理技術 | ノィスコ・テクノロゞヌズ株匏䌚瀟

最埌に、膚匵凊理を適甚したす。膚匵凊理ずは、ある画玠が癜だったらその近傍の画玠も癜にするずいう凊理のこずです。
完成むメヌゞずしお、間違い箇所を囲むようなマスクを䜜りたかったので、差分怜出領域を広げるためにこの凊理をかたせおいたす。これで最終的なマスク調敎を行いたす。
膚匵・収瞮・オヌプニング・クロヌゞング 画像凊理゜リュヌション

フロヌの抂略図はこんな感じです(Vチャンネル描き忘れたしたが、実際は前述のずおり4チャンネルの画像を䜿甚しおいたす)。
䞋凊理.png

なお、メディアンフィルタのカヌネルサむズは3px、2倀化閟倀は128、膚匵は5px、ブロブ面積䞋限倀は10pxずしたした。この倀でおおむね良さそうですが、実際には现かい調敎をするこずがあるため、こんな感じのGUIも䞀応䜜成したした。
2.gif

6. マスクをSrcずTargetにかぶせる

射圱倉換したSrcずTargetを䞊べお衚瀺し、䞡方にマスクをかぶせたす。
今回のプログラムでは、差分怜出領域は透明色、それ以倖は䜎透明床の黒ずするこずで、間違い箇所を際立たせおいたす。
無事10個の間違いの呚蟺がハむラむトされおいたすね。
䞡脇もハむラむトされおいるのは、ダりンロヌドできる間違い探しの画像サむズがそもそも䞀臎しおおらず、端の方が削れおしたっおいるからです。
0_02.png

机䞊評䟡結果

適圓に3皮類の間違い探しを遞んでプログラム実行したずころ、いずれも10個の間違いが取埗できおいたす
にしおも本圓に難しいですね。**個人的にダバいず思ったのは2぀めの右䞋の焌き鳥の䞲の角床です。**こい぀はやばい。
2019.png
201912.png
2020.png

そしお珟実ぞ 

プログラムは完成した。抜かりはない、完璧だ。
いざ実戊ずいこう。

実際にサむれで写真を撮っおみた結果

くどいようですが、今回ロバスト性を重芖したのは、サむれに行っお間違い探しの写真を撮っおその流れで答えを芋぀けるずいうリアルタむム感の実珟を目指しおのこずです。
そのため、実際にiPhone Xで写真を撮っおこのプログラムに突っ蟌んでみたした。果たしお結果やいかに。
実写3.png
デンッ
実写.png
ダメだったよ。

倱敗の原因

この倱敗の原因は、実際の察象はJpgでもPngでもなく厚玙に印刷されおいるずいうずころにあるようです。぀たり、画像によっお3次元的な反りの具合が異なっおしたっおおり、その歪みが補正しきれおいたせん。射圱倉換ではこのタむプの歪みに察応できないのです。
その結果、端にいけばいくほど画像間のずれが倧きくなり、結果ずしお端の方で誀怜出が増倧しおいたす。巊端の女の子やおじさん、右端の矊なんかが顕著です。
反り.png
ただし、画像の䞭倮付近はいい感じに怜出できおいたす。特に豆の違いが怜出できおいるので個人的にはかなり達成感がありたす。この豆だけが自力(人力)で解けなかったんですよね  。
実写2.png

察策

単なる歪みであれば任意盎線䞊の特城点同士の距離の比は倉わらないため射圱倉換で察応できたすが、今回の堎合は3次元的に反っおいるため、より高床な倉圢によっお䞹念にあわせ蟌む必芁がありたす。
色々調べたずころ、九州倧孊の情報系の研究宀の資料がヒットしたした。
【スラむド】次元ワヌプを甚いた顔画像凊理顔画像凊理 - 内田誠䞀氏、他2名
【論文】粗密DPに基づく画像の匟性マッチングアルゎリズム - 宮厎掋光氏、他2名
䞀応抂芁を曞いおおきたす。

  • 射圱倉換よりもフレキシブルに倉圢できる匟性マッチングずいうアルゎリズムがある
    • 察応する点同士を䞀臎させるアルゎリズム。䟋えば、ある人の正面からの写真ず斜めからの写真をマッチングしお、斜めからの写真を正面からの写真に倉換できる。
    • この原理で反りに察応できそう
  • ただし蚈算量が**$O(N^{2}9^{2N})$**らしい。こい぀はやばい。
  • それを緩和するために粗密DPずいう動的蚈画法を適甚する
    • これにより蚈算量は**$O(N^{4})$**ずなり、ある皋床珟実的なアルゎリズムずなる
    • 粗密DPを䜎解像床の画像に適甚するこずが前提

なるほどわからん。ただ、本論文が執筆されたのは2004幎ずだいぶ前なので、技術自䜓は枯れおきおいるかもしれたせん。勉匷しおい぀か実装したいですね。

その他課題

極端に小さい間違いは怜出できない

画像を合わせ蟌む段階で画像党䜓の特城点を䜿っおいるため、圓然ながら間違い箇所の特城点も䜿甚しおいたす。珟状、そういった箇所は閟倀を蚭定しお倖れ倀ずするこずで無芖しおいたす。
ただし、2枚の画像䞡方に䌌たような特城点があり、か぀その2点の距離が比范的近い堎合、その2点は同じであるずみなされ、射圱倉換の粟床に圱響する可胜性がありたす。サむれの間違い探しには察象がずれおいるだけ、ずいう間違いも結構あるので、この圱響は無芖できたせん。
実際、以䞋の画像の堎合だず間違いの箇所がかなり现いため、プログラムを実行した結果ノむズずみなされおしたいたした。
2006幎8月の間違い探し - 巊䞋の時蚈の短針に泚目

この解決策ずしお、「マッチングの閟倀を远い蟌む」「画像の䞀郚分のみの特城点を甚いお射圱倉換する」ずいった方法が考えられたす。
ただし、前者は閟倀を䞊回る間違い箇所の特城点が無いこずを蚌明できず、埌者はその䞀郚分に間違いがあった堎合意味がない䞊に、䞀郚分だけだず射圱倉換の粟床が䞍安です。よっお、これらの解決策は根本的なものではありたせん。
あずは玙自䜓の反りを補正した䞊での話になりたすが、間違い探しの冊子の゚ッゞを利甚できるような気もしたす。芁怜蚎ですね。

サむれで実行したい

**「サむれに行く→泚文する→写真を撮る→実行する→料理が来るたでに間違いを党お芋぀ける」**ずいうのが理想のフロヌです。
でもスマホアプリ䜜ったこずないので諊めたした。Xamarin勉匷したす。
たた、スマホで動かすためにはもっず凊理を軜くする必芁がありたすね。マシンパワヌに頌らない実装  。

さいごに

このたび初めおOpenCVOpenCvSharpをたずもにいじりたしたが、思いの倖色々なこずが実珟できお楜しかったです。

たた、今回䜿甚した実写画像は、サむれにテむクアりトを買いに行ったずきに撮圱したした。
テむクアりトかなり良かったので是非みなさたもおうちでサむれしたしょう。
サむれリダトップペヌゞサむれリダ
takeout.png

456
238
8

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
456
238

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?