Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
ARコンテンツ作成勉強会
C#ではじめようOpenCV
#AR_Fukuoka
今回の内容 (for Slide Share)
OpenCVを使ったカラートラッキングをやります
ただし・・・
画像処理プログラミングの基礎も勉強するため
ストレートに目的コンテンツには到達しません
あらかじめご了承ください
本日作成するコンテンツ
https://youtu.be/vnLiZFIe09I
Visual Studioを起動
プロジェクトを作成 (1/2)
①File
②New
③Project
プロジェクトを作成 (2/2)
①VisualC#
②Windows
③Windows Form Application
④OK
動作確認
Start
OpenCvSharpの導入 (1/4)
①Tools
②NuGet Package Manager
③Manage Nuget Package...
OpenCvSharpの導入 (2/4)
①Brows ②nuget.org
OpenCvSharpの導入 (3/4)
①OpenCVsharp3
②OpenCvSharp3 AnyCPU
③パッケージのチェックをON
④3.2.0.20170324
⑤Install
OpenCvSharpの導入 (4/4)
NuGetを閉じる
画像表示領域を作成 (1/5)
①ToolBoxタブ
②PictureBox
画像表示領域を作成 (2/5)
フォーム内をクリック
画像表示領域を作成 (3/5)
①PictureBoxをクリック
②Sizeを400,300
(なんでもOK)
画像表示領域を作成 (4/5)
BackgroundImageLayoutを
Zoomに変更
Zoomにするとアスペクト比を保持したままPictureBoxのサイズに合わせて画像を拡大・縮小
画像表示領域を作成 (5/5)
Formを選択してサイズを
PictureBoxに合わせる
Formのサイズはあとで変えるから適当でOK
Timerを用いた更新周期の設定
Timerを使うと任意に設定した間隔で処理(画像の取得・表示)を実行させられる
①ToolBoxタブ
②Timer
Timerを用いた更新周期の設定
①Form上をクリック
②Timerが追加される
➂Intervalを30[ms]
に変更(なんでもOK)
一定時間毎に呼び出される関数を作成
①Timerをダブルクリック
一定時間毎に呼び出される関数を作成
timer1_Tick関数が追加される
一定時間沖にtimer1_Tick関数の内部に書いた処理が実行される(予定)
Formが表示されたらTimerをスタートさせる
①Form1[Design]
②Form1をクリック
Formが表示されたらTimerをスタートさせる
①⚡をクリック
②Shownをダブルクリック
Formが表示されたらTimerをスタートさせる
namespace CVTest
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
...
Webカメラの画像を取得
using OpenCvSharp; //OpenCvSharpの機能をインポート
using OpenCvSharp.Extensions;
namespace CVTest
{
public partial cla...
Webカメラの画像を取得
VideoCapture video = new VideoCapture(0);;
Mat bgr;
public Form1()
{
InitializeComponent();
}
private void ti...
画像を左右反転させよう
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
pi...
画像処理の過程を表示させよう
横方向に広げる
(大体2倍くらい)
画像処理の過程を表示させよう
PictureBoxをコピペ
pictureBox1 pictureBox2
画像処理用の画像を作成
VideoCapture video;
Mat bgr;
Mat src; //画像処理に用いる画像を保持
public Form1(){ /*省略*/ }
private void timer1_Tick(object...
動作確認
OpenCVに慣れよう:グレースケール変換
Mat bgr;
Mat src; //画像処理に用いる画像を保持
public Form1(){ /*省略*/ }
private void timer1_Tick(object sender, E...
動作確認
グレースケール画像を二値化してみよう
0 255
0 255
0~255の輝度値を、ある値(しきい値)以上か未満かで分離することを二値化と呼ぶ。
画像処理すべき領域か否かをはっきり分けることができるため非常に重要なテクニック。
二値化の下準備
dstを表示
Mat src; //画像処理に用いる画像を保持
Mat dst; //二値化結果を保持
private void timer1_Tick(object sender, EventArgs e)
{
bgr = v...
二値化(ImageProcessing関数内)
src = bgr.CvtColor(ColorConversionCodes.BGR2GRAY);
dst = new Mat(bgr.Height, bgr.Width, MatType.CV...
動作確認
GUIでしきい値(Threshold)を変化させよう
Trackbar
GUIでしきい値(Threshold)を変化させよう
Trackbarを配置
GUIでしきい値(Threshold)を変化させよう
① Trackbarをクリックして
Propertiesを選択
③Maximumを255
TrackBarで入力できる値を0~255に設定
② をクリック
GUIでしきい値(Threshold)を変化させよう
①⚡をクリック
②Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
GUIでしきい値(Threshold)を変化させよう
Mat src;
Mat dst;
int th1;
/*省略*/
private void ImageProcessing ()
{
/*省略*/
for(int y = 0; y < s...
複数のしきい値を設定しよう
Trackbarをコピペ
複数のしきい値を設定しよう
① 2つめのTrackbarを
クリックしてPropertiesを選択
③Valueを255
2つめのTrackBarのデフォルト値を255に変更
② をクリック
複数のしきい値を設定しよう
①⚡をクリック
②Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
複数のしきい値を設定しよう
int th1;
int th2 = 255;
/*省略*/
private void ImageProcessing (){
/*省略*/
for(int y = 0; y < src.Height; y++){
...
動作確認
色の抽出をしよう
【問題点】
 GrayScaleの場合は1変数なので抽出が容易
 色をR(赤)G(緑)B(青)での組み合わせで表現
した場合、特定の色の抽出はちょっと面倒。
 RGBでは色の境目を直線的に引くことも困難
【解決策】
 ...
色検出用の関数を作成
private void timer1_Tick(object sender, EventArgs e)
{
bgr = video.RetrieveMat();
bgr=bgr.Flip(FlipMode.Y);
Ima...
HSV変換と指定範囲の二値化
private void ImageProcessing2()
{
src = bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = new Mat(bgr.Heigh...
抽出したい色のHueの確認
private void ImageProcessing2()
{
src = bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = new Mat(bgr.Height...
彩度S(Saturation)の設定もしよう
1つめのTrackbarをコピペ
彩度S(Saturation)の設定もしよう
②⚡をクリック
③Scrollをダブルクリック
TrackBarをスクロールしたときの挙動を記述する関数を生成
① 3つめのTrackbarを
右クリックしてPropertiesを選択
複数のしきい値を設定しよう
int th2 = 255;
int th3;
/*省略*/
private void ImageProcessing2 (){
/*省略*/
for(int y = 0; y < src.Height; y++){...
抽出したい色のSaturationの確認
private void ImageProcessing2()
{
src = bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = new Mat(bgr...
動作確認
ノイズが残ることもある
Erodeを用いたノイズ除去
private void ImageProcessing2()
{
src = bgr.CvtColor(ColorConversionCodes.BGR2HSV);
dst = new Mat(bgr.Heigh...
白画素の中から面積最大のもの(Blob)を探す
private void ImageProcessing2()
{
/*省略(HSV変換から二値化まで)*/
dst = dst.Erode(null, null, 2);
ConnectedCo...
動作確認
円がついてくるはず
線を描く
List<OpenCvSharp.Point2d> list = new List<OpenCvSharp.Point2d>();
private void ImageProcessing2(){
/*省略(HSV変換から二値化、ノイ...
完成
Upcoming SlideShare
Loading in …5
×

ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)

11,000 views

Published on

https://eventon.jp/6890/

Published in: Technology
  • If you’re struggling with your assignments like me, check out ⇒ www.HelpWriting.net ⇐.
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Hi there! I just wanted to share a list of sites that helped me a lot during my studies: .................................................................................................................................... www.EssayWrite.best - Write an essay .................................................................................................................................... www.LitReview.xyz - Summary of books .................................................................................................................................... www.Coursework.best - Online coursework .................................................................................................................................... www.Dissertations.me - proquest dissertations .................................................................................................................................... www.ReMovie.club - Movies reviews .................................................................................................................................... www.WebSlides.vip - Best powerpoint presentations .................................................................................................................................... www.WritePaper.info - Write a research paper .................................................................................................................................... www.EddyHelp.com - Homework help online .................................................................................................................................... www.MyResumeHelp.net - Professional resume writing service .................................................................................................................................. www.HelpWriting.net - Help with writing any papers ......................................................................................................................................... Save so as not to lose
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Sex in your area is here: ❤❤❤ http://bit.ly/36cXjBY ❤❤❤
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
  • Dating direct: ❶❶❶ http://bit.ly/36cXjBY ❶❶❶
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

ARコンテンツ作成勉強会:C#ではじめようOpenCV(カラートラッキング編)

  1. 1. ARコンテンツ作成勉強会 C#ではじめようOpenCV #AR_Fukuoka
  2. 2. 今回の内容 (for Slide Share) OpenCVを使ったカラートラッキングをやります ただし・・・ 画像処理プログラミングの基礎も勉強するため ストレートに目的コンテンツには到達しません あらかじめご了承ください
  3. 3. 本日作成するコンテンツ https://youtu.be/vnLiZFIe09I
  4. 4. Visual Studioを起動
  5. 5. プロジェクトを作成 (1/2) ①File ②New ③Project
  6. 6. プロジェクトを作成 (2/2) ①VisualC# ②Windows ③Windows Form Application ④OK
  7. 7. 動作確認 Start
  8. 8. OpenCvSharpの導入 (1/4) ①Tools ②NuGet Package Manager ③Manage Nuget Package...
  9. 9. OpenCvSharpの導入 (2/4) ①Brows ②nuget.org
  10. 10. OpenCvSharpの導入 (3/4) ①OpenCVsharp3 ②OpenCvSharp3 AnyCPU ③パッケージのチェックをON ④3.2.0.20170324 ⑤Install
  11. 11. OpenCvSharpの導入 (4/4) NuGetを閉じる
  12. 12. 画像表示領域を作成 (1/5) ①ToolBoxタブ ②PictureBox
  13. 13. 画像表示領域を作成 (2/5) フォーム内をクリック
  14. 14. 画像表示領域を作成 (3/5) ①PictureBoxをクリック ②Sizeを400,300 (なんでもOK)
  15. 15. 画像表示領域を作成 (4/5) BackgroundImageLayoutを Zoomに変更 Zoomにするとアスペクト比を保持したままPictureBoxのサイズに合わせて画像を拡大・縮小
  16. 16. 画像表示領域を作成 (5/5) Formを選択してサイズを PictureBoxに合わせる Formのサイズはあとで変えるから適当でOK
  17. 17. Timerを用いた更新周期の設定 Timerを使うと任意に設定した間隔で処理(画像の取得・表示)を実行させられる ①ToolBoxタブ ②Timer
  18. 18. Timerを用いた更新周期の設定 ①Form上をクリック ②Timerが追加される ➂Intervalを30[ms] に変更(なんでもOK)
  19. 19. 一定時間毎に呼び出される関数を作成 ①Timerをダブルクリック
  20. 20. 一定時間毎に呼び出される関数を作成 timer1_Tick関数が追加される 一定時間沖にtimer1_Tick関数の内部に書いた処理が実行される(予定)
  21. 21. Formが表示されたらTimerをスタートさせる ①Form1[Design] ②Form1をクリック
  22. 22. Formが表示されたらTimerをスタートさせる ①⚡をクリック ②Shownをダブルクリック
  23. 23. Formが表示されたらTimerをスタートさせる namespace CVTest { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void timer1_Tick(object sender, EventArgs e) { } private void Form1_Shown(object sender, EventArgs e) { timer1.Start(); //タイマーの動作を開始 } } } ①Form1_Shownが追加される ②自分で追加
  24. 24. Webカメラの画像を取得 using OpenCvSharp; //OpenCvSharpの機能をインポート using OpenCvSharp.Extensions; namespace CVTest { public partial class Form1 : Form { VideoCapture video = new VideoCapture(0); //Videoキャプチャ Mat bgr; //カラー画像を扱うクラス public Form1() { InitializeComponent(); } private void timer1_Tick(object sender, EventArgs e) { } /*****以下省略*****/
  25. 25. Webカメラの画像を取得 VideoCapture video = new VideoCapture(0);; Mat bgr; public Form1() { InitializeComponent(); } private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); //カメラから画像を取得しbgrに記録 pictureBox1.BackgroundImage = bgr.ToBitmap(); bgr.Release(); //画像を記録したメモリを開放 } private void Form1_Shown(object sender, EventArgs e) { timer1.Start(); }
  26. 26. 画像を左右反転させよう private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); pictureBox1.BackgroundImage = bgr.ToBitmap(); bgr.Release(); } x y Flip
  27. 27. 画像処理の過程を表示させよう 横方向に広げる (大体2倍くらい)
  28. 28. 画像処理の過程を表示させよう PictureBoxをコピペ pictureBox1 pictureBox2
  29. 29. 画像処理用の画像を作成 VideoCapture video; Mat bgr; Mat src; //画像処理に用いる画像を保持 public Form1(){ /*省略*/ } private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = src.ToBitmap(); bgr.Release(); src.Release(); } //以下の関数に画像処理のコードを書いていく private void ImageProcessing() { src = bgr.Clone(); }
  30. 30. 動作確認
  31. 31. OpenCVに慣れよう:グレースケール変換 Mat bgr; Mat src; //画像処理に用いる画像を保持 public Form1(){ /*省略*/ } private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = src.ToBitmap(); bgr.Release(); src.Release(); } private void ImageProcessing() { src = bgr.Clone(); src = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); } CvtColorで画像変換
  32. 32. 動作確認
  33. 33. グレースケール画像を二値化してみよう 0 255 0 255 0~255の輝度値を、ある値(しきい値)以上か未満かで分離することを二値化と呼ぶ。 画像処理すべき領域か否かをはっきり分けることができるため非常に重要なテクニック。
  34. 34. 二値化の下準備 dstを表示 Mat src; //画像処理に用いる画像を保持 Mat dst; //二値化結果を保持 private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = dst.ToBitmap(); bgr.Release(); src.Release(); dst.Release(); } private void ImageProcessing() { src = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); } 符号なし8bit(0~255)
  35. 35. 二値化(ImageProcessing関数内) src = bgr.CvtColor(ColorConversionCodes.BGR2GRAY); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); MatOfByte b_src = new MatOfByte(src); MatOfByte b_dst = new MatOfByte(dst); var index_src = b_src.GetIndexer(); var index_dst = b_dst.GetIndexer(); for(int y = 0; y < src.Height; y++) { for(int x = 0; x < src.Width; x++) { if (index_src[y, x] >= 100) { index_dst[y, x] = 255; } else { index_dst[y, x] = 0; } } } 追加
  36. 36. 動作確認
  37. 37. GUIでしきい値(Threshold)を変化させよう Trackbar
  38. 38. GUIでしきい値(Threshold)を変化させよう Trackbarを配置
  39. 39. GUIでしきい値(Threshold)を変化させよう ① Trackbarをクリックして Propertiesを選択 ③Maximumを255 TrackBarで入力できる値を0~255に設定 ② をクリック
  40. 40. GUIでしきい値(Threshold)を変化させよう ①⚡をクリック ②Scrollをダブルクリック TrackBarをスクロールしたときの挙動を記述する関数を生成
  41. 41. GUIでしきい値(Threshold)を変化させよう Mat src; Mat dst; int th1; /*省略*/ private void ImageProcessing () { /*省略*/ for(int y = 0; y < src.Height; y++){ for(int x = 0; x < src.Width; x++){ if (index_src[y, x] >= th1){ index_dst[y, x] = 255; } else{ index_dst[y, x] = 0; } } } } private void trackBar1_Scroll(object sender, EventArgs e) { th1 = trackBar1.Value; } しきい値をth1に変更 trackBar1_Scroll関数が追加される
  42. 42. 複数のしきい値を設定しよう Trackbarをコピペ
  43. 43. 複数のしきい値を設定しよう ① 2つめのTrackbarを クリックしてPropertiesを選択 ③Valueを255 2つめのTrackBarのデフォルト値を255に変更 ② をクリック
  44. 44. 複数のしきい値を設定しよう ①⚡をクリック ②Scrollをダブルクリック TrackBarをスクロールしたときの挙動を記述する関数を生成
  45. 45. 複数のしきい値を設定しよう int th1; int th2 = 255; /*省略*/ private void ImageProcessing (){ /*省略*/ for(int y = 0; y < src.Height; y++){ for(int x = 0; x < src.Width; x++){ if (index_src[y, x] >= th1 && index_src[y, x] <= th2){ index_dst[y, x] = 255; } else{ index_dst[y, x] = 0; } } } } /*省略*/ private void trackBar2_Scroll(object sender, EventArgs e){ th2 = trackBar2.Value; } 2つめのしきい値を設定 trackBar2_Scroll関数が追加される
  46. 46. 動作確認
  47. 47. 色の抽出をしよう 【問題点】  GrayScaleの場合は1変数なので抽出が容易  色をR(赤)G(緑)B(青)での組み合わせで表現 した場合、特定の色の抽出はちょっと面倒。  RGBでは色の境目を直線的に引くことも困難 【解決策】  色空間としてHSVを使用する  H(Hue)は色相で0~360°で表現される ※OpenCVの場合0~180  S(Saturation)は彩度、0~100%で表現。 値が大きいほうがその色らしさが強い。 ※OpenCVの場合0~255  V(Value)は明度、 0~100%で表現。 値が大きいほど明るい ※OpenCVの場合0~255 GrayScale RGB HSV
  48. 48. 色検出用の関数を作成 private void timer1_Tick(object sender, EventArgs e) { bgr = video.RetrieveMat(); bgr=bgr.Flip(FlipMode.Y); ImageProcessing2(); pictureBox1.BackgroundImage = bgr.ToBitmap(); pictureBox2.BackgroundImage = dst.ToBitmap(); bgr.Release(); src.Release(); dst.Release(); } private void ImageProcessing() { /*二値化処理*/ } private void ImageProcessing2() { /*二値化処理*/ } GrayScaleの二値化を行うImageProcessing関数をサンプルとして取っておく場合 ②ImageProcessing2に変更 ①コピペした後、関数名を ImageProcessing2に変更
  49. 49. HSV変換と指定範囲の二値化 private void ImageProcessing2() { src = bgr.CvtColor(ColorConversionCodes.BGR2HSV); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); MatOfByte3 b_src = new MatOfByte3(src); MatOfByte b_dst = new MatOfByte(dst); var index_src = b_src.GetIndexer(); var index_dst = b_dst.GetIndexer(); for (int y = 0; y < src.Height; y++) { for (int x = 0; x < src.Width; x++) { if (index_src[y, x][0] >= th1 && index_src[y, x][0] <= th2) { index_dst[y, x] = 255; } else { index_dst[y, x] = 0; } /*以下省略*/ ①変換方法を変更 ②MatOfByte3に変更 ③ index_src[y, x][0]
  50. 50. 抽出したい色のHueの確認 private void ImageProcessing2() { src = bgr.CvtColor(ColorConversionCodes.BGR2HSV); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); MatOfByte3 b_src = new MatOfByte3(src); MatOfByte b_dst = new MatOfByte(dst); /*省略(二値化処理など)*/ this.Text = th1 * 2 + “~” + th2 * 2; //Hueの範囲を表示 } 抽出したい色に 関するHueの範囲を メモしておこう
  51. 51. 彩度S(Saturation)の設定もしよう 1つめのTrackbarをコピペ
  52. 52. 彩度S(Saturation)の設定もしよう ②⚡をクリック ③Scrollをダブルクリック TrackBarをスクロールしたときの挙動を記述する関数を生成 ① 3つめのTrackbarを 右クリックしてPropertiesを選択
  53. 53. 複数のしきい値を設定しよう int th2 = 255; int th3; /*省略*/ private void ImageProcessing2 (){ /*省略*/ for(int y = 0; y < src.Height; y++){ for(int x = 0; x < src.Width; x++){ if (index_src[y, x][0] >= th1 && index_src[y, x][0] <= th2 && index_src[y, x][1] >= th3){ index_dst[y, x] = 255; } else{ index_dst[y, x] = 0; } } } } /*省略*/ private void trackBar3_Scroll(object sender, EventArgs e){ th3 = trackBar3.Value; } 3つめのしきい値を設定 trackBar3_Scroll関数が追加される
  54. 54. 抽出したい色のSaturationの確認 private void ImageProcessing2() { src = bgr.CvtColor(ColorConversionCodes.BGR2HSV); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); MatOfByte3 b_src = new MatOfByte3(src); MatOfByte b_dst = new MatOfByte(dst); /*省略(二値化処理など)*/ this.Text = th1 * 2 + "~" + th2 * 2 + ": " + th3; } 抽出したい色に 関するSの値を メモしておこう
  55. 55. 動作確認 ノイズが残ることもある
  56. 56. Erodeを用いたノイズ除去 private void ImageProcessing2() { src = bgr.CvtColor(ColorConversionCodes.BGR2HSV); dst = new Mat(bgr.Height, bgr.Width, MatType.CV_8U); /*省略(二値化処理など)*/ dst = dst.Erode(null,null,2); this.Text = th1 * 2 + "~" + th2 * 2+": "+th3; } 白画素の縮小を2回行う http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/index.html 成功例 やり過ぎ例
  57. 57. 白画素の中から面積最大のもの(Blob)を探す private void ImageProcessing2() { /*省略(HSV変換から二値化まで)*/ dst = dst.Erode(null, null, 2); ConnectedComponents cc = Cv2.ConnectedComponentsEx(dst); if (cc.Blobs.Count > 0) //0で落ちる場合は1に変更 { ConnectedComponents.Blob blob = cc.GetLargestBlob(); if (blob.Area > 2000) { bgr.Circle(blob.Centroid, 30, Scalar.Cyan, 4); } } this.Text = th1 * 2 + "~" + th2 * 2+": "+th3; } 追 加
  58. 58. 動作確認 円がついてくるはず
  59. 59. 線を描く List<OpenCvSharp.Point2d> list = new List<OpenCvSharp.Point2d>(); private void ImageProcessing2(){ /*省略(HSV変換から二値化、ノイズ除去まで)*/ ConnectedComponents cc = Cv2.ConnectedComponentsEx(dst); if (cc.Blobs.Count > 0){ ConnectedComponents.Blob blob = cc.GetLargestBlob(); if (blob.Area > 2000){ list.Add(blob.Centroid); for (int i = 1; i < list.Count; i++) { bgr.Line(list[i - 1], list[i], Scalar.Red, 3); } if (list.Count > 20) { list.RemoveAt(0); } } } this.Text = th1 * 2 + "~" + th2 * 2+": "+th3; } 追 加
  60. 60. 完成

×