在C#中转换图像

6

编辑:已解决! 详细信息请参见下面我的答案。 我找不到原问题的答案,但我找到了一种替代方案。

也许在其他地方有人问过这个问题,但我已经搜索了几天,没有找到任何有用的信息。

问题:我需要一次性将“Stream”转换为“image(bgr,byte)”,是否有一种方法/命令可以直接从System.Drawing.Image.FromStream转换为Emgu.CV.Image(Bgr,Byte),而不必先将其从stream转换为image再转换为bitmap最后再转换为image(bgr, byte)

信息:我的论文项目中使用c#在Visual Studio 2010中编写代码。我从网络上的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>();

我知道有一种方法可以暂时在本地保存图片,但出于安全考虑,我需要避免使用这种方法。我更希望能直接转换以帮助节省处理能力。

我是否忽略了什么?所有的帮助都将不胜感激。

谢谢您的阅读。(如果有人需要更多细节,我会更新)- Dave


你可以使用 Stopwatch 类来测量每行代码的性能吗?(针对最后三行) - Vano Maisuradze
我没有用那个来测量性能,但如果你给我一点时间,我会拼凑一些东西并回复评论。最后三行是它变慢的原因,因为它正在进行5次转换,绘制到图像框和渲染算法输出,同时准备下一帧。 - Hughsie28
@stopwatch_class 表示完成这三行任务花费了00:00:00:0343616的时间(我想这是以毫秒为单位的)。 - Hughsie28
你有考虑/尝试使用多核吗? - Lasse V. Karlsen
我还没有尝试过多核处理,但我知道它的存在。相机图像我认为是以JPEG或MJPEG格式到达的。 - Hughsie28
显示剩余2条评论
3个回答

3
我相信我找到了解决我的问题的答案。我尝试了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

3
您有几个可能的瓶颈,其中最主要的是您可能会将jpeg解码流转换为图像,然后将其转换为位图,再转换为openCV图像。
一种绕过这个问题的方法是完全绕过.NET成像。这将涉及尝试直接使用libjpeg。在C#中有一个免费移植版本,我IRC可以钩入它以在每个扫描线基础上调用来填充缓冲区。
缺点是您正在托管代码中解码JPEG数据,这将比等效的C运行至少慢1.5倍,尽管老实说,我期望网络速度会极大地超越这一点。
OpenCV应该能够直接读取jpeg图像(想猜一下他们在底层使用什么吗?调查显示:libjpeg),这意味着您可以缓冲整个流并将其交给OpenCV,从而完全绕过.NET层。

2

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

类似这样:

//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规格,所以应该能够尝试这种方法。 - Hughsie28

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接