You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:18:42