继之前分享的一篇《Robotics Studio学习教程:第二十天——开始动手写相扑机器人》, 我们将继续分享下一篇《Robotics Studio学习教程:第二十一天——相扑机器人可以看到什么?》,让我继续开始我们学习Visual Programming Language,以及使用Robotics Studio学习开发机器人应用的道路吧。
[Robotics Studio] Sumo[III] - 揪~竟...相扑机器人看到了啥? -- Day21
每次发生了不思议的悬案时, 都是会暂停画面 (或是缩小为子画面), 然后跳出一位盛竹如 : "揪~竟..."
让我们把时间倒回到机器人被推倒之前, 我也很想对机器人问说 "揪~竟...你看到啥?"
就让我们动手把事实真相展露在世人面前...
(其实如果你在程序执行的时候打开 http://localhost:50000/controlpanel , 参照之前的 DSS in WebInterface , 你其实可以看到一些东西, PS : Simulated Webcam 的 Web 接口只支持 IE, 不支持 firefox )
还记得之前的 Microsoft.Ccr.Adapters.WinForms 吗, 有了这东东, 我们随时可以在 Windows 当中开出窗口, 显示我们想要看的东西.
所以先把这个 dll 参考到 mysumo 的项目当中, 接着, 新增一个 WinForm , 就叫 CameraImage 好了, 上面放个 PictureBox , 就用预设的名称 pictureBox1 , 大小至少要超过 176 x 144 (从 Simulated Webcam 偷看来的大小).
在 using 当中加入:
- using Microsoft.Robotics.Services.mysumo;
- using webcam = Microsoft.Robotics.Services.WebCam.Proxy;
- using System.Drawing.Imaging;
然后我们就可以加入一个 UpdateImage 的函式:
- /// <summary>
- /// 更新图像而且画出影像辨识结果
- /// </summary>
- /// <param name="cameraFrame"></param>
- /// <param name="result"></param>
- public void UpdateImage(webcam.QueryFrameResponse cameraFrame, ImageProcessResult result)
- {
- System.Drawing.Bitmap img =
- new System.Drawing.Bitmap(cameraFrame.Size.Width,
- cameraFrame.Size.Height,
- PixelFormat.Format24bppRgb);
- System.Drawing.Imaging.BitmapData bmd =
- img.LockBits( new System.Drawing.Rectangle(0, 0, img.Width, img.Height),
- System.Drawing.Imaging.ImageLockMode.WriteOnly,
- System.Drawing.Imaging.PixelFormat.Format24bppRgb);
- System.Runtime.InteropServices.Marshal.Copy(cameraFrame.Frame, 0,
- bmd.Scan0,
- img.Width * img.Height * 3);
- img.UnlockBits(bmd);
- using (Graphics g = Graphics.FromImage(img))
- {
- g.FillRectangle(Brushes.Red, new Rectangle(result.XMean - 10, result.YMean - 10, 20, 20));
- }
- pictureBox1.Image = img;
- }
这个函式的内容就是把 cameraFrame 所传进来的 data, 转成 bitmap, 然后在上面画一个 20x20 的红色正方形, 标示 ImageProcessResult 的结果,最后再把它设定给 pictureBox1.Image。
对了, 因为这个项目预设会产生 XML 说明文件, 而且又把所有警告视为错误, 所以你必须要将 public 的 class , function, property 都加上批注, 看你是要改项目设定还是加上批注 (提醒你在 class, function, property 的上一行输入 /// , 就会自动加上批注), 都可以解决问题.
接着在 mysumo.cs , 先新增一个 cameraimage property , 如下:
- private CameraImage cameraform;
你可以对 CameraImage 右键单击选解析, 会自动帮你加上 using Robotics.mysumo;
然后在 Start() 当中就可以加入 :
- WinFormsServicePort.Post(new RunForm(() => cameraform = new CameraImage()));
当然, RunForm 也可以靠自动解析产生 using Microsoft.Ccr.Adapters.WinForms; 最后, 我们就可以修改 ValidateFrameHandler 这个函式, 加入一行就可以, 如下:
- // Ignore old images!
- if (msFrame < 1000.0)
- {
- result = ProcessImage(cameraFrame.Size.Width,
- cameraFrame.Size.Height,
- cameraFrame.Frame);
- WinFormsServicePort.FormInvoke(() => cameraform.UpdateImage(cameraFrame, result));
- // 加這行!
- double msProcessing = DateTime.Now.Subtract(begin).TotalMilliseconds;
- LogVerbose(LogGroups.Console, string.Format("{0} Frame {1} ms Processed {2} ms",
- begin, msFrame, msProcessing));
- if (result != null)
- {
- result.TimeStamp = cameraFrame.TimeStamp;
- HandleProcessedImage(result);
- }
- else
- {
- LogVerbose(LogGroups.Console, "Skipping empty camera frame!");
- }
- }
在 ProcessImage 之后, 我们就可以更新画面到 cameraform。
另外提一点, 如果你发生问题, 有可能是一个 bug 造成的, 因为我们加了 WinForm, 会导致该 Dll 多了一个 resource, 而原本的 GetEmbedded() 函式是这样:
- /// <summary>
- /// Find the embedded file with the specified name and return a stream to read its contents
- /// </summary>
- /// <returns></returns>
- private Stream GetEmbeddedFile()
- {
- try
- {
- System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
- string[] names = asm.GetManifestResourceNames();
- if(names.Length > 0)
- return asm.GetManifestResourceStream(names[0]);
- return null;
- }
- catch
- {
- return null;
- }
- }
它其实只传回第一个 resource, 但那不一定是 PlayerImage.bmp (有可能是我们加入的 CameraImage form) , 所以建议改为:
- /// <summary>
- /// Find the embedded file with the specified name and return a stream to read its contents
- /// </summary>
- /// <returns></returns>
- private Stream GetEmbeddedFile()
- {
- try
- {
- System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
- List<string> lstnames = new List<string>(asm.GetManifestResourceNames());
- string bmpname = lstnames.Find(n => n.EndsWith("playerimage.bmp",
- StringComparison.InvariantCultureIgnoreCase));
- if(bmpname != null)
- return asm.GetManifestResourceStream(bmpname);
- return null;
- }
- catch
- {
- return null;
- }
- }
这样才会找到 playerimage.bmp 这个资源. (其实我比较建议用项目的内容去产生资源文件, 这样 vs2008 都会帮你搞定好存取资源相关的程序, 简单又不会出包...)
欧欧, 还有, 写程序养成归还资源的好习惯, 所以在 DropHandler 当中加入:
- if (cameraform != null)
- {
- WinFormsServicePort.FormInvoke(() =>
- {
- cameraform.Dispose();
- cameraform = null;
- });
- }
这样就可以在机器人被移除的当下, 关掉 cameraform.
最后启动 SimulatedSumoReferee, 加入 mysumo 就可以看到画面啦:
在比赛的过程中, 你就会知道机器人看到啥, 而且还会知道影像辨识最后认定敌人的位置...
最后还是要学盛竹如:
李组长皱了皱眉知道这案情必定不单纯...
难道冥冥之中, mysumo 相扑机器人注定要打赢这场仗?
揪~竟, mysumo 会不会得到最终的胜利呢, 我们下回分晓...
让我们继续一下章教程:
《Robotics Studio学习教程:第二十二天——让相扑机器人动起来!》