Friday, July 2, 2010

Axis Camera Control

For my live image processing, I tend to use Axis products. Their cameras provide the images as a motion JPEG stream via Ethernet, while their video servers can convert full PAL/NTSC video to motion JPEG streams via Ethernet. It's handy, no real video converters required. Just plug your computer onto the same network as cameras, and bam!

There's a few way to get the images. Individual images can be accessed using an HTTP request:
http:///axis-cgi/jpg/image.cgi?resolution=704x576&camera=1
If you wanted to do that in C#:
System.Net.HttpWebRequest myReq;
System.Net.HttpWebResponse myResp;
myReq = (System.Net.HttpWebRequest)(System.Net.WebRequest.Create("http://136.18.33.16/axis-cgi/jpg/image.cgi?resolution=704x576&camera=1");
myReq.Timeout = 500;
myResp = (System.Net.HttpWebResponse)(myReq.GetResponse());
Bitmap bmp = new Bitmap(Image.FromStream(myResp.GetResponseStream()));
Easy. For easy access of motion video, Axis has provided the handy AxAxisMediaControl as part of their Windows SDK.

I've been using this for years, but have recently noticed a few problems. The main one being that the camera control has to always be visible in your form, otherwise it will just loop the last received image. So no screensavers, the program has to always be on top, and don't even think of hiding the Axis control (which is what I did, I wanted to hide the live stream). Also, to get the image into C# required some memory manipulation and even then the images were coming in upside down.

Despite trolling through the forums and support, there's no fix for this. It seems like I'm not the only one. Now this was a problem for me. I had a safety-critical program that was analysing 15+ frames a second 24 hours a day and could not be turned off. However, we needed to lock the computer up so that no one else could access the computer. Once we locked up the computer, the whole thing stopped working.

I tried work around, I tried accessing the image frame by frame using the HTTP request, but it was too slow. So in frustration, I created my own custom AxisCameraControl (download the code here).

Although not as complete as the Axis one (there's no audio or PTZ) it's perfect for receiving just motion JPEG streams. It has support for
  • multiple camers for video servers
  • password protected devices
  • limiting of maximum frame rate
  • different video sizes
  • autoreconnect on stream loss
  • limiting JPEG compression
The control is not a visible control, unlike the Axis one. It is more of a background threaded class. It has a lot of the same options as the original Axis control, but the following are always required:
public string IP;
public int Width;
public int Height;
public int Framerate;
When running (by calling public void Start()), a worker thread runs in the background. It opens up a motion JPEG stream to the device and looks for the start of each image. At this point, I have to credit/thank http://www.codeproject.com/KB/audio-video/cameraviewer.aspx for providing me with the motion JPEG code.

Once an individual JPEG is decoded from the stream, it throws an event.
Bitmap bmp = (Bitmap)Bitmap.FromStream(new MemoryStream(buffer, start, stop - start));
AxisArgs ax = new AxisArgs(bmp);
OnNewImage(this, ax);
The argument for the event (AxisArgs) is a Bitmap, allowing the image to be passed by value, not by reference like the old Axis control. After all of this, the image is disposed and everything is started again.

Usage in a program is fairly easy:
AxisCameraControl.Camera axisCameraControl1 = new AxisCameraControl.Camera();
axisCameraControl1.IP = "136.18.33.16";

axisCameraControl1.Width = width;
axisCameraControl1.Height = height;
axisCameraControl1.Framerate = frameRate;
axisCameraControl1.OnNewImage += new AxisCameraControl.Camera.NewImage(axisCameraControl1_OnNewImage);
if (!axisCameraControl1.IsPlaying())
axisCameraControl1.Start();

void axisCameraControl1_OnNewImage(object sender, AxisCameraControl.AxisArgs args)
{
Bitmap bmp = new Bitmap(args.Image());
//do something with bmp
pictureBox1.Image = bmp;
}
Simple! And it doesn't have to be visible to work. I've been testing this for the last month, and so far there seems to be nothing wrong with it. I've got a lot more work to do before it has the same functionality as the original AxAxisCameraControl, so I will update as it progresses.

Download the C# camera control class

Bugs:
  • On some servers, can take a few seconds to get the image started
  • If updating to a pictureBox with a high frame rate, the maximum frame rate drops dramatically for large images. Better to update pictureBox with an image every few frames for a frame rate over 8 FPS at an image size greater then 640x480

No comments:

Post a Comment