c# – 用户创建的像素字节数组似乎无法正确更新(WPF)
我有一个系统,我可以从相机收集8位灰色图像,将数据放入WriteableBitmap并在
WPF图像对象上显示图像.这项工作发生在相机线程中.我用这篇文章来帮助我:
How to create a BitmapImage from a pixel byte array (live video display)
我想要做的是复制图像数据的像素数据的子集.在相机线程中的帧更新期间,我试图在单独的字节数组中创建数据的副本.我的代码似乎首先工作,但是经过几次迭代后,我的缓冲区变量从一系列灰度级(0-255)变为每个数组元素中只有255的值.变量似乎累积数据和最大值,而不是每次调用后台工作程序时重置.我将在下面提供我的代码. 任何人都可以看到并描述我做错了什么吗?谢谢. public partial class MainWindow : Window { [DllImport("Kernel32.dll",EntryPoint="RtlMoveMemory")] public static extern void CopyMemory(IntPtr Destination,IntPtr Source,uint Length); // Declarations var pData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte))*FrameSize); var pFocusData = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) *FrameSize); BackgroundWorker bw = new BackgroundWorker(); static CameraWorker cWorker; static Thread cThread; WriteableBitmap wbm; public IntPtr wbmBackBuffer; const int FrameSize = 1944*2592; // CameraWorker Event Handler void CW_FrameUpdated(object sender,CameraWorkerEventArgs e) { if (!e.Updated) return; // e.pData is an IntPtr containing the camera frame data CopyMemory(this.wbmBackBuffer,e.pData,FrameSize); this.Dispatcher.Invoke(wbm.Lock); this.Dispatcher.Invoke(()=>{ wbm.AddDirtyRect( new Int32Rect(0,wbm.PixelWidth,wbm.PixelHeight)); }); this.Dispatcher.Invoke(wbm.Unlock); // The above works and I get streaming data to my view port. // Now I want to make a copy of the pixel data to send to another thread // for processing. This is where I am having trouble. if (bw.IsBusy) return; CopyMemory(pFocusData,FrameSize); var args = new List<object>(); args.Add(pFocusData); bw.RunWorkerAsync(args); } // BackgroundWorker event handlers void bw_DoWork(object sender,DoWorkEventArgs e) { // This is where I see the result of the problem when debugging. List<object> argu = e.Argument as List<object>; var pData = (IntPtr) argu[0]; var fullFrame = new byte[FrameSize]; Marshal.Copy(pData,fullFrame,FrameSize); // Perform operations on the byte array data. // I extract a subregion from the byte array to process,however after a // couple of iterations,all values in fullFrame equal 255. The pData that // is coming in should be a copy of the pixel data that is being displayed // on the screen. While the screen keeps updating with a live video image,// the frameData variable appears to keep accumulating rather than resetting // with each backgroundworker call. } void bw_RunWorkerCompleted(object sender,RunWorkerCompletedEventArgs e) { // Update UI elements using Dispatcher with data obtained during DoWork. } // Window event handlers private void MainWindow_Initialized(object sender,EventArgs e) { // Set up the WBM wbm = new WriteableBitmap(width,height,96d,PixelFormats.Gray8,null); this.wbmBackBuffer = wbm.BackBuffer; // Set up the camera to grab frames in another thread cWorker = new CameraWorker(camera); cWorker.CameraFrameUpdated += CW_FrameUpdated; cThread = new Thread(new ThreadStart(cWorker.ThreadRun)); cThread.Start(); while(!cThread.IsAlive); // Set up the background worker for processing image data bw.DoWork += bw_DoWork; bw.RunWorkerCompleted += bw_RunWorkerCompleted; // Bind the image data to the Image object that has the name "viewer" viewer.Source = wbm; } private void MainWindow_Closing(object sender,System.ComponentModel.CancelEventArgs e) { Marshal.FreeHGlobal(pData); } } 编辑:我纠正了Erti-Chris Eelmaa指出的错字.我只是在显示代码的相关部分时出现了转录错误. 编辑#2: 1)if if(!e.Updated)返回后,如果你做了BW的话,会发生什么?线? wbm是否会开始出现这种累积错误,你的BW会好吗? 行为没有区别. wbm仍然很好,我的BW变量累积. 2)bw_DoWork完成后,BackgroundWorker.IsBusy属性为false.你对这种行为没问题吗? 我的意图是在BW完成时只处理一个新帧.我认为IsBusy会持续到运行RunWorkerCompleted为止?我不需要处理每一帧. 我试图为CW_FrameUpdated的每次调用创建一个新的BW,但这也没有解决问题. 3)MoveMemory做什么?它是否将内存从SOURCE复制到DESTINATION而不更改SOURCE中的任何内容?它甚至复制任何东西吗? RtlMoveMemory(CopyMemory)应该将字节从一个内存区域复制到另一个区域.我认为通过AllocHGlobal函数分配了两个相同大小的独立空间,我可以将8MB数据从一个变量快速复制到另一个变量.从我一直在阅读的内容看来,我正在做一些托管内存不喜欢的事情.我需要快速深度复制数据.我会再次尝试System.Buffer.BlockCopy,以防我第一次错过了什么. 4)你在说什么缓冲变量?在每个阶段,验证所有BUFFERS并确定缓冲区不同的确切位置.您需要创建函数DumpMemory(IntPtr unmanagedMemory).您可以将IntPtr转换为byte并使用fixed()语句来执行此操作.* 我将设置DumpMemory函数,让你知道我发现了什么.与此同时,这里是数据流和相关变量的名单. pData(IntPtr):非托管内存,包含格雷8格式的1944 * 2592字节图像数据,没有标题 wbm(WriteableBitmap):绑定到主窗口上的Image对象的WPF变量 wbmBackBuffer(IntPtr):指向与wbm.BackBuffer相同位置的局部变量 使用CopyMemory将pData复制到wbmBackBuffer,并且因为wbm绑定到Image对象,所以当前图像帧在主窗口上更新 pFocusData(IntPtr):这是一个本地指针,它以一整帧数据的大小分配内存 pData通过CopyMemory复制到pFocusData. fullFrame(byte []):这是pFocusData的字节数组副本.这是我看到积累发生的地方. 编辑#3:我终于解决了这个问题.事实证明,我选择的子阵列存在逻辑错误.我的视口约为800 * 400,而图像阵列约为3.5倍.我没有正确地缩放坐标,因此我的样本区域都在查看帧数据的一个小而相似的区域.通过遵循第4个建议并使用内存转储来查看每个步骤的确切内容,我能够注意到这一点.它帮助我看到代码最终没有任何问题,我认为积累的实际上只是相机在一个小区域内饱和. 好消息是上面发布的代码是正确的并且有效.我最终使用Buffer.BlockCopy并创建了一个复制帧,我通过相机工作器事件args. 非常感谢你的帮助Erti-Chris Eelmaa! 解决方法
你现在累了,现在是圣诞节的时候了:P
if (bw.IsBusy) return; // replace this.wbmBackBuffer with pFocusData CopyMemory(this.wbmBackBuffer,FrameSize); var args = new List<object>(); args.Add(pFocusData); bw.RunWorkerAsync(args); //编辑,所以查看你的代码,我几乎没有问题/建议. 1)if if(!e.Updated)返回后,你的BW会好吗? 2)bw_DoWork完成后,BackgroundWorker.IsBusy属性为false.你对这种行为没问题吗? 3)MoveMemory做什么?它是否将内存从SOURCE复制到DESTINATION而不更改SOURCE中的任何内容?它甚至复制任何东西吗? 4)你在说什么缓冲变量?在每个阶段,验证所有BUFFERS并确定缓冲区不同的确切位置.您需要创建函数DumpMemory(IntPtr unmanagedMemory).您可以将IntPtr转换为byte *并使用fixed()语句来执行此操作. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |