[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 會不會得到最終的勝利呢, 我們下回分曉...