【问题标题】:Image from a bytearray failure来自字节数组故障的图像
【发布时间】:2017-05-04 09:08:17
【问题描述】:

我有一个函数叫做:DisplayAndSaveImageFromByteArray。

通过它的名字,你可能明白我想要做什么。在字节数组中,值是像素数据。所以像这样 255,220,130,0 等等。

字节的大小是图像的宽度和高度乘以 4。 因为它可以大步前进。

public void DisplayAndSaveImageFromByteArray(byte[] byteArray)
{
    try{
        byte[] data = new byte[width * height * 4];
        int o = 0;

        for (int io = 0; io < width * height; io++){
            byte value = byteArray[io];

            data[o++] = value;
            data[o++] = value;
            data[o++] = value;
            data[o++] = 0;
        }
        unsafe
        {
            fixed (byte* ptr = data)
            {
                using (image = new Bitmap((int)width, (int)height, (int)width * 4,System.Drawing.Imaging.PixelFormat.Format32bppRgb, new IntPtr(ptr)))
                {
                    image.Save(@"c:\\testmap\" + nextpicture + ".jpg", ImageFormat.Jpeg);
                    if (nextpicture >= 10)
                    {
                        pbCameraPreview.BeginInvoke(new InvokeDelegate(InvokeMethod));
                    }
                    nextpicture++;
                }
            }
        }
    }
    catch (Exception ex){
        MessageBox.Show(ex.ToString());
    }

}

当我运行此代码时,它会起作用,但前提是所有值都相同,例如:白色 (255,255,255) 或黑色 (0,0,0)。 它能够在 RGB(A) 值中上下偏离大约 5 倍,直到它停止工作。 但是一旦图像的颜色发生变化,它就会停止工作,而不会给我任何异常。

如果我让它打开一分钟,我会得到它唯一的错误/异常,VS 会识别出正在执行的代码没有做任何事情,并会给我一个警告。 > 上下文切换死锁

我做错了什么让它崩溃? 解决方案是什么? 出于某种原因,它不允许我使用 using 和命名空间名称... (更新)完整代码:

    public Form1()
    {
        InitializeComponent();
        pbCameraPreview.Image = defImg;
    }

    #region Global
    int ii;
    object __p1;
    EventArgs __p2;
    string path;
    Image defImg = Image.FromFile(@"c:\\testimg\def.jpg");
    UInt32 width;
    UInt32 height;
    int nextpicture = 0;
    FGNodeInfoContainer InfoContainer = new FGNodeInfoContainer();
    FGNodeInfo NodeInfo = new FGNodeInfo();
    FireWrap_CtrlCenter CtrlCenter;
    enFireWrapResult Result;
    UInt32 XSize = new UInt32();
    UInt32 YSize = new UInt32();
    UInt32 NodeCnt;
    public delegate void InvokeDelegate();
    Bitmap image;
    CameraCode Cam = new CameraCode();
    FGFrame Frame = new FGFrame();
    FGUIntHL Guid = new FGUIntHL();
    #endregion

    public void CheckDirectory()
    {
        path = @"c:\\testmap\" + ii + "\\";
        if (Directory.Exists(@"c:\\testmap\") == false)
        {
            Directory.CreateDirectory(@"c:\\testmap\");
        }

        if (Directory.Exists(path))
        {
            if (File.Exists(path + "0.Jpeg"))
            {
                ii++;
                CheckDirectory();
            }
        }
        else
        {
            Directory.CreateDirectory(path);
        }
    }

    //Haal de images op
    /// <param name="__p1"></param>
    /// <param name="__p2"></param>
    public void btStart_Click(object sender, EventArgs e)
    {
        Debug.WriteLine("btStart_Click is clicked");
        // Init module
        CtrlCenter = FireWrap_CtrlCenter.GetInstance();
        Result = CtrlCenter.FGInitModule();

        // Register frame start event
        CtrlCenter.OnFrameReady += new FireWrap_CtrlCenter.FireWrapEvent(OnFrameReady);

        // Get list of connected nodes
        if (Result == enFireWrapResult.E_NOERROR)
        {
            Result = InfoContainer.FGGetNodeList();
            NodeCnt = InfoContainer.Size();

            // Print Nodecnt
            Console.WriteLine(NodeCnt.ToString() + " camera found");

            // Connect with first node
            InfoContainer.GetAt(NodeInfo, 0);
            Result = Cam.Connect(NodeInfo.Guid);
            if (Result == enFireWrapResult.E_NOERROR)
            {
                Cam.m_Guid = NodeInfo.Guid;
            }

            // Set Format7 Mode0 Y8
            if (Result == enFireWrapResult.E_NOERROR)
            {
                Result = Cam.SetParameter(enFGParameter.E_IMAGEFORMAT,
                        (uint)(((uint)enFGResolution.E_RES_SCALABLE << 16) |
                            ((uint)enColorMode.E_CCOLORMODE_Y8 << 8) |
                                0));
            }

            if (Result != enFireWrapResult.E_NOERROR)
            {
                Result = Cam.SetParameter(enFGParameter.E_IMAGEFORMAT,
                            (uint)(((uint)enFGResolution.E_RES_SCALABLE << 16) |
                                ((uint)enColorMode.E_CCOLORMODE_Y8 << 8) |
                                    1));
            }

            // Start DMA logic
            if (Result == enFireWrapResult.E_NOERROR)
                Result = Cam.OpenCapture();

            // Print device settings
            Result = Cam.GetParameter(enFGParameter.E_XSIZE, ref XSize);
            Result = Cam.GetParameter(enFGParameter.E_YSIZE, ref YSize);
            Debug.WriteLine(Cam.DeviceAll + " [" + Cam.m_Guid.Low.ToString() + "] " + XSize + "x" + YSize);
            width = XSize;
            height = YSize;

            // Start camera
            if (Result == enFireWrapResult.E_NOERROR)
            {
                Result = Cam.StartDevice();
            }          
        }
    }

    public void btStop_Click(object sender, EventArgs e)
    {
        // Stop the device
        Cam.StopDevice();
        // Close capture
        Cam.CloseCapture();
        // Disconnect before ExitModule
        Cam.Disconnect();
        // Exit module
        CtrlCenter.FGExitModule();
    }

    /// <param name="__p1"></param>
    /// <param name="__p2"></param>
    public void OnFrameReady(object __p1,  EventArgs __p2)
    {
        Debug.WriteLine("OnFrameReady is called");
        FGEventArgs args = (FGEventArgs)__p2;
        Guid.High = args.High;
        Guid.Low = args.Low;

        if (Guid.Low == Cam.m_Guid.Low)
        {
            Result = Cam.GetFrame(Frame, 0);
            // Process frame, skip FrameStart notification
            if (Result == enFireWrapResult.E_NOERROR & Frame.Length > 0)
            {
                byte[] data = new byte[Frame.Length];

                // Access to frame data
                if (Frame.CloneData(data))
                {
                    DisplayAndSaveImageFromByteArray(data);
                    // Here you can start your image processsing logic on data
                    string debug = String.Format("[{6}] Frame #{0} length:{1}byte [ {2} {3} {4} {5} ... ]",
                        Frame.Id, Frame.Length, data[0], data[1], data[2], data[3], Cam.m_Guid.Low);
                    Debug.WriteLine(debug);
                }
                // Return frame to module as fast as posible after this the Frame is not valid 
                Result = Cam.PutFrame(Frame);
            }
        }

    }

    public void DisplayAndSaveImageFromByteArray(byte[] byteArray)
    {
        try{
            byte[] data = new byte[width * height * 4];
            int o = 0;

            for (int io = 0; io < width * height; io++){
                byte value = byteArray[io];

                data[o++] = value;
                data[o++] = value;
                data[o++] = value;
                data[o++] = 0;
            }
            unsafe
            {
                fixed (byte* ptr = data)
                {
                    using (image = new Bitmap((int)width, (int)height, (int)width * 4,System.Drawing.Imaging.PixelFormat.Format32bppRgb, new IntPtr(ptr)))
                    {
                        image.Save(@"c:\\testmap\" + nextpicture + ".jpg", ImageFormat.Jpeg);
                        if (nextpicture >= 10)
                        {
                            pbCameraPreview.BeginInvoke(new InvokeDelegate(InvokeMethod));
                        }
                        nextpicture++;
                    }
                }
            }
        }
        catch (Exception ex){
            MessageBox.Show(ex.ToString());
        }

    }
    public void InvokeMethod()
    {
        pbCameraPreview.Image = Image.FromFile(@"c:\\testmap\" + (nextpicture -10) + ".jpg");
    }

}
public class CameraCode : FireWrap_Camera
{
    public FGUIntHL m_Guid;
}}

正在运行的线程: 我记录了它以获取更多信息: https://www.youtube.com/watch?v=i3TxWRyZaIU

【问题讨论】:

  • 能否将您的代码粘贴为代码而不是屏幕截图
  • 让我再试一次,由于某种原因它不允许我第一次粘贴它。
  • 路径中的\\应该是\。你是byteArraygrayscale 吗?因为您的代码试图将其转换为data 中的“彩色”图像(灰色阴影)。 width/height/nextImage 的声明/数据类型是什么?另外,您能否创建一个完整且最小的示例程序来重现您的问题?无论如何,很可能不在粘贴的代码中,因为我用随机的byte[] 输入运行了几百万次并且它工作(假设widthheight 的数据类型是const uint 并且两者都很小足以使案例不会造成溢出)
  • 让我看看我能想出什么。可能需要一段时间。
  • byteArray:byteArray 是灰度的。宽度:首先是一个 Uint32,然后转换为位图的 int。高度:首先是 Uint32,然后转换为位图的 int。 nextpicture:只是保存图像数量的计数器。所以它将保存下一张具有更高价值的图像。

标签: c# arrays image bitmap


【解决方案1】:

我不是 100% 确定我已经理解了您的问题,因为输入数组的格式不是很清楚,以及在将其解析为 Bitmap 变量之前您必须如何对其进行格式化...但是在这里我们去吧,我希望这些提示可以帮助你。如果他们不这样做,请尝试提供一些有关您正在尝试做的事情的额外细节。

首先,如果我理解的很好,你应该在主循环中每次将它分配给data[o++]时增加“io”并更新变量“value”,否则你将相同的值分配给R 、G 和 B 像素,这将始终产生灰色阴影。

其次,我在您的代码中看到了一些不太符合 .net 的内容... .Net 已经提供了使用内存流等从字节数组加载图像的方法。看看How to create bitmap from byte array?

并且在实例化位图或图像时一定要指明你的字节数组图像的正确格式 --> https://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat(v=vs.110).aspx

问候。

【讨论】:

  • 感谢您的回复!首先,这是正确的,我需要它是灰色的。其次,我知道 .Net 有一种提供简单方法的方法,我在其他网络摄像头应用程序中使用过这种方法,但这是使用工业相机并且仅提供像素数据作为字节数组。
  • 如果您密切关注代码,您可能已经注意到我使用的代码与某人在此处发布的完全相同。
  • 哦...好吧,我第一次没有做对。很抱歉,在这种情况下我没有任何建议。
【解决方案2】:
  • 如果您在 StackOverflow 中发布代码时遇到问题,请确保每行缩进至少 4 个空格(在 Visual Studio 中标记代码,按 Tab,然后复制它)并由任何其他段落分隔至少一个空行。或者在代码的开头和和添加一行三个反引号 (`)。

  • 在查看您的代码时,在我看来,您在 Windows 窗体中使用了第三方控件,并尝试将其事件绑定到您的 Windows 窗体事件处理程序之一。在这种情况下,您必须注意 Windows 窗体期望所有事件都以单线程处理(在事件调度线程中),因此如果 InvokeRequired 是 @987654327,您应该在 OnFrameReady 方法中检查 (Debug.WriteLine) @ 如果是的话,你必须采取一些预防措施:

    • 如果未将调用封装到 InvokeBeginInvoke 中,切勿访问任何 Form 的内部成员(如 pbCameraPreview)。请记住,在单个事件调度线程可用之前,每个 Invoke 调用都会有效地阻塞,因此同步调用会消耗大量性能。

    • 当访问您自己的成员(如widthheightnextpicture)时,请确保使用适当的locking 以避免在您的情况下一个线程/回调更改值的情况不要指望它。特别是,由于您有多个摄像头但只有一个宽度/高度变量,如果分辨率不同,一个摄像头回调可能会更改宽度,而另一个摄像头回调刚刚分配字节数组但在将宽度传递给 Bitmap构造函数,很可能导致挂起、崩溃或内存访问冲突。通过将宽度和高度作为变量传递给您的方法而不是使用全局变量来避免这种情况。

  • 表单的事件处理程序 (btStart_Click) 包含一个忙等待循环。这有多个问题:

    • 如前所述,每个 Form 事件处理程序都在同一个线程中运行(每个 Invoke 也是如此)。因此,只要您的循环忙于等待,就无法处理其他事件,并且 UI 将完全冻结。如果代码使用Invoke,该代码最终也将不得不等待并被阻止。
    • 没有任何睡眠代码的繁忙等待循环将导致您的处理器以全 cpu 速度运行,它会占用 100% 的内核,从而导致笔记本电脑电池耗尽和高功耗,并且可能在其他机器上风扇噪音很大。只是不要那样做。

在您的具体示例中,只需删除循环并将循环之后的所有内容移至 stop 按钮即可。为了避免 Windows 窗体和调用的问题,我建议将所有图像处理逻辑放入一个单独的类中,该类执行自己的锁定(类的每个实例仅处理 one 相机),并且只需调用它即可开始该过程。您甚至可能更愿意先将您的应用程序作为控制台应用程序来避免任何事件分派问题,然后在它按您希望的方式工作后将其转换为 Windows 窗体应用程序。

【讨论】:

  • 感谢您的回复!该代码来自我正在使用的 SDK 示例。 (来自 AlliedVision 的 FireWrap.Net)它最初位于控制台应用程序中,我尝试将其转换为用于 GUI 的 Windows 窗体应用程序。我会尝试“清理代码”。该代码似乎适用于多台相机,但我只使用一台相机,我还没有解决所有这些问题。我首先担心图像保存问题,所以我试图搜索它的帮助。感谢您的建议。
  • @ThatInternGuy 您是否尝试过在程序挂起时中断程序并在其确切位置获取线程堆栈跟踪?
  • 是的,我试过了,但它只是冻结了。一旦我将相机移到较亮的位置,像素数据就会发生变化,它会多拍大约 10 帧,但随后会挂起。我知道它停在哪里,它在我改变房间照明后的第 10 次 onframeready 呼叫时停止。当它发生时我没有收到错误。
  • 这是我能找到的唯一调用堆栈 > Camera.exe!Camera.Program.Main() 第 16 行 C#
  • 没有更多的帧?没有其他线程在运行?那么可能是第三方网络摄像头库崩溃或不再调用您的回调?也许问他们的支持? :D -> 你发布的代码看起来不错,问题很可能在其他地方......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-21
  • 2014-05-29
  • 2017-06-10
  • 1970-01-01
相关资源
最近更新 更多