- 通过海康威视SDK调用摄像头实现预览以及录像功能。按照SDK说明,在WinForm上一切OK,轻松加愉快。
- 但是因为软件主体使用的是WPF。所以想通过WindowsFormHost,将picturebox或panel等控件包装在WPF上。一顿操作猛如虎,咋看像这么回事,但是实际上,使用了WindowsFormHost后。如果有WPF控件会显示在WindowsFormHost之上,那就会出现大问题,因为WinForm控件始终是最顶层,哪怕你设置了 IsHitTestVisible="False" IsEnabled="False" Panel.ZIndex="-2" Background="Transparent" 都没用,他还是显示在WPF控件上面。这样很不爽,还会影响到我软件的其他功能。
看最终解决方案请看最后一条- 尝试其他方案:
- 使用WPF控件句柄:
添加一个Image控件,通过var abc = ((HwndSource)PresentationSource.FromVisual(imageControl)).Handle;
获取WPF控件的句柄;
测试发现通过这个句柄会将整个窗口渲染成预览图像,原因是在WPF中,所有元素是以元素树的形式渲染的,各个控件没有单独的句柄。 - 使用海康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的录像功能,一录像回调函数就没了,需要自己处理录像)
- 好!有问题我们跳过问题:它监控摄像头不是还可以直接通过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可以加载组件,但我没去测试,也不知道该怎么加。麻了,已经被卡主两三天了。 -
今天,弯弯绕终于让我绕出来了。以前为了一个功能无所不用其极,现在咋把优良传统给忘了。-
直接创建一个全局的PictureBox控件,获取它的Handle。按方法一操作; -
加个定时器,通过PictureBox控件的Image属性将预览的图片转成ImageSource赋值给我们WPF的Image控件。 -
完美,操作简单、功能完善,还可以添加些 MenuItem 增加些功能
-
- 使用WPF控件句柄:
ok,完结撒花!
补充:麻了,第4条完全不行。首先WinForm的picturebox控件的Image属性是空的,因为SKD是通过句柄将控件通过DGI+渲染成的。如果没有显示控件,完全不会进行渲染,而且是控件多大渲染多少,所以无法通过手动从句柄中获取到绘制的图片(如果这个控件显示在屏幕显示区域时是可以的)。所以这条PASS,无效。人麻了,老实用回调函数去处理吧(限制队列长度,确保实时性就好。但视频保存得另写)。