【问题标题】:Converting Image in c#在 C# 中转换图像
【发布时间】:2015-05-27 20:41:17
【问题描述】:

编辑:已解决!请在下面查看我的答案以了解详细信息。 我无法找到原始问题的答案,但我找到了替代解决方案

这个问题可能会在其他地方被问到,但我已经搜索了好几天,找不到任何有用的东西。

问题:我需要一次性将“Stream”转换为“image(bgr, byte)”,有没有办法/命令直接从系统转换.Drawing.Image.FromStream 到 Emgu.CV.Image(Bgr, Byte) 不从 stream 转换为 imagebitmap图像(bgr,字节)

信息:作为我的论文项目的一部分,我在 Visual Studio 2010 中使用 c# 进行编码。 我正在从网络上的 IP 摄像机获取图像流,并应用许多算法来检测面部/提取面部特征并识别个人面部。在我的笔记本电脑本地摄像头上,我可以实现大约 25~(给或取)的 FPS,包括算法,因为我不必转换图像。对于 IP 摄像机流,我需要对其进行多次转换以达到所需的格式,结果约为 5-8fps。

(我知道我目前的方法效率极低,这就是我在这里的原因,我实际上将图像总共转换了 5 次(甚至是灰度缩放),实际上只使用了我的处理器内存的一半(i7、8gb RAM ))。它必须是 image(bgr, byte),因为这是算法将使用的唯一格式。

我用来获取图像的代码:

//headers
using System.IO
using System.Threading;
using System.Net;
//request a connection
req = (HttpWebRequest)HttpWebRequest.Create(cameraUrl);
//gives chance for timeout for errors to occur or loss of connection
req.AllowWriteStreamBuffering = true;
req.Timeout = 20000;
//retrieve response (if successfull)
res = req.GetResponse();
//image returned
stream = res.GetResponseStream();

我在后台管理连接、数据、安全等方面有很多东西,我已将其简化为上述代码。 我当前将图像转换为所需输出的代码:

//Convert stream to image then to bitmap
Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));                    
//Convert to emgu image (desired goal)
currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
//gray scale for other uses
gray = currentFrame.Convert<Gray, Byte>();

我知道有一种方法可以暂时在本地保存图像,但出于安全考虑,我需要避免这种情况。我正在寻找更多的直接转换来帮助节省处理能力。 我忽略了什么吗?感谢所有帮助。

感谢阅读。 (如果有人要求更多详细信息,我会更新此内容) -戴夫

【问题讨论】:

  • 您可以使用 Stopwatch 类来衡量每条线路的性能吗? (最后 3 行)
  • 我没有用它来衡量性能,但如果你给我一点时间,我会一起拍一些东西并发表评论。最后 3 行是它进行 5 次转换、绘制到图像框并渲染算法输出同时准备下一帧的原因
  • @VanoMaisuradze 秒表类表明完成这 3 行的任务需要 00:00:00:0343616 (想想以毫秒为单位)。
  • 您从相机中得到什么样的图像?我怀疑它是 jpeg。
  • 您是否考虑过/尝试过使用多核?

标签: c# image emgucv type-conversion


【解决方案1】:

我相信我找到了问题的答案。我曾尝试使用 Vano Maisuradze 的内存处理理念,该理念将 fps 提高了很小的幅度(未经测试不会立即注意到)。还要感谢 Plinths 的回答,我对多线程有所了解,并且可以随着我的进步进行优化,因为我可以将算法拆分为并行工作。

我认为我的原因是网络速度!不是实际的算法延迟。正如 Vano 用秒表指出的那样,算法实际上并没有消耗那么多的速度。因此,无论有无算法,如果我使用线程进行优化,速度大致相同,因此下一帧将在前一帧完成处理时被收集。

我在一些物理 Cisco 路由器上进行了一些测试,如果时钟速度和带宽稍微变慢一点,则得到了相同的结果,这很明显。所以我需要找到一种方法来更快地通过网络检索帧,非常感谢所有回答的人帮助我更好地理解!

结论:

  • 多线程优化
  • 在内存中处理而不是不断转换
  • 更好的网络解决方案(更高的带宽和速度)

编辑:在内存中检索图像和进程的代码,供任何发现此内容寻求帮助的人使用

public void getFrames(object sender, EventArgs e)
{//Gets a frame from the IP cam
    //Replace "IPADDRESS", "USERNAME", "PASSWORD" 
    //with respective data for your camera
    string sourceURL = "http://IPADDRESS/snapshot.cgi?user=USERNAME&pwd=PASSWORD";
    //used to store the image retrieved in memory
    byte[] buffer = new byte[640 * 480];
    int read, total = 0;

    //Send a request to the peripheral via HTTP
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(sourceURL);
    WebResponse resp = req.GetResponse();

    //Get the image capture after recieving a request
    //Note: just a screenshot not a steady stream
    Stream stream = resp.GetResponseStream();
    while ((read = stream.Read(buffer, total, 1000)) != 0)
    {
        total += read;
    }//While End

    //Convert memory (byte) to bitmap and store in a picturebox    
    pictureBox1.Image = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, 0, total));
}//getFrames End

private void button1_Click(object sender, EventArgs e)
{//Trigger an event to start running the function when possible
    Application.Idle += new EventHandler(getFrames);
}//Button1_Click End

【讨论】:

    【解决方案2】:

    您有几个潜在的瓶颈,其中最重要的是您可能正在将流 jpeg 解码为图像,然后将其转换为位图,然后再转换为 openCV 图像。

    解决此问题的一种方法是完全绕过 .NET 映像。这将涉及尝试直接使用 libjpeg。 C# 中有一个 free port of it here,您可以将 IIRC 挂接到它,以便在每个扫描线的基础上调用它来填充缓冲区。

    缺点是您在托管代码中解码 JPEG 数据,它的运行速度至少比等效 C 慢 1.5 倍,但坦率地说,我希望网络速度大大降低。

    OpenCV 应该能够直接读取 jpeg 图像(想猜猜它们在后台使用什么?调查显示:libjpeg),这意味着您可以缓冲整个流并将其交给 OpenCV 并完全绕过 .NET 层.

    【讨论】:

      【解决方案3】:

      您可以在内存(缓冲区)中保存多个图像,然后从缓冲区开始处理。

      类似这样的:

      //Convert stream to image then to bitmap
      Bitmap bmpImage = new Bitmap(System.Drawing.Image.FromStream(stream));                    
      //Convert to emgu image (desired goal)
      currentFrame = new Emgu.CV.Image<Bgr, Byte>(bmpImage);
      
      //gray scale for later use
      gray = currentFrame.Convert<Gray, Byte>();
      SaveToBuffer(gray);
      
      Queue<Emgu.CV.Image<Gray, Byte>> buffer = new Queue<Emgu.CV.Image<Gray, Byte>>();
      bool canProcess = false;
      
      // ...
      
      private void SaveToBuffer(Emgu.CV.Image<Gray, Byte> img)
      {
          buffer.Enqueue(img);
          canProcess = buffer.Count > 100;
      }
      
      private void Process()
      {
          if(canProcess)
          {
              buffer.Dequeue();
              // Processing logic goes here...
          }
          else
          {
              // Buffer is still loading...
          }
      }
      

      但请注意,您将需要足够的 RAM 来将图像存储在内存中,并且您应该调整缓冲区大小以满足您的要求。

      【讨论】:

      • 这个主意不错,我研究一下内存转换的方法。我有一些很好的 RAM 规格,所以我应该可以尝试这种方法。
      猜你喜欢
      • 1970-01-01
      • 2013-06-17
      • 2011-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      • 2013-02-26
      • 1970-01-01
      相关资源
      最近更新 更多