Me gustaría capturar imágenes de varias cámaras en Linux. Seguí esta presentación, usé el código y lo mejoré para mi propósito. Funciona muy bien para 1 cámara.
Una vez que empiezo a capturar imágenes de varias cámaras (sucesivamente), obtengo imágenes corruptas.
Cuantas más cámaras demando, más artefactos/rayas aparecen en las imágenes. No hace ninguna diferencia si guardo las imágenes como BMP usando otro código. Así que supongo que el problema no tiene nada que ver con la rutina de almacenamiento. La resolución también es correcta (744 * 480).
El resultado es el mismo en dos computadoras diferentes que ejecutan Fedora y Debian. Estoy absolutamente desconcertado y no puedo encontrar ninguna pista de lo que está saliendo mal. ¿Podría por favor alguien dar algunos consejos?
Aquí está mi código
int main()
{
/* #################### INIT #################### */
int numOfCameras = 1;
int xRes = 744;
int yRes = 480;
int exposure = 2000;
unsigned int timeBetweenSnapshots = 2; // in sec
char fileName[sizeof "./output/image 000 from camera 0.PNG"];
static const char *devices[] = { "/dev/video0", "/dev/video1", "/dev/video2", "/dev/video3", "/dev/video4", "/dev/video5", "/dev/video6", "/dev/video7" };
struct v4l2_capability cap[8];
struct v4l2_control control[8];
struct v4l2_format format[8];
struct v4l2_requestbuffers req[8];
struct v4l2_buffer buffer[8];
int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // had to declare the type here because of the loop
unsigned int i;
unsigned int j;
unsigned int k;
int fd[8];
void **mem[8];
/* #################### OPEN DEVICE #################### */
for (j = 0; j < numOfCameras; ++j) {
fd[j] = open(devices[j], O_RDWR);
ioctl(fd[j], VIDIOC_QUERYCAP, &cap[j]);
/* #################### CAM CONTROLL #################### */
control[j].id = V4L2_CID_EXPOSURE_AUTO;
control[j].value = V4L2_EXPOSURE_SHUTTER_PRIORITY;
ioctl(fd[j], VIDIOC_S_CTRL, &control[j]);
control[j].id = V4L2_CID_EXPOSURE_ABSOLUTE;
control[j].value = exposure;
ioctl(fd[j], VIDIOC_S_CTRL, &control[j]);
/* #################### FORMAT #################### */
ioctl(fd[j], VIDIOC_G_FMT, &format[j]);
format[j].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
format[j].fmt.pix.width = xRes;
format[j].fmt.pix.height = yRes;
//format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
format[j].fmt.pix.pixelformat = V4L2_PIX_FMT_GREY;
ioctl(fd[j], VIDIOC_S_FMT, &format[j]);
/* #################### REQ BUF #################### */
req[j].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req[j].count = 4;
req[j].memory = V4L2_MEMORY_MMAP;
ioctl(fd[j], VIDIOC_REQBUFS, &req[j]);
mem[j] = malloc(req[j].count * sizeof(*mem));
/* #################### MMAP #################### */
for (i = 0; i < req[j].count; ++i) {
buffer[j].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer[j].memory = V4L2_MEMORY_MMAP;
buffer[j].index = i;
ioctl(fd[j], VIDIOC_QUERYBUF, &buffer[j]);
mem[j][i] = mmap(0, buffer[j].length,
PROT_READ|PROT_WRITE,
MAP_SHARED, fd[j], buffer[j].m.offset);
}
/* #################### CREATE QUEUE #################### */
for (i = 0; i < req[j].count; ++i) {
buffer[j].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer[j].memory = V4L2_MEMORY_MMAP;
buffer[j].index = i;
ioctl(fd[j], VIDIOC_QBUF, &buffer[j]);
}
} /* ### ### end of camera init ### ### */
/* ##################### STREAM ON #################### */
for (j = 0; j < numOfCameras; ++j) {
ioctl(fd[j], VIDIOC_STREAMON, &type);
}
/* ##################### GET FRAME ##################### */
k = 0;
while (!kbhit()){
k ++;
for (j = 0; j < numOfCameras; j++) {
buffer[j].type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buffer[j].memory = V4L2_MEMORY_MMAP;
usleep(100000);
ioctl(fd[j], VIDIOC_DQBUF, &buffer[j]);
// create filename
sprintf(fileName, "./output/image %03d from camera %d.PNG", k, j);
// save as PNG file
saveToPng(mem[j][buffer[j].index], fileName, xRes, yRes);
ioctl(fd[j], VIDIOC_QBUF, &buffer[j]);
sleep(timeBetweenSnapshots);
}
}
/* ##################### STREAM OFF ##################### */
for (j = 0; j < numOfCameras; ++j) {
ioctl(fd[j], VIDIOC_STREAMOFF, &type);
}
/* ##################### CLEANUP ##################### */
for (j = 0; j < numOfCameras; ++j) {
close(fd[j]);
free(mem[j]);
}
return (0);
}
Su problema es probablemente que no tiene suficiente ancho de banda USB disponible, si sus cámaras web lo admiten, cambie a MJPEG en lugar de fotogramas sin comprimir. Por lo general, cualquier cámara web admite la codificación MJPEG para enviar fotogramas a su PC.
Aquí una pregunta similar sobre SO https://stackoverflow.com/questions/9781770/capturing-multiple-webcams-uvcvideo-with-opencv-on-linux
Estoy bastante seguro de que está obteniendo la corrupción debido a usleep (100000); - Esto se basa en el hecho de que logré una corrupción similar usando una cámara web al agregar usleep (100000) a mi código.
Mi sueño (100000); es una sustitución para ejecutar un kernel opencl, lo que significa que en este punto sé que el problema es que usleep (100000) afecta la entrega de cuadros de video de alguna manera (esto sucede en mi código si elimino todo el otro código y solo guardo los cuadros en disco como archivos ppm).
lo que no se es porque....
Puedo poner el usleep en cualquier parte de mi código, el efecto es siempre el mismo.
Tal vez el código de la cámara web de video4linux deba estar en un subproceso diferente.
timonsku
AJ Henderson