介绍
本文是接着上文《C# WPF 显示图片和视频显示 EmuguCv、AForge.Net测试》写的,建议先看下上文,因为有些代码还需要了解。
增添
接着上文的代码,我们可以在事件处理方法videoSource_NewFrame 里面加一些对每帧图片的处理 。
private void videoSource_NewFrame(object sender, NewFrameEventArgs eventArgs) { //eventArgs.Frame.Save(stream, System.Drawing.Imaging.ImageFormat.Bmp); if (this.Dispatcher != null) { this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render, (SendOrPostCallback)delegate { if (map != IntPtr.Zero) { var tempImg = eventArgs.Frame; if (this.switchRecognize)//处理代码 { tempImg= this.recognizer.Recognize(tempImg); } System.Drawing.Imaging.BitmapData bmpData = tempImg.LockBits(new System.Drawing.Rectangle(, , (int)sizeMemory.Width, (int)sizeMemory.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppRgb); /* Get the pointer to the pixels */ IntPtr pBmp = bmpData.Scan0; int srcStride = bmpData.Stride;//步?长¡è int pSize = srcStride * (int)sizeMemory.Height; CopyMemory(map, pBmp, pSize); tempImg.UnlockBits(bmpData); tempImg.Dispose();//释放资源 GC.Collect(); } if (bitmapSource != null) { bitmapSource.Invalidate(); } }, null); } if (captureLock) { try { lock (mutCurrentImg) { eventArgs.Frame.Save(imageFileName); } } catch (System.Exception ex) { } captureLock = false; } }
在处理每帧图片的时候,可以做一些放大、缩小、校验等等操作,当然是比较耗费性能和内存的。这里我想要强调的是在处理图片之后会返回一个新的对象Bitmap,这个对象我们需要手动释放。如果不释放内存将会逐渐增加然后回到原点(刚启动摄像头并switchRecognize为真时为初内存,逐渐增加到原来的15倍左右就会回到原点。也就是托管的内存资源会自动释放,不过要等。)。
到目前为止,加上
tempImg.Dispose();//释放资源
程序本身的内存停止增长,但是操作系统的整体内存还是在增长然后自动释放。这时候需要手动控制系统的垃圾收未使用的内存 GC.Collect();
工作集
这里用到啦CreateFileMapping创建映射内存,所以会考虑到工作集。
所谓工作集是指进程已映射的物理内存部分(即这些内存块全在物理内存中,并且 CPU 可以直接访问),还有一部分不在工作集中的虚拟内存则可能在转换列表中(CPU 不能通过虚地址访问,需要 Windows 映射之后才能访问),还有一部分则在磁盘上的页面文件里。工作集在进程运行时会被 Windows 自动调整,频繁访问的页面(4KB 的块)会留在内存中,而不频繁访问的页面在内存紧张时会被从工作集中移出,暂时保存在内存中的“转换列表”中,或者进一步换出到页面文件中。当应用程序再次访问某一页面时,操作系统会将它重新加回工作集中。
实现共享内存操作和释放代码:
//内存共享类
internal class Win32Mess
{ [DllImport("Kernel32.dll", EntryPoint = "RtlMoveMemory")]
private static extern void CopyMemory(IntPtr Destination, IntPtr Source, int Length); [DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, IntPtr lParam); [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr CreateFileMapping(int hFile, IntPtr lpAttributes, uint flProtect, uint dwMaxSizeHi, uint dwMaxSizeLow, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr OpenFileMapping(int dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, string lpName); [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr MapViewOfFile(IntPtr hFileMapping, uint dwDesiredAccess, uint dwFileOffsetHigh, uint dwFileOffsetLow, uint dwNumberOfBytesToMap); [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool UnmapViewOfFile(IntPtr pvBaseAddress); [DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CloseHandle(IntPtr handle); [DllImport("kernel32", EntryPoint = "GetLastError")]
public static extern int GetLastError(); const int ERROR_ALREADY_EXISTS = ; const int FILE_MAP_COPY = 0x0001;
const int FILE_MAP_WRITE = 0x0002;
const int FILE_MAP_READ = 0x0004;
const int FILE_MAP_ALL_ACCESS = 0x0002 | 0x0004; //0xF001F; const int PAGE_READONLY = 0x02;
const int PAGE_READWRITE = 0x04;
const int PAGE_WRITECOPY = 0x08;
const int PAGE_EXECUTE = 0x10;
const int PAGE_EXECUTE_READ = 0x20;
const int PAGE_EXECUTE_READWRITE = 0x40; const int SEC_COMMIT = 0x8000000;
const int SEC_IMAGE = 0x1000000;
const int SEC_NOCACHE = 0x10000000;
const int SEC_RESERVE = 0x4000000; const int INVALID_HANDLE_VALUE = -; IntPtr m_hSharedMemoryFile = IntPtr.Zero;
IntPtr m_pwData = IntPtr.Zero;
bool m_bAlreadyExist = false;
bool m_bInit = false;
public bool IsOpen
{
get { return this.m_bInit; }
}
long m_MemSize = ; public Win32Mess()
{
}
~Win32Mess()
{
Close();
} ///
/// 初始化共享内存
///
/// 共享内存名称
/// 共享内存大小
///
public int Init(string strName, long lngSize)
{
if (lngSize <= || lngSize > 0x00800000) lngSize = 0x00800000;
m_MemSize = lngSize;
if (strName.Length > )
{
//创建内存共享体(INVALID_HANDLE_VALUE)
m_hSharedMemoryFile = CreateFileMapping(INVALID_HANDLE_VALUE, IntPtr.Zero, (uint)PAGE_READWRITE, , (uint)lngSize, strName);
if (m_hSharedMemoryFile == IntPtr.Zero)
{
m_bAlreadyExist = false;
m_bInit = false;
return ; //创建共享体失败
}
else
{
if (GetLastError() == ERROR_ALREADY_EXISTS) //已经创建
{
m_bAlreadyExist = true;
}
else //新创建
{
m_bAlreadyExist = false;
}
}
//---------------------------------------
//创建内存映射
m_pwData = MapViewOfFile(m_hSharedMemoryFile, FILE_MAP_ALL_ACCESS, , , (uint)lngSize);
if (m_pwData == IntPtr.Zero)
{
m_bInit = false;
CloseHandle(m_hSharedMemoryFile);
return ; //创建内存映射失败
}
else
{
m_bInit = true;
if (m_bAlreadyExist == false)
{
//初始化
}
}
//----------------------------------------
}
else
{
return ; //参数错误
} return ; //创建成功
}
public InteropBitmap GetImage(System.Drawing.Size sizeMemory)
{
if (m_bInit)
return System.Windows.Interop.Imaging.CreateBitmapSourceFromMemorySection(m_hSharedMemoryFile, (int)sizeMemory.Width, (int)sizeMemory.Height, PixelFormats.Bgr32,
(int)(sizeMemory.Width * PixelFormats.Bgr32.BitsPerPixel / ), ) as InteropBitmap;
else
return null; } ///
/// 关闭共享内存
///
public void Close()
{
if (m_bInit)
{
UnmapViewOfFile(m_pwData);
CloseHandle(m_hSharedMemoryFile);//多次释放会报错,所以需要下行代码值零
m_hSharedMemoryFile = IntPtr.Zero;
m_bInit = false;
}
} ///
/// 读数据
///
/// 数据
/// 起始地址
/// 个数
///
public int Read(ref byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return ; //超出数据区
if (m_bInit)
{
Marshal.Copy(m_pwData, bytData, lngAddr, lngSize);
}
else
{
return ; //共享内存未初始化
}
return ; //读成功
} ///
/// 写数据
///
/// 数据
/// 起始地址
/// 个数
///
public int Write(byte[] bytData, int lngAddr, int lngSize)
{
if (lngAddr + lngSize > m_MemSize) return ; //超出数据区
if (m_bInit)
{
Marshal.Copy(bytData, lngAddr, m_pwData, lngSize);
}
else
{
return ; //共享内存未初始化
}
return ; //写成功
}
public int Write(IntPtr lngAddr, int lngSize)
{
if (lngSize > m_MemSize) return ; //超出数据区
if (m_bInit)
{
CopyMemory(m_pwData, lngAddr, lngSize);
}
else
{
return ; //共享内存未初始化
}
return ; //写成功
}
}