4

Background

I'm developing a C# WinForms application uses OpenCvSharp4 to capture images from one or more connected webcam on demand.

I currently have code that captures from the first camera using var capture = new VideoCapture(0). This works great for the first connected camera (i.e. index 0), and any other index I specify manually if the camera is connected.

Question

I want each deployment to dynamically choose the cameras to capture from. Other than trial and error, how can I find the index of all available webcams on the current system so I can create a list of camera indexes to cycle through later?

Intended usage

I was hoping to use it something like this:

void main()
{
  var cameras = GetAvailableCameras();
  foreach (var idx in cameras)
  {
    var capture = VideoCapture(idx);
    // capture the image...
  }
}

IEnumerable<int> GetAvailableCameras()
{
  // do something here to get the available indexes
}

Caveats

To keep the program a lean as possible, I would like to avoid adding any additional third party libraries, which is why I have not used AForge.Net even though I know there are published solutions using that library.

I would consider adding additional Microsoft library if it simplifies things.

Jens Ehrich
  • 573
  • 1
  • 6
  • 19
  • I don't have multiple cameras to try it but give the following a try I think the return order might be the same: https://stackoverflow.com/a/62555468/1599751 – PeterJ Oct 04 '20 at 02:10
  • @PeterJ this looks quite simple to implement, but unfortunately doesn't meet my (soft) requirement of not adding additional 3rd party libraries. – Jens Ehrich Oct 08 '20 at 13:53

1 Answers1

2

Do the INDEXES on the system cameras list NOT MATCH the camera accessed by OpenCvSharp?

I discovered a very weird behaviour of OpenCvSharp, and a way to correct it.

In order to create a VideoCapture object you need to provide a "camera index". In theory, this index should match the indexes on the list of cameras provided by the system (as explained in stackoverflow.com/a/62555468/1599751). However, if the system has an "default" camera then OpenCvSharp will consider that camera index 0, sliding the rest of the cameras indexes. So, if the system gave you this list of cameras:

  1. Camera 0
  2. Camera 1
  3. Camera 2 (default)
  4. Camera 3
  5. Camera 4

This is how OpenCvSharp would behave:

var cap0 = new VideoCapture(0); // Opens camera 2 !!
var cap1 = new VideoCapture(1); // Opens camera 0 !!
var cap2 = new VideoCapture(2); // Opens camera 1 !!
var cap3 = new VideoCapture(3); // Opens camera 3
var cap4 = new VideoCapture(4); // Opens camera 4

The documentation of VideoCapture.Open describes the index parameter as:

id of the video capturing device to open. To open default camera using default backend just pass 0. (to backward compatibility usage of camera_id + domain_offset (CAP_*) is valid when apiPreference is CAP_ANY).

That's a bit obscure for me, as I could not find any explanation of what the domain_offset was. So, I coded a for loop passing every possible integer value as index, and I discovered that (at least on my computer) you can also create VideoCapture objects by adding 700 (or 1400) to the index. And when doing this, the indexes do match the indexes from the system. None of this "sending the default camera to the top" business.

var cap0 = new VideoCapture(700); // Opens camera 0
var cap1 = new VideoCapture(701); // Opens camera 1
var cap2 = new VideoCapture(702); // Opens camera 2
var cap3 = new VideoCapture(703); // Opens camera 3
var cap4 = new VideoCapture(704); // Opens camera 4

If anyone know how this domain_offset works, please let us know.

Cesar Coll
  • 105
  • 10
  • In `OpenCVSharp.VideoCaptureAPIs` 700 points to DirectShow and 1400 to MSMF. Hoping that explains the observed behavior. Alternative way to initialize the device is `var cap = new VideoCapture(x, VideoCaptureAPIs.DSHOW)` where x is the input number in the human-observed order. – Ivy Growing Dec 05 '22 at 15:01