找回密码
 立即注册
首页 业界区 业界 通过COM组件在Web上实现Kinect骨骼追踪、声控截屏保存的 ...

通过COM组件在Web上实现Kinect骨骼追踪、声控截屏保存的功能

磁呃泵 2025-5-29 15:48:25
      前些天在淘宝上订购了Kinect,刚刚到货,对于这个新鲜的玩意儿,自己赶紧卸开包裹,插上PC机,先前已经装好了Kinect SDK(官方下载地址:http://research.microsoft.com/en-us/um/redmond/projects/kinectsdk/download.aspx)
1.jpeg

希望自己的机子能够跑得动DEMO,最后送了一口气,正常运行:)。当然了,既然Kinect已经入手,自己打算也小小地练一下手,因为自己目前主要从事Web方面的开发,自然想到,如果能够在网页上能够运行Kinect该有多好啊!后来考虑了三种方案:
 
1)在Web应用程序上引用Kinect SDK的DLL(Microsoft.Research.Kinect.dll),可以正常引用,通过img标签或者页面输出图像流的方式显示Bitmap图像,当然你还需要定时刷新页面,当然这种方式的缺点可想而知,就是不够实时性。并且增加了服务器的负担。
2)通过Silverlight应用程序的方式,但是目前SL4/5均不支持Kinect的dll,这是由于Kinect是个.Net Framework的类库,非SL支持的客户端类库。另外,在非OOB的模式下,也SL目前不支持自定义的COM组件,希望微软团队将来能够让SL也支持Kinect。
3)通过ActiveX的COM组件技术,通过开发窗体组件,产生ActiveX插件,嵌套在IE浏览器中进行显示。
 
      于是,我考虑了最简单的方式,通过ActiveX的技术在网页上对Kinect相关基本功能进行展示。
本文会分成三个部分来叙述:
一、ActiveX插件的实现;
二、Kinect基本功能的实现;
三、ActiveX的安装;
 
具体实现
第一部分  ActiveX插件的实现


1) 创建一个新的解决方案,叫做MyFirstKinect。
 
2)接着创建一个Windows窗体控件库,用于做ActiveX的插件,项目叫做MyFirstKinectControl
2.png

 
3)在MyFirstKinectControl项目的右键点击“属性”,选择“生成”项:
3.png

将”为COM互操作注册”勾上,然后关闭。
 
4)打开AssemblyInfo.cs:
4.png

将ComVisible设置为true,并将下面这行注释掉(这个很重要,切记!
对应地,需要在自定义窗体控件上加上该Guid:
5.png

至此,一个基本的COM组件已经实现了,现在来看下该如何在浏览器上显示ActiveX插件。
 
5)打开Visual Studio的命令提示符:输入“oleview”,页面会打开一个“OLE/COM Object Viewer”应用程序:
6.png

由于我是使用C#创建的COM组件,于是在“.NET Category”寻找刚才创建的”MyFirstKinectControl”:
7.png

右键选择“Copy HTML Tag to Clipboard”,得到:
8.png

 
6)然后我在自定义窗体控件上(SkeletalControl.cs),随便加入点东西上去,比如按钮、标签等等。
 
7)然后新创建一个Web应用程序的项目(WebApp),重新编译。将上面的代码复制到Html或相关页面中。
9.png

在IE正常状态下,发现插件无法正常显示。于是,把浏览器的安全级别调低:
10.png

继续运行:
11.png

就可以正常显示插件了。当然这种方式造成了浏览器使用上的危害性,所以不建议这样来使用。
 
8)如果想要在不调整浏览器安全级别的情况下,又能够在浏览器上正常显示插件,这样就必须调整一些代码:
  1. [Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  2. public interface IObjectSafety
  3. {
  4.     // methods
  5.     void GetInterfacceSafyOptions(
  6.         System.Int32 riid,
  7.         out System.Int32 pdwSupportedOptions,
  8.         out System.Int32 pdwEnabledOptions);
  9.     void SetInterfaceSafetyOptions(
  10.         System.Int32 riid,
  11.         System.Int32 dwOptionsSetMask,
  12.         System.Int32 dwEnabledOptions);
  13. }
复制代码
添加一个IObjectSafety的接口,并且Guid是固定的。
SkeletalControl继承这个接口:
  1. [Guid("d678c286-b26f-4f72-ae22-2dcb1952851b")]
  2. public partial class SkeletalControl : UserControl, IObjectSafety
  3. {
  4.     public SkeletalControl()
  5.     {
  6.         InitializeComponent();
  7.     }
  8.     #region IObjectSafety 成员
  9.     public void GetInterfacceSafyOptions(Int32 riid, out Int32 pdwSupportedOptions, out Int32 pdwEnabledOptions)
  10.     {
  11.         pdwSupportedOptions = 1;
  12.         pdwEnabledOptions = 2;
  13.     }
  14.     public void SetInterfaceSafetyOptions(Int32 riid, Int32 dwOptionsSetMask, Int32 dwEnabledOptions)
  15.     {
  16.    
  17.     }
  18.     #endregion
  19. }
复制代码
接着重新编译并运行Web程序,运行结果为:
12.png

这样,你就不需要调整浏览器的安全级别,就可以正常显示ActiveX插件了。
 
第二部分 Kinect的基本功能实现

 
      从这一部分起,我将开始介绍Kinect如何实现一些基本功能:包括视频监控、骨骼追踪以及声控截屏的功能。
1)项目中引用以下的Dll:
13.png

其中Microsoft.Research.Kinect就是在电脑上装好Kinect SDK后可以引用的类库;
另外地,Coding4Fun.Kinect.WinForm是一个基于SDK的DLL的相关封装好的一些功能类库,网上开源地址为:http://c4fkinect.codeplex.com/;
Microsoft.Speech是一个微软提供的语音识别的基本类库,也包含相关的SDK,并且和Kinect进行绑定的相关类库,具体地址在Kinect SDK中的相关文档也有说明:
- Speech Platform Runtime (v10.2) x86. Even on x64 platforms the x86 needs to be used because the MSR Kinect SDK runtime is x86
  http://www.microsoft.com/downloads/en/details.aspx?FamilyID=bb0f72cb-b86b-46d1-bf06-665895a313c7
- Speech Platform SDK (v10.2)
  http://www.microsoft.com/downloads/en/details.aspx?FamilyID=1b1604d3-4f66-4241-9a21-90a294a5c9a4&displaylang=en
- Kinect English Language Pack: MSKinectLangPack_enUS.msi (available in the same location as the Kinect For Windows SDK)
 
2)在控件页面上创建三个PictureBox的控件:
14.png

三个图片框将分别用来存放:深度图视频、普通视频、以及骨骼追踪。
 
3)编写相关代码:
  1. using Microsoft.Research.Kinect.Nui;
  2. using Coding4Fun.Kinect.WinForm;
  3. Runtime nui;
  4. private void SkeletalControl_Load(object sender, EventArgs e)
  5. {
  6.     nui = new Runtime();
  7.     try
  8.     {
  9.         nui.Initialize(
  10.             RuntimeOptions.UseDepthAndPlayerIndex
  11.             | RuntimeOptions.UseSkeletalTracking
  12.             | RuntimeOptions.UseColor);
  13.     }
  14.     catch (InvalidOperationException)
  15.     {
  16.         MessageBox.Show("Runtime initialization failed. Please make sure Kinect device is plugged in.");
  17.         return;
  18.     }
  19.     try
  20.     {
  21.         nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
  22.         nui.DepthStream.Open(ImageStreamType.Depth, 2, ImageResolution.Resolution320x240, ImageType.DepthAndPlayerIndex);
  23.     }
  24.     catch (InvalidOperationException)
  25.     {
  26.         MessageBox.Show("Failed to open stream. Please make sure to specify a supported image type and resolution.");
  27.         return;
  28.     }
  29.     nui.DepthFrameReady += new EventHandler<imageframereadyeventargs>(nui_DepthFrameReady);
  30.     nui.SkeletonFrameReady += new EventHandler<skeletonframereadyeventargs>(nui_SkeletonFrameReady);
  31.     nui.VideoFrameReady += new EventHandler<imageframereadyeventargs>(nui_VideoFrameReady);
  32. }</imageframereadyeventargs></skeletonframereadyeventargs></imageframereadyeventargs>
复制代码
其中DepthFrameReady,VideoFrameReady,SkeletonFrameReady分别用来追踪深度图、普通视图、骨骼图所产生的事件。
  1. void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
  2. {
  3.      pictureBoxDepth.Image = e.ImageFrame.ToBitmap();
  4. }
  5. void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
  6. {
  7.      pictureBoxVideo.Image = e.ImageFrame.ToBitmap();
  8. }
  9. void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
  10. {
  11.      Graphics graphics = this.pictureBoxSkeleton.CreateGraphics();
  12.      pictureBoxSkeleton.Refresh();
  13.      SkeletonFrame skeletonFrame = e.SkeletonFrame;
  14.      int iSkeleton = 0;
  15.      foreach (SkeletonData data in skeletonFrame.Skeletons)
  16.      {
  17.          if (SkeletonTrackingState.Tracked == data.TrackingState)
  18.          {
  19.              // Draw bones
  20.              graphics.DrawLines(pen, getBodySegment(data.Joints, JointID.HipCenter, JointID.Spine, JointID.ShoulderCenter, JointID.Head));
  21.              graphics.DrawLines(pen, getBodySegment(data.Joints, JointID.ShoulderCenter, JointID.ShoulderLeft, JointID.ElbowLeft, JointID.WristLeft, JointID.HandLeft));
  22.              graphics.DrawLines(pen, getBodySegment(data.Joints, JointID.ShoulderCenter, JointID.ShoulderRight, JointID.ElbowRight, JointID.WristRight, JointID.HandRight));
  23.              graphics.DrawLines(pen, getBodySegment(data.Joints, JointID.HipCenter, JointID.HipLeft, JointID.KneeLeft, JointID.AnkleLeft, JointID.FootLeft));
  24.              graphics.DrawLines(pen, getBodySegment(data.Joints, JointID.HipCenter, JointID.HipRight, JointID.KneeRight, JointID.AnkleRight, JointID.FootRight));
  25.          }
  26.          iSkeleton++;
  27.      } // for each skeleton
  28. }
  29. private Point getDisplayPosition(Joint joint)
  30. {
  31.      float depthX, depthY;
  32.      nui.SkeletonEngine.SkeletonToDepthImage(joint.Position, out depthX, out depthY);
  33.      depthX = Math.Max(0, Math.Min(depthX * 320, 320));  //convert to 320, 240 space
  34.      depthY = Math.Max(0, Math.Min(depthY * 240, 240));  //convert to 320, 240 space
  35.      int colorX, colorY;
  36.      ImageViewArea iv = new ImageViewArea();
  37.      // only ImageResolution.Resolution640x480 is supported at this point
  38.      nui.NuiCamera.GetColorPixelCoordinatesFromDepthPixel(ImageResolution.Resolution640x480, iv, (int)depthX, (int)depthY, (short)0, out colorX, out colorY);
  39.      // map back to skeleton.Width & skeleton.Height
  40.      return new Point((int)(this.pictureBoxSkeleton.Width * colorX / 640.0), (int)(this.pictureBoxSkeleton.Height * colorY / 480));
  41. }
  42. Point[] getBodySegment(Microsoft.Research.Kinect.Nui.JointsCollection joints, params JointID[] ids)
  43. {
  44.      Point[] points = new Point[ids.Length];
  45.      for (int i = 0; i < ids.Length; ++i)
  46.      {
  47.          points[i] = getDisplayPosition(joints[ids[i]]);
  48.      }
  49.      return points;
  50. }
复制代码
其中,getBodySegment,getDisplayPosition方法将确定骨骼追踪中的20个骨骼点的具体位置。
 
4)接着编译并运行程序,查看Web页面,连上Kinect传感设备,运行结果为:
15.png

 
5)接着,来实现一些声控截屏功能:
  1. using Microsoft.Research.Kinect.Audio;
  2. using Microsoft.Speech.AudioFormat;
  3. using Microsoft.Speech.Recognition;
  4. private const string RecognizerId = "SR_MS_en-US_Kinect_10.0";
  5. private KinectAudioSource kinectSource;
  6. private SpeechRecognitionEngine sre;
  7. // 声控截屏功能
  8. RecognizerInfo ri = SpeechRecognitionEngine.InstalledRecognizers().Where(r => r.Id == RecognizerId).FirstOrDefault();
  9. if (ri == null)
  10. {
  11.     MessageBox.Show("Could not find speech recognizer: {0}. Please refer to the sample requirements.", RecognizerId);
  12.     return;
  13. }
  14. sre = new SpeechRecognitionEngine(ri.Id);
  15. var colors = new Choices();
  16. colors.Add("cut"); //添加cut的英文发音
  17. var gb = new GrammarBuilder();                          
  18. gb.Culture = ri.Culture; //本地化处理
  19. gb.Append(colors);
  20. var g = new Grammar(gb);
  21. sre.LoadGrammar(g);
  22. sre.SpeechRecognized += SreSpeechRecognized;                    //发音匹配以后的后续处理事件
  23. sre.SpeechHypothesized += SreSpeechHypothesized;                //发音的英文识别事件
  24. sre.SpeechRecognitionRejected += SreSpeechRecognitionRejected;  //拒绝之后的后续处理事件
  25. var thread = new Thread(StartDMO);
  26. thread.Start();
  27. private void StartDMO()
  28. {
  29.      kinectSource = new KinectAudioSource();
  30.      kinectSource.SystemMode = SystemMode.OptibeamArrayOnly;
  31.      kinectSource.FeatureMode = true;
  32.      kinectSource.AutomaticGainControl = false;
  33.      kinectSource.MicArrayMode = MicArrayMode.MicArrayAdaptiveBeam;
  34.      var kinectStream = kinectSource.Start();
  35.      sre.SetInputToAudioStream(kinectStream, new SpeechAudioFormatInfo(
  36.                                            EncodingFormat.Pcm, 16000, 16, 1,
  37.                                            32000, 2, null));
  38.      sre.RecognizeAsync(RecognizeMode.Multiple);
  39. }
  40. void SreSpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
  41. {
  42. }
  43. void SreSpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
  44. {
  45. }
  46. void SreSpeechRecognized(object sender, SpeechRecognizedEventArgs e)
  47. {
  48.      lblSpeech.Text = e.Result.Text;
  49.      //屏幕截屏
  50.      Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height);
  51.      Graphics g = Graphics.FromImage(bmp);
  52.      g.CopyFromScreen(0, 0, 0, 0, bmp.Size);
  53.      g.Dispose();
  54.      SaveFileDialog fileDialog = new SaveFileDialog();
  55.      fileDialog.Filter = "JPG File(*.jpg)|*.jpg||";
  56.      DialogResult result = fileDialog.ShowDialog();
  57.      if (result == DialogResult.OK)
  58.      {
  59.          bmp.Save(fileDialog.FileName, System.Drawing.Imaging.ImageFormat.Jpeg);
  60.      }
  61. }
复制代码
在代码中发现,var colors = new Choices(); colors.Add("cut"); //添加cut的英文发音 ,这样当你在传感器前发音“cut”就会进行相关事件的触发,当发音和英文库的单词语音识别向匹配时,将触发SreSpeechRecognized事件。 执行屏幕截图的相关操作。
(注:记得这里需要添加代码gb.Culture = ri.Culture; 如果没有这句代码,有可能导致sre.LoadGrammar(g); 语法加载失败!)
 
6. 运行结果:
16.png

我将刚才发的英文单词,通过文本的方式显示在页面中。
 
第三部分 ActiveX插件的安装


      由于本文使用的是C#来开发ActiveX插件,所以当你需要安装插件的时候,需要使用regasm命令。那么开始编写脚本:
1)安装脚本:
  1. @echo off
  2. echo 开始安装MyFirstKinectControl......
  3. echo.
  4. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v4.0.30319
  5. if exist "%FrameworkPath%\regasm.exe" goto :Start
  6. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v3.5
  7. if exist "%FrameworkPath%\regasm.exe" goto :Start
  8. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v3.0
  9. if exist "%FrameworkPath%\regasm.exe" goto :Start
  10. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
  11. if exist "%FrameworkPath%\regasm.exe" goto :Start
  12. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v1.1.4322
  13. if exist "%FrameworkPath%\regasm.exe" goto :Startv
  14. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\1.0.3705
  15. if exist "%FrameworkPath%\regasm.exe" goto :Start
  16. :Start
  17. %FrameworkPath%\regasm.exe MyFirstKinectControl.dll /codebase MyFirstKinectControl.dll
  18. echo 安装完成!
  19. echo.
  20. pause
复制代码
 
2)卸载脚本:
  1. @echo off
  2. echo 开始卸载MyFirstKinectControl......
  3. echo.
  4. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v4.0.30319
  5. if exist "%FrameworkPath%\regasm.exe" goto :Start
  6. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v3.5
  7. if exist "%FrameworkPath%\regasm.exe" goto :Start
  8. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v3.0
  9. if exist "%FrameworkPath%\regasm.exe" goto :Start
  10. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v2.0.50727
  11. if exist "%FrameworkPath%\regasm.exe" goto :Start
  12. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\v1.1.4322
  13. if exist "%FrameworkPath%\regasm.exe" goto :Startv
  14. set FrameworkPath=%SystemRoot%\Microsoft.NET\Framework\1.0.3705
  15. if exist "%FrameworkPath%\regasm.exe" goto :Start
  16. :Start
  17. %FrameworkPath%\regasm.exe /u MyFirstKinectControl.dll
  18. echo 卸载完成!
  19. echo.
  20. pause
复制代码
 
这样通过注册COM组件就可以实现Kinect的插件在浏览器上的展示。
 
附上本文的源代码:MyFirstKinect.rar   谢谢大家阅读!

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册