在 C# 中转换图像

Converting Image in c#

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

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

问题:我需要一次性把"Stream"转成"image(bgr, byte)",有没有way/command 直接从 System.Drawing.Image.FromStream 转换为 Emgu.CV.Image(Bgr, Byte) 而无需从 stream 转换为 imagebitmapimage(bgr, byte)?

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

(我知道我目前的方法效率极低,这就是我来这里的原因,我实际上将图像总共转换了 5 次(甚至灰度缩放),实际上只使用了我处理器内存的一半(i7, 8GB 内存))。它必须是 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>();

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

感谢阅读。 (如果有人需要更多细节,我会更新这个) -戴夫

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

像这样:

//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 来将图像存储在内存中,并且您还应该调整缓冲区大小以满足您的要求。

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

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

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

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

我相信我找到了问题的答案。我已经尝试使用 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