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,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, e.pData, 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,0,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,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, e.pData, FrameSize);
var args = new List<object>();
args.Add(pFocusData);
bw.RunWorkerAsync(args);
//编辑,所以查看你的代码,我几乎没有问题/建议.
1)if if(!e.Updated)返回后,如果你做了BW的话,会发生什么?线? wbm是否会开始出现这种累积错误,你的BW会好吗?
2)bw_DoWork完成后,BackgroundWorker.IsBusy属性为false.你对这种行为没问题吗?
3)MoveMemory做什么?它是否将内存从SOURCE复制到DESTINATION而不更改SOURCE中的任何内容?它甚至复制任何东西吗?
4)你在说什么缓冲变量?在每个阶段,验证所有BUFFERS并确定缓冲区不同的确切位置.您需要创建函数DumpMemory(IntPtr unmanagedMemory).您可以将IntPtr转换为byte *并使用fixed()语句来执行此操作.
标签:c,arrays,wpf,intptr,writeablebitmap 来源: https://codeday.me/bug/20190708/1406786.html