踩大坑!关于海康威视摄像头预览与录像,WinForm和WPF控件的问题

文章讲述了在WPF应用中集成海康威视SDK遇到的问题,包括如何通过WindowsFormHost预览和录像,以及使用Image控件处理YUV到RGB转换。作者最终通过定时器和PictureBox控件实现了稳定的实时预览,尽管录像功能受限于回调函数处理。
摘要由CSDN通过智能技术生成
  1. 通过海康威视SDK调用摄像头实现预览以及录像功能。按照SDK说明,在WinForm上一切OK,轻松加愉快。
  2. 但是因为软件主体使用的是WPF。所以想通过WindowsFormHost,将picturebox或panel等控件包装在WPF上。一顿操作猛如虎,咋看像这么回事,但是实际上,使用了WindowsFormHost后。如果有WPF控件会显示在WindowsFormHost之上,那就会出现大问题,因为WinForm控件始终是最顶层,哪怕你设置了 IsHitTestVisible="False" IsEnabled="False" Panel.ZIndex="-2" Background="Transparent" 都没用,他还是显示在WPF控件上面。这样很不爽,还会影响到我软件的其他功能。
  3. 看最终解决方案请看最后一条
  4. 尝试其他方案:
    1. 使用WPF控件句柄:
      添加一个Image控件,通过var abc = ((HwndSource)PresentationSource.FromVisual(imageControl)).Handle;
      获取WPF控件的句柄;
      测试发现通过这个句柄会将整个窗口渲染成预览图像,原因是在WPF中,所有元素是以元素树的形式渲染的,各个控件没有单独的句柄。
    2. 使用海康SDK实时预览回调函数:
      在解码回调中将数据添加到队列(数据大,不要直接在回调函数中解码)。
      通过各种方式将YV12格式数据转成RGB图像(论坛中有些这个方法)。再将图像转成ImageSource 传给WPF的Image控件。但是直接耗时长,debug模式300ms一张图。
      其他解码方式:
       public Bitmap RGB24_to_Image(byte[] byteBuff, int nWidth, int nHeight)
          {
            if (byteBuff.Length <= 0 || byteBuff.Length < nWidth * nHeight)
              return null;
            Bitmap bmp = new Bitmap(nWidth, nHeight, PixelFormat.Format24bppRgb);
      
            //锁定内存数据
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, nWidth, nHeight), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
      
            //输入颜色数据
            System.Runtime.InteropServices.Marshal.Copy(byteBuff, 0, bmpData.Scan0, byteBuff.Length);
      
            //解锁
            bmp.UnlockBits(bmpData);
      
            return bmp;
          }
       
        public bool YV12ToRBG24_OpenCV(byte[] pYUV, byte[] pRGB24, int width, int height)
          {
            if (width < 1 || height < 1 || pYUV == null || pRGB24 == null)
            {
              return false;
            }
            using (Mat dst = new Mat(height, width, MatType.CV_8UC3, pRGB24))
            {
              using (Mat src = new Mat(height + height / 2, width, MatType.CV_8UC1, pYUV))
              {
                Cv2.CvtColor(src, dst, ColorConversionCodes.YUV2RGB_YV12);
              }
            }
            return true;
          }
      byte[] byteBuffYV12 = new byte[data.nSize];
                  Marshal.Copy(data.pBuf, byteBuffYV12, 0, data.nSize);
      
                  long lRGBSize = (long)data.pFrameInfo.nWidth * data.pFrameInfo.nHeight * 3;
                  byte[] bufferRGB32 = new byte[lRGBSize];
                  YV12ToRBG24_OpenCV(byteBuffYV12, bufferRGB32, data.pFrameInfo.nWidth, data.pFrameInfo.nHeight);
       Bitmap bmpFromGRB32 = RGB24_to_Image(bufferRGB32, data.pFrameInfo.nWidth, data.pFrameInfo.nHeight);

      通过OpenCVSharp将图像转换时间在25ms左右,可以不调用RGB24_to_Image();
      而是直接将dst通过OpenCVSharp转bitmap.

      再将bitmap转成ImageSource:
       

      public BitmapSource ToBitmapSource(Bitmap image)
          {
            IntPtr ptr = image.GetHbitmap();//obtain the Hbitmap
            BitmapSource bs = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap
            (
                ptr,
                IntPtr.Zero,
                Int32Rect.Empty,
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions()
            );
            DeleteObject(ptr);//release the HBitmap
            return bs;
      
          }

      OK,看起来一起都很完美,但在debug时发现队列的Count一直在涨,我25ms处理一张数据居然处理不过来?(摄像头的帧数是25)很快就到几千张了,但是看画面延迟只有1秒左右。哪里出问题了?后面可以将队列张数限制一下,比如限制到10,多了我不要,因为可能是重复的数据。不然如果回调只执行了25次话,线程肯定是处理无压力的(补充:回调函数的方式不能打开SKD的录像功能,一录像回调函数就没了,需要自己处理录像)

    3. 好!有问题我们跳过问题:它监控摄像头不是还可以直接通过ip地址访问网页进行预览吗。直接通过js脚本自动化登录,点击预览,点击录屏。

      1:使用webView2的WPF控件进行网页操作
      await webView.CoreWebView2.ExecuteScriptAsync("var button = document.getElementById('username').value='123';");
      await webView.CoreWebView2.ExecuteScriptAsync("var button = document.getElementById('buttonId').click();");

      结果发现登录页面了,但是webView2不能加载海康的网页组件。
      2.CefSharp可以加载组件,但我没去测试,也不知道该怎么加。麻了,已经被卡主两三天了

    4. 今天,弯弯绕终于让我绕出来了。以前为了一个功能无所不用其极,现在咋把优良传统给忘了。

      1. 直接创建一个全局的PictureBox控件,获取它的Handle。按方法一操作;

      2. 加个定时器,通过PictureBox控件的Image属性将预览的图片转成ImageSource赋值给我们WPF的Image控件。

      3. 完美,操作简单、功能完善,还可以添加些  MenuItem 增加些功能

                 ok,完结撒花!

补充:麻了,第4条完全不行。首先WinForm的picturebox控件的Image属性是空的,因为SKD是通过句柄将控件通过DGI+渲染成的。如果没有显示控件,完全不会进行渲染,而且是控件多大渲染多少,所以无法通过手动从句柄中获取到绘制的图片(如果这个控件显示在屏幕显示区域时是可以的)。所以这条PASS,无效。人麻了,老实用回调函数去处理吧(限制队列长度,确保实时性就好。但视频保存得另写)

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值