Bitmap.Clone报错“OBJECT IS CURRENTLY IN USE ELSEWHERE”问题求助
解决Bitmap“OBJECT IS CURRENTLY IN USE ELSEWHERE”异常(USB摄像头帧处理场景)
嘿,这个问题我之前做摄像头帧处理的时候踩过一模一样的坑!本质原因是你操作的Bitmap对象还被摄像头的回调线程或者UI渲染线程占用着,导致你没法直接对它做克隆、格式转换这类操作。结合你要把32bpp转成24bpp的场景,给你几个实用的解决方案:
1. 立刻创建独立的Bitmap副本,避免共享对象
摄像头返回的帧对象(比如e.Frame)一般还被摄像头驱动持有引用,你直接赋值给全局变量后去操作,肯定会冲突。正确的做法是拿到帧的第一时间,就创建一个完全独立的24bpp Bitmap副本:
private void Camera_FrameReceived(object sender, FrameEventArgs e) { // 用using包裹原帧,确保摄像头能及时释放它 using (var originalFrame = e.Frame) { // 直接创建目标格式的Bitmap camera_frame_Bitmap = new Bitmap(originalFrame.Width, originalFrame.Height, PixelFormat.Format24bppRgb); // 把原帧内容绘制到新Bitmap上 using (var g = Graphics.FromImage(camera_frame_Bitmap)) { g.DrawImage(originalFrame, 0, 0, originalFrame.Width, originalFrame.Height); } } // 现在camera_frame_Bitmap就是完全独立的,后续可以安全用于对比 }
如果是高分辨率摄像头追求性能,用LockBits直接复制像素数据会比Graphics.DrawImage更快:
private void Camera_FrameReceived(object sender, FrameEventArgs e) { using (var originalFrame = e.Frame) { var rect = new Rectangle(0, 0, originalFrame.Width, originalFrame.Height); var sourceData = originalFrame.LockBits(rect, ImageLockMode.ReadOnly, originalFrame.PixelFormat); // 创建目标格式的Bitmap camera_frame_Bitmap = new Bitmap(originalFrame.Width, originalFrame.Height, PixelFormat.Format24bppRgb); var destData = camera_frame_Bitmap.LockBits(rect, ImageLockMode.WriteOnly, camera_frame_Bitmap.PixelFormat); // 计算像素数据长度,复制内存 int byteCount = sourceData.Stride * sourceData.Height; byte[] pixelBuffer = new byte[byteCount]; System.Runtime.InteropServices.Marshal.Copy(sourceData.Scan0, pixelBuffer, 0, byteCount); System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, 0, destData.Scan0, byteCount); // 解锁内存,这步必须做! originalFrame.UnlockBits(sourceData); camera_frame_Bitmap.UnlockBits(destData); } }
2. 确保线程安全,避免跨线程操作冲突
摄像头帧回调一般是在后台线程触发的,而你的UI或者后续对比逻辑可能在主线程执行,跨线程访问同一个Bitmap必然会出问题。解决办法是用Invoke切换到UI线程处理:
private void Camera_FrameReceived(object sender, FrameEventArgs e) { // 判断是否需要切换线程 if (this.InvokeRequired) { this.Invoke(new Action<object, FrameEventArgs>(Camera_FrameReceived), sender, e); return; } // 这里已经在UI线程,安全处理Bitmap using (var originalFrame = e.Frame) { // 先释放旧的Bitmap,避免内存泄漏 if (camera_frame_Bitmap != null) { camera_frame_Bitmap.Dispose(); camera_frame_Bitmap = null; } camera_frame_Bitmap = new Bitmap(originalFrame.Width, originalFrame.Height, PixelFormat.Format24bppRgb); using (var g = Graphics.FromImage(camera_frame_Bitmap)) { g.DrawImage(originalFrame, 0, 0); } } }
3. 及时释放旧Bitmap,避免资源泄漏
每次生成新的帧Bitmap前,一定要释放旧的对象,不然不仅会内存泄漏,还可能导致旧对象被残留的引用占用,触发异常:
// 替换Bitmap前的释放逻辑 if (latest_frame_buffer_Bitmap != null) { latest_frame_buffer_Bitmap.Dispose(); latest_frame_buffer_Bitmap = null; } // 再创建新的Bitmap副本 using (var originalFrame = e.Frame) { latest_frame_buffer_Bitmap = new Bitmap(originalFrame.Width, originalFrame.Height, PixelFormat.Format24bppRgb); using (var g = Graphics.FromImage(latest_frame_buffer_Bitmap)) { g.DrawImage(originalFrame, 0, 0); } }
核心总结
这个异常的本质是Bitmap是非线程安全对象,且被多个线程/资源同时持有引用。只要保证你操作的是完全独立的副本,并且在同一线程内访问,同时及时释放旧对象,就能彻底解决问题。
内容的提问来源于stack exchange,提问作者Doug Null
相关产品推荐
相关产品推荐

