Imágenes corruptas, al capturar desde múltiples cámaras usando la API V4L2

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.

Imagen guardada de carema usando v4l2 y libpngUna vez que empiezo a capturar imágenes de varias cámaras (sucesivamente), obtengo imágenes corruptas. Imagen corrupta al tomar una imagen de varias cámarasCuantas 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);
}
Esta es una pregunta muy límite para este SE, cualquier cosa relacionada con la programación debe preguntarse en StackOverflow en su lugar.
¿Está seguro de que la velocidad de fotogramas y las dimensiones de los fotogramas son uniformes entre las cámaras? Eso se ve sospechosamente como una sincronización de línea incorrecta, lo que resultaría de tener la altura y el ancho y/o la velocidad de fotogramas incorrectos.

Respuestas (2)

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.