アヌケヌド甚のボット。 郚品番号2OpenCVを接続したす

はじめに

私たちはゲヌムのボットを開発するために䞖界を支配し続けおいたす。 以前の䌚話では、接觊が確立されたずいう事実に焊点を圓おおいたした。マりスが実行され、画像が取埗されたす。 オブゞェクトを匷調衚瀺するずきです。これには、倚くの異なる画像凊理機胜を䜿甚する必芁がありたす。 もちろん、自転車を発明しおこれらの機胜を自分で䜜成するこずもできたすが、既成のアむデアを䜿甚するこずをお勧めしたす。



画像凊理には、広く普及しおいるOpenCVラむブラリを䜿甚しおください 。 .netでは管理されおいないため、ラッパヌOpenCvSharpを介しお接続したす。



画像にさたざたな倉換を適甚し、背景ず圱をオブゞェクトから分離し、オブゞェクトを盞互に分離する倉換を遞択するには、OpenCVが必芁です。 今日はこの目暙に向かっお進みたす。




カバヌされおいるトピック  画像凊理甚のラむブラリの遞択、OpenCVを䜿甚するためのラッパヌの遞択、基本的なOpenCV関数、移動オブゞェクトの匷調衚瀺、HSVカラヌモデル。






なぜopencvですか

他の画像凊理甚の優れたラむブラリがありたす。あるOpenCVでは、光が収束したせんでした。



.netでプログラミングする堎合、 Accord.Netラむブラリおよびその以前のバヌゞョンのAForge.Net にも泚意を払う䟡倀がありたす。 これらの2぀のラむブラリも無料ですが、OpenCVずは異なり、.netプラットフォヌムのネむティブ管理察象です。



「楜しみのため」ではなく、販売甚の補品を開発するずきは、マネヌゞラむブラリに集䞭したいず思いたす。 適切なマネヌゞラむブラリを遞択しおもパフォヌマンスは䜎䞋したせんが、展開、移怍性、およびその埌のメンテナンスが倧幅に簡玠化されたす。 しかし、スタヌトアップや「楜しみのため」の開発では、将来のメンテナンスの簡玠化よりも倧きなコミュニティの方が重芁です。 そしお、それがOpenCVラむブラリに戻っおきたした。これは、はるかに広く知られおいたす。



倧きなコミュニティ
ラむブラリを独立しお遞択できるため、倧芏暡で掻発なコミュニティを持぀ラむブラリに焊点を圓おるこずは理にかなっおいたす。 倚数のフォロワヌが、高いテストラむブラリ、ごくわずかな䞍䟿さ、さたざたなドキュメントを提䟛し、䜜業䞭に発生する質問をGoogleで簡単に怜玢できるようにしたす。



OpenCVはこの論文を完党にサポヌトしおいたす。 本がありたす OpenCVを孊ぶ  正盎、ただ読んでいたせんが、近いうちに修正したす  stackoverflowで 。



このすべおの採甚により、「Pli 準備をしなさい 目的は「クラシック」ではなく、「ラむブラリを孊習しながら孊習する」こずです。 目指せ Pli」最初にラむブラリデバむスの予備知識にかなりの時間を費やしたずき。



Googleの質問
ほずんどのタスクは、クむックgoogleを䜿甚しお解決されたした。キヌワヌドは質問の件名でGoogleに入力され、回答は最初のリンクにすぐに衚瀺されたす。 自分で回答を怜玢する堎合は、目的のトピックを説明するために䜿甚されるキヌワヌドに泚意しおください。 Googleは質問の矎しさず文法的な正確さを気にしたせん;それは、キヌワヌドの存圚だけが重芁であり、それによっお答えを遞択するからです。



解決すべき問題の䟋

-画像からRコンポヌネントを遞択したす-Googleリク゚スト googleopencv get single channel 、最初のリンクは、これがSplit関数を䜿甚しお行われたこずを瀺しおいたす

-画像間の違いを芋぀ける googleopencv difference images 、最初の答えの䟋では、absdiff関数がこれを行うず述べおいたす。 怜玢時に差ではなく単語比范を䜿甚するず、Googleは完党に異なるペヌゞを衚瀺し始めたす。これにより、ヒストグラム比范などの䜿甚を掚奚する䞀般的な回答が埗られたす。 これは、質問ぞの回答を怜玢するずきにキヌワヌドを遞択するこずの重芁性を瀺しおいたす。



OpenCV甚の.net-wrapperの遞択

ラむブラリが遞択されたしたが、Cず友達になりたす。 この問題はすでに解決されおおり、䜿甚可胜なオプションを遞択するだけで枈みたす。 䞀般的なラッパヌには、Emmu CvずOpenCvSharpの 2぀がありたす。 Emgu Cvはより叀く、よりフォヌマルで、OpenCvSharpはよりモダンです。 遞択はOpenCvSharpに限定され、IDisposableがサポヌトされおいるずいう著者の蚀葉を埋めたした。 これは、䜜成者が構造ず関数をC / C ++からC1 in 1に転送するだけでなく、それらをファむルに远加しお、Cスタむルのコヌド蚘述で䜿甚する方が䟿利になるこずを意味したす。



OpenCvSharpをプロゞェクトに接続する
OpenCvSharpのプロゞェクトぞの接続は、特別な手間をかけずに暙準的な方法で行われたす。 著者からの小さなチュヌトリアルがありたす。たた、 nugetを介しおOpenCvSharpに接続する機胜もありたす。



基本的な基本機胜

OpenCVには、画像を操䜜するための倚くの機胜がありたす。 画像からオブゞェクトを抜出する問題を解決するために䜿甚される基本的な基本機胜に぀いおのみ説明したしょう。 OpenCVには、CスタむルずC ++スタむルの2぀の甚途がありたす。 コヌドを簡玠化するために、C ++スタむルたたは、OpenCvSharpを介したそのアナログを䜿甚したす。



䞻に2぀のクラスがありたすMatずCv2。 どちらもOpenCvSharp.CPlusPlusのネヌムスペヌスにありたす。 マットは画像そのものであり、Cv2は画像に察する䞀連のアクションです。

機胜

//  var mat = new Mat("test.bmp"); //  mat.ImWrite("out.bmp"); //  bitmap var bmp = mat.ToBitmap(); //  bitmap (  OpenCvSharp.Extensions) var mat2 = new Mat(bmp.ToIplImage(), true); //  using (new Window("", mat)) { Cv2.WaitKey(); } //   (,     ) Cv2.CvtColor(mat, dstMat, ColorConversion.RgbToGray); //      Cv2.Split(mat, out mat_channels) //      Cv2.Merge(mat_channels, mat) //    Cv2.Absdiff(mat1, mat2, dstMat); // ,  /  (50)  (0)   (255) Cv2.Threshold(mat, dstMat, 50, 255, OpenCvSharp.ThresholdType.Binary); //  mat.Circle(x, y, radius, new Scalar(b, g, r)); mat.Line(x1, y1, x2, y2, new CvScalar(b, g, r)); mat.Rectangle(new Rect(x, y, width, height), new Scalar(b, g, r)); mat.Rectangle(new Rect(x, y, width, height), new Scalar(b, g, r), -1); //  mat.PutText("test", new OpenCvSharp.CPlusPlus.Point(x, y), FontFace.HersheySimplex, 2, new Scalar(b, g, r))
      
      





OpenCVには、オブゞェクトを遞択するための特別な機胜 構造分析ず圢状蚘述子 、 モヌション分析ずオブゞェクト远跡 、 特城怜出 、 オブゞェクト怜出 もありたすが、それらを絞り出すこずに成功したせんでした ずにかく本を読む必芁がありたす 埌で䜿えるようにしおおきたす。



オブゞェクトを遞択

オブゞェクトを遞択する簡単な方法は、オブゞェクトから背景を切り離し、オブゞェクトを互いに切り離すフィルタヌを発明するこずです。 残念ながら、ズマフィヌルドの画像は非垞にカラフルで、単玔な茝床カットオフは機胜したせん。 以䞋は、元の画像、癜黒バヌゞョン、およびさたざたなカットオフのはしごです。 最埌の画像は、すべおの堎合に、背景がボヌルずマヌゞするか、䞡方が存圚するか、同時に存圚しないこずを瀺しおいたす。





オブゞェクトを遞択するず、「ストラむピング」が倧幅に耇雑になりたす。 たずえば、ここでは、Canny関数がどのように反応するかを瀺し、オブゞェクトの茪郭を匷調しおいたす。





個々の色成分を䜿甚しおも、生掻は良くなりたせん。







移動するオブゞェクトを匷調衚瀺する

移動するオブゞェクトを匷調衚瀺するための基瀎は簡単です。2぀のファむルが比范され、倉曎されたポむントが目的のオブゞェクトです。 実際には、すべおがより耇雑であり、悪魔はい぀ものように詳现にありたす...



画像シリヌズの圢成
䞀連の画像を䜜成するには、ボットに小さなコヌドを远加したす。 ボットは最埌のフレヌムの履歎を保持し、スペヌスキヌを抌しおディスクにリセットしたす。

  var history = new List<Bitmap>(); for (var tick = 0; ;tick++) { var bmp = GetScreenImage(gameScreenRect); history.Insert(0, bmp); const int maxHistoryLength = 10; if (history.Count > maxHistoryLength) history.RemoveRange(maxHistoryLength, history.Count - maxHistoryLength); if (Console.KeyAvailable) { var keyInfo = Console.ReadKey(); if (keyInfo.Key == ConsoleKey.Spacebar) { for (var i = 0; i < history.Count; ++i) history[i].Save(string.Format("{0}.png", i)); } [..] } [..] }
      
      







起動、クリック、そしお出来䞊がり-2぀のフレヌムが手元にありたす。





近隣の比范
ある画像から別の画像を匕きたす...そしお、いわゆる「山に混じった銬、人々」。 ボヌルは「奇劙な」ものに倉わりたしたこれはフルサむズの断片ではっきりず芋えたす、倧䞈倫です。䞻なこずは、ボヌルを背景から分離するこずができたずいうこずです。





特別に準備された背景ずの比范
隣接するフレヌムの比范は、「自立した」移動オブゞェクトに適しおいたす。 動きの激しいオブゞェクトを区別する必芁がある堎合は、特別に準備された静的な背景ずの比范が適切に機胜したす。

次の背景を準備したす。



比范する





はるかに良いですが、圱はすべおを台無しにしたす。 私は色々な方法で圱を取り陀こうずしたしたが、圱は実際には明るさの倉化であるずいう考えによっお最高の効果が埗られたした。これはすでにHSVカラヌモデルに぀いお考えるようになりたした。



Hsv

RGBのようなHSVカラヌモデルは、3぀のチャネルで構成されたす。 しかし、圌同じCMYKずは異なり、これは単なる色の混合ではありたせん。

-最初のチャネル、H色盞-色調。 最初の近䌌では、これは虹の色番号です。

-2番目のチャネル、S圩床-圩床。 このチャンネルの倀が䜎いほど、色がグレヌに近くなり、色がはっきりしたす。 圩床の高い色は、口語では「酞性」ず呌ばれおいたす。

-3番目のチャネル、V倀-明るさ。 これは最も理解しやすいチャネルであり、照床が倧きいほど、このチャネルの倀は高くなりたす。

右偎の図は、チャンネルず色の関係を瀺しおいたす。 円の䞭には虹がありたす-これはチャンネルHです。特定の色の䞉角圢珟圚は赀は、チャンネルSの倉化-圩床右䞊の方向ずチャンネルVの倉化-明るさ巊䞊を瀺しおいたす。 叀兞的に、チャンネルHの倀は0-360、S-0-100、V-0-100の範囲にありたす。 OpenCVでは、1バむトの次元を最倧限に掻甚するために、すべおのチャネルの倀が0〜255の範囲に削枛されたす。



RGBカラヌモデルは、人間の目に近い配眮になっおいたす。 HSVカラヌモデルは、脳がどのように色を知芚するかに近いです。 以䞋では、各チャンネルがプラス/マむナス50オりムに倉曎された堎合に䜕が起こるかに぀いおの䞀連の画像を具䜓的に匕甚したした。 チャンネルSずVを100単䜍およびこれは半分の範囲だけ倉曎した埌でも、画像はほが同じように知芚されたすが、チャンネルHを少し倉曎しおも知芚が倧きく倉化し、画像が「䞭毒性」になりたす。 これは、長幎にわたる進化の過皋で脳がより安定したデヌタを安定性の䜎いデヌタから分離するこずを孊んだずいう事実によるものです。



安定ずはどういう意味ですか これは、䞀郚の倖郚条件からほずんど倉化しない情報の䞀郚です。 実際のアむテム、たずえば硬いボヌルを取りたす。 独自の色がありたすが、この色の知芚は、照明、空気透過性、隣接するオブゞェクトの反射などの倖郚条件に応じお倉化したす。 したがっお、タスクが倖郚条件に関係なくボヌルを倖界から隔離するこずである堎合、倖郚条件からほずんど倉化しない情報のその郚分にもっず焊点を合わせ、最も倉化する郚分を無芖する必芁がありたす。 最も安定性が䜎いのは明るさチャンネルVです。それらは圱に移動し、呚囲の䞖界の明るさが倉化し、空が曇りたした-明るさが再び倉化したした。 圩床チャンネルSも1日を通しお倉化したす。より正確には色の倉化の知芚-照明が少ないほど、より倚くの錐䜓 癜黒の芖芚が寄䞎し、 ロッドからの情報が少なくなりたす色の芖芚。 色調チャンネルHは、オブゞェクトの色を最も匱く、最も安定しお反映したす。















hsv空間の背景ずの比范
静的な背景からの枛算を繰り返したすが、hsv空間ぞの倉換埌、ああ 奇跡 チャンネルHずSでは、ボヌルがシャドりから明確に分離されおおり、すべおのシャドりはほが完党にチャンネルVに入りたした。Hチャンネルでは、ボヌルの「くがみ」も消えたすが、残念ながら、黄色のボヌルは背景ず融合し始めたす。 粗さはSチャンネルに残りたすが、すべおのボヌルがはっきりず芋え、2色画像ぞの倉換「ゎミ」が25未満にカットされおいるにより、明確な円が埗られ、䞍芁なものがすべお削陀されたす。















たずめ
今日の目暙は達成されボヌルは背景ず圱から分離されおいたす、穏やかな魂で眠りに぀くこずができたす。



PS
衚瀺されるすべおの画像は、OpenCVカットの䞋のコヌドを䜿甚しお生成されたす。

非衚瀺のテキスト
  var resizeK = 0.2; var dir = "Example/"; var src = new Mat("0.bmp"); var src_g = new Mat("0.bmp", LoadMode.GrayScale); var src_1 = new Mat("1.bmp"); var src_1_g = new Mat("1.bmp", LoadMode.GrayScale); var background = new Mat("background.bmp"); var background_g = new Mat("background.bmp", LoadMode.GrayScale); src.Resize(resizeK).ImWrite(dir + "0.png"); src_g.Resize(resizeK).ImWrite(dir + "0 g.png"); src_g.ThresholdStairs().Resize(resizeK).ImWrite(dir + "0 g th.png"); var canny = new Mat(); Cv2.Canny(src_g, canny, 50, 200); canny.Resize(0.5).ImWrite(dir + "0 canny.png"); Mat[] src_channels; Cv2.Split(src, out src_channels); for (var i = 0; i < src_channels.Length; ++i) { var channels = Enumerable.Range(0, src_channels.Length).Select(j => new Mat(src_channels[0].Rows, src_channels[0].Cols, src_channels[0].Type())).ToArray(); channels[i] = src_channels[i]; var dst = new Mat(); Cv2.Merge(channels, dst); dst.Resize(resizeK).ImWrite(dir + string.Format("0 ch{0}.png", i)); src_channels[i].ThresholdStairs().Resize(resizeK).ImWrite(dir + string.Format("0 ch{0} th.png", i)); } if (true) { src.Resize(0.4).ImWrite(dir + "0.png"); src_1.Resize(0.4).ImWrite(dir + "1.png"); background.Resize(0.4).ImWrite(dir + "bg.png"); var dst_01 = new Mat(); Cv2.Absdiff(src, src_1, dst_01); dst_01.Resize(resizeK).ImWrite(dir + "01.png"); dst_01.Cut(new Rect(50, src.Height * 4 / 5 - 50, src.Width / 5, src.Height / 5)).ImWrite(dir + "01 part.png"); dst_01.Cut(new Rect(50, src.Height * 4 / 5 - 50, src.Width / 5, src.Height / 5)).CvtColor(ColorConversion.RgbToGray).ImWrite(dir + "01 g.png"); dst_01.CvtColor(ColorConversion.RgbToGray).ThresholdStairs().Resize(resizeK).ImWrite(dir + "01 g th.png"); var dst_01_g = new Mat(); Cv2.Absdiff(src_g, src_1_g, dst_01_g); dst_01_g.Cut(new Rect(50, src.Height * 4 / 5 - 50, src.Width / 5, src.Height / 5)).ImWrite(dir + "0g1g.png"); dst_01_g.ThresholdStairs().Resize(resizeK).ImWrite(dir + "0g1g th.png"); } if (true) { var dst_0b = new Mat(); Cv2.Absdiff(src, background, dst_0b); dst_0b.Resize(0.6).ImWrite(dir + "0b.png"); var dst_0b_g = new Mat(); Cv2.Absdiff(src_g, background_g, dst_0b_g); dst_0b_g.Resize(0.3).ImWrite(dir + "0b g.png"); dst_0b_g.ThresholdStairs().Resize(0.3).ImWrite(dir + "0b g th.png"); } if (true) { var hsv_src = new Mat(); Cv2.CvtColor(src, hsv_src, ColorConversion.RgbToHsv); var hsv_background = new Mat(); Cv2.CvtColor(background, hsv_background, ColorConversion.RgbToHsv); var hsv_background_channels = hsv_background.Split(); var hsv_src_channels = hsv_src.Split(); if (true) { var all = new Mat(src.ToIplImage(), true); for (var i = 0; i < hsv_src_channels.Length; ++i) { hsv_src_channels[i].CvtColor(ColorConversion.GrayToRgb).CopyTo(all, new Rect(i * src.Width / 3, src.Height / 2, src.Width / 3, src.Height / 2)); } src_g.CvtColor(ColorConversion.GrayToRgb).CopyTo(all, new Rect(src.Width / 2, 0, src.Width / 2, src.Height / 2)); all.Resize(0.3).ImWrite(dir + "all.png"); } foreach (var pair in new[] { "h", "s", "v" }.Select((channel, index) => new { channel, index })) { var diff = new Mat(); Cv2.Absdiff(hsv_src_channels[pair.index], hsv_background_channels[pair.index], diff); diff.Resize(0.3).With_Title(pair.channel).ImWrite(dir + string.Format("0b {0}.png", pair.channel)); diff.ThresholdStairs().Resize(0.3).ImWrite(dir + string.Format("0b {0} th.png", pair.channel)); hsv_src_channels[pair.index].Resize(resizeK).With_Title(pair.channel).ImWrite(dir + string.Format("0 {0}.png", pair.channel)); foreach (var d in new[] { -100, -50, 50, 100 }) { var delta = new Mat(hsv_src_channels[pair.index].ToIplImage(), true); delta.Rectangle(new Rect(0, 0, delta.Width, delta.Height), new Scalar(Math.Abs(d)), -1); var new_channel = new Mat(); if (d >= 0) Cv2.Add(hsv_src_channels[pair.index], delta, new_channel); else Cv2.Subtract(hsv_src_channels[pair.index], delta, new_channel); var new_hsv = new Mat(); Cv2.Merge(hsv_src_channels.Select((channel, index) => index == pair.index ? new_channel : channel).ToArray(), new_hsv); var res = new Mat(); Cv2.CvtColor(new_hsv, res, ColorConversion.HsvToRgb); res.Resize(resizeK).With_Title(string.Format("{0} {1:+#;-#}", pair.channel, d)).ImWrite(dir + string.Format("0 {0}{1}.png", pair.channel, d)); } } } static class OpenCvHlp { public static Scalar ToScalar(this Color color) { return new Scalar(color.B, color.G, color.R); } public static void CopyTo(this Mat src, Mat dst, Rect rect) { var mask = new Mat(src.Rows, src.Cols, MatType.CV_8UC1); mask.Rectangle(rect, new Scalar(255), -1); src.CopyTo(dst, mask); } public static Mat Absdiff(this Mat src, Mat src2) { var dst = new Mat(); Cv2.Absdiff(src, src2, dst); return dst; } public static Mat CvtColor(this Mat src, ColorConversion code) { var dst = new Mat(); Cv2.CvtColor(src, dst, code); return dst; } public static Mat Threshold(this Mat src, double thresh, double maxval, ThresholdType type) { var dst = new Mat(); Cv2.Threshold(src, dst, thresh, maxval, type); return dst; } public static Mat ThresholdStairs(this Mat src) { var dst = new Mat(src.Rows, src.Cols, src.Type()); var partCount = 10; var partWidth = src.Width / partCount; for (var i = 0; i < partCount; ++i) { var th_mat = new Mat(); Cv2.Threshold(src, th_mat, 255 / 10 * (i + 1), 255, ThresholdType.Binary); th_mat.Rectangle(new Rect(0, 0, partWidth * i, src.Height), new Scalar(0), -1); th_mat.Rectangle(new Rect(partWidth * (i + 1), 0, src.Width - partWidth * (i + 1), src.Height), new Scalar(0), -1); Cv2.Add(dst, th_mat, dst); } var color_dst = new Mat(); Cv2.CvtColor(dst, color_dst, ColorConversion.GrayToRgb); for (var i = 0; i < partCount; ++i) { color_dst.Line(partWidth * i, 0, partWidth * i, src.Height, new CvScalar(50, 200, 50), thickness: 2); } return color_dst; } public static Mat With_Title(this Mat mat, string text) { var res = new Mat(mat.ToIplImage(), true); res.Rectangle(new Rect(res.Width / 2 - 10, 30, 20 + text.Length * 15, 25), new Scalar(0), -1); res.PutText(text, new OpenCvSharp.CPlusPlus.Point(res.Width / 2, 50), FontFace.HersheyComplex, 0.7, new Scalar(150, 200, 150)); return res; } public static Mat Resize(this Mat src, double k) { var dst = new Mat(); Cv2.Resize(src, dst, new OpenCvSharp.CPlusPlus.Size((int)(src.Width * k), (int)(src.Height * k))); return dst; } public static Mat Cut(this Mat src, Rect rect) { return new Mat(src, rect); } public static Mat[] Split(this Mat hsv_background) { Mat[] hsv_background_channels; Cv2.Split(hsv_background, out hsv_background_channels); return hsv_background_channels; } }
      
      








DirectXアヌケヌド甚のボット。 郚品番号1連絡する

アヌケヌド甚のボット。 郚品番号2OpenCVを接続したす



All Articles