简体   繁体   中英

Converting Image in c#

Edit: SOLVED! Please see my answer down below for details. I was unable to find an answer to the original question but I found an alternate solution

This question may be asked somewhere else but I have been searching for days and can't find anything that helps.

Question: I need to convert "Stream" to "image(bgr, byte)" in one go, Is there a way/command to convert directly from System.Drawing.Image.FromStream to Emgu.CV.Image(Bgr, Byte) without converting from stream to image to bitmap to image(bgr, byte) ?

Information: I'm coding in c# in Visual Studio 2010 as part of my dissertation project. I am taking a image stream from an IP camera on a network and applying many algorithms to detect faces/extract facial features and recognise an individuals face. On my laptops local camera I can achieve FPS of about 25~ (give or take) including algorithms because I don't have to convert the image. For an IP camera stream I need to convert it many times to achieve the desired format and the result is around 5-8fps.

(I know my current method is extremely inefficient which is why I'm here, I'm actually converting an image 5 times total (even gray scaling too), actually only using half of my processors memory (i7, 8gb RAM)). It does have to be image(bgr, byte) as that is the only format the algorithms will function with.

The code I'm using to get the image:

//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();

I have alot of stuff in the background managing connections, data, security etc which I have shortened to the above code. My current code to covert the image to the desired output:

//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>();

I understand there is a method to save an image locally temporarily but I would need to avoid that for security purposes. I'm looking more for a direct conversion to help save processing power. Am I overlooking something? All help is appreciated.

Thanks for reading. (I will update this if anyone requests any more details) -Dave

You've got a couple potential bottlenecks, not the least of which is that you're probably jpeg decoding the stream into an image and then converting that into a bitmap and then into an openCV image.

One way around this is to bypass the .NET imaging entirely. This would involve trying to use libjpeg directly. There's a free port of it here in C#, and IIRC you can hook into it to get called on a per-scanline basis to fill up a buffer.

The downside is that you're decoding JPEG data in managed code which will run at least 1.5X slower than equivalent the C, although quite frankly I would expect network speed to dwarf this immensely.

OpenCV should be able to read jpeg images directly (wanna guess what they use under the hood? Survey says: libjpeg), which means that you can buffer up the entire stream and hand it to OpenCV and bypass the .NET layer entirely.

I believe I found the answer to my problem . I have dabbled using Vano Maisuradze 's idea of processing in memory which improved the fps a tiny margin (not immediately noticable without testing). And also thanks to Plinths answer I have a understanding of Multi-Threading and I can optimise this as I progress as I can split the algorithms up to work in parallel.

What I think is my cause is the networking speed! not the actual algorithm delay. As pointed out by Vano with the stopwatch to find the speed the algorithms didn't actually consume that much. So with and without the algorithms the speed is about the same if I optimise using threading so the next frame is being collected as the previous one finishes processing.

I did some testing on some physical Cisco routers and got the same result if a bit slower messing round with clock speeds and bandwidths which was noticeable. So I need to find out a way to retrieve frames over networks faster, Very big thank you to everyone who answered who helped me understand better!

Conclusion:

  • Multi-threading to optimise
  • Processing in memory instead of converting constantly
  • Better networking solutions (Higher bandwidth and speeds)

Edit: The code to retrieve an image and process in memory for anyone who finds this looking for help

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

You can save several image in memory (buffer) and then start processing from buffer.

Something like this:

//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...
    }
}

But note that you will need enough RAM to store images in memory and also you should adjust buffer size to meat your requirements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM