You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用V4L与C捕获Elgato Facecam时UYVY转RGB颜色异常问题

问题:V4L捕获Elgato Facecam图像颜色异常(绿/洋红色)

我正在尝试用V4L和C语言捕获Elgato Facecam的图像,相关教程匮乏,因此基于AI生成的代码和少量示例代码修改调试,但目前捕获的图像始终呈现绿色和洋红色。

我试过调整YUV转RGB公式、值范围,也确认过像素格式(认为UYVY是正确格式),得到过不同的颜色组合,甚至出现过三种颜色,但始终无法显示正常全色。Stack Overflow上的相关解决方案也尝试过,均无效。

以下是我的主代码:

#include <sys/mman.h>
#include <linux/videodev2.h>
#include <jpeglib.h>

#define VIDEO_DEVICE "/dev/video2"
#define CAPTURE_WIDTH 1920
#define CAPTURE_HEIGHT 1080
#define BUFFER_SIZE (CAPTURE_WIDTH * CAPTURE_HEIGHT * 2)

// YUV转RGB函数
void yuv2rgb(unsigned char *yuv, unsigned char *rgb, int width, int height) {
    int i, j;
    int y, u, v;
    int r, g, b;
    int index_yuv, index_rgb;

    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j += 2) {
            index_yuv = (i * width + j) * 2;
            index_rgb = (i * width + j) * 3;

            y = yuv[index_yuv];
            u = yuv[index_yuv + 1];
            v = yuv[index_yuv + 3];

            // 将U、V范围调整为-128到127
            u -= 128;
            v -= 128;

            // YUV转RGB转换
            r = y + 1.402 * v;
            g = y - 0.344136 * u - 0.714136 * v;
            b = y + 1.772 * u;

            // 限制值在0-255范围内
            r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
            g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
            b = (b < 0) ? 0 : ((b > 255) ? 255 : b);

            rgb[index_rgb] = r;
            rgb[index_rgb + 1] = g;
            rgb[index_rgb + 2] = b;

            y = yuv[index_yuv + 2];

            // 第二个像素的YUV转RGB转换
            r = y + 1.402 * v;
            g = y - 0.344136 * u - 0.714136 * v;
            b = y + 1.772 * u;

            // 限制值在0-255范围内
            r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
            g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
            b = (b < 0) ? 0 : ((b > 255) ? 255 : b);

            rgb[index_rgb + 3] = r;
            rgb[index_rgb + 4] = g;
            rgb[index_rgb + 5] = b;
        }
    }
}

int main() {
    int videoFd;
    struct v4l2_capability cap;
    struct v4l2_format format;
    struct v4l2_requestbuffers reqbuf;
    struct v4l2_buffer buf;

    // 打开视频设备
    videoFd = open(VIDEO_DEVICE, O_RDWR);
    if (videoFd < 0) {
        perror("Failed to open video device");
        return 1;
    }

    // 检查设备能力
    if (ioctl(videoFd, VIDIOC_QUERYCAP, &cap) < 0) {
        perror("Failed to query device capabilities");
        close(videoFd);
        return 1;
    }

    if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
        fprintf(stderr, "Video capture not supported\n");
        close(videoFd);
        return 1;
    }

    // 设置捕获格式
    memset(&format, 0, sizeof(format));
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = CAPTURE_WIDTH;
    format.fmt.pix.height = CAPTURE_HEIGHT;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; // YUV 4:2:2格式
    if (ioctl(videoFd, VIDIOC_S_FMT, &format) < 0) {
        perror("Failed to set video format");
        close(videoFd);
        return 1;
    }

    // 请求捕获缓冲区
    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.count = 1;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if (ioctl(videoFd, VIDIOC_REQBUFS, &reqbuf) < 0) {
        perror("Failed to request buffer for capture");
        close(videoFd);
        return 1;
    }

    // 映射缓冲区到用户空间
    memset(&buf, 0, sizeof(buf));
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_MMAP;
    buf.index = 0;
    if (ioctl(videoFd, VIDIOC_QUERYBUF, &buf) < 0) {
        perror("Failed to query buffer");
        close(videoFd);
        return 1;
    }

    void *buffer = mmap(NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, videoFd, buf.m.offset);
    if (buffer == MAP_FAILED) {
        perror("Failed to map buffer");
        close(videoFd);
        return 1;
    }

    // 入队缓冲区准备捕获
    if (ioctl(videoFd, VIDIOC_QBUF, &buf) < 0) {
        perror("Failed to queue buffer");
        munmap(buffer, buf.length);
        close(videoFd);
        return 1;
    }

    // 开始捕获帧
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (ioctl(videoFd, VIDIOC_STREAMON, &type) < 0) {
        perror("Failed to start capturing");
        munmap(buffer, buf.length);
        close(videoFd);
        return 1;
    }

    // 等待帧捕获完成并出队缓冲区
    if (ioctl(videoFd, VIDIOC_DQBUF, &buf) < 0) {
        perror("Failed to dequeue buffer");
        munmap(buffer, buf.length);
        close(videoFd);
        return 1;
    }

    // 将捕获的YUV帧转换为RGB
    unsigned char *rgbBuffer = (unsigned char *)malloc(CAPTURE_WIDTH * CAPTURE_HEIGHT * 3);
    yuv2rgb((unsigned char *)buffer, rgbBuffer, CAPTURE_WIDTH, CAPTURE_HEIGHT);

    // 将RGB帧保存为JPEG图像
    FILE *file = fopen("captured_frame.jpg", "wb");
    if (file == NULL) {
        perror("Failed to open output file");
        munmap(buffer, buf.length);
        free(rgbBuffer);
        close(videoFd);
        return 1;
    }

    // 使用libjpeg写入JPEG图像
    struct jpeg_compress_struct cinfo;
    struct jpeg_error_mgr jerr;

    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_compress(&cinfo);
    jpeg_stdio_dest(&cinfo, file);

    cinfo.image_width = CAPTURE_WIDTH;
    cinfo.image_height = CAPTURE_HEIGHT;
    cinfo.input_components = 3;
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo, 80, TRUE);
    jpeg_start_compress(&cinfo, TRUE);

    JSAMPROW row_pointer[1];
    while (cinfo.next_scanline < cinfo.image_height) {
        row_pointer[0] = &rgbBuffer[cinfo.next_scanline * cinfo.image_width * cinfo.input_components];
        jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    jpeg_finish_compress(&cinfo);
    fclose(file);
    jpeg_destroy_compress(&cinfo);

    // 停止捕获
    if (ioctl(videoFd, VIDIOC_STREAMOFF, &type) < 0) {
        perror("Failed to stop capturing");
        munmap(buffer, buf.length);
        free(rgbBuffer);
        close(videoFd);
        return 1;
    }

    // 清理资源
    munmap(buffer, buf.length);
    free(rgbBuffer);
    close(videoFd);

    printf("Frame captured and saved successfully.\n");

    return 0;
}

另一个尝试过的YUV转RGB函数(效果类似,可能稍差):

void yuv2rgb(unsigned char *uyvy, unsigned char *rgb, int width, int height) {
    int i, j;
    int y0, u, y1, v;
    int r, g, b;
    int index_uyvy, index_rgb;

    for (i = 0; i < height; i++) {
        for (j = 0; j < width; j += 2) {
            index_uyvy = (i * width + j) * 2;
            index_rgb = (i * width + j) * 3;

            y0 = uyvy[index_uyvy];
            u = uyvy[index_uyvy + 1];
            y1 = uyvy[index_uyvy + 2];
            v = uyvy[index_uyvy + 3];

            // 将U、V范围调整为-128到127
            u -= 128;
            v -= 128;

            // UYVY转RGB转换(第一个像素)
            r = y0 + 1.402 * v;
            g = y0 - 0.344136 * u - 0.714136 * v;
            b = y0 + 1.772 * u;

            // 限制值在0-255范围内
            r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
            g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
            b = (b < 0) ? 0 : ((b > 255) ? 255 : b);

            rgb[index_rgb] = r;
            rgb[index_rgb + 1] = g;
            rgb[index_rgb + 2] = b;

            // UYVY转RGB转换(第二个像素)
            r = y1 + 1.402 * v;
            g = y1 - 0.344136 * u - 0.714136 * v;
            b = y1 + 1.772 * u;

            // 限制值在0-255范围内
            r = (r < 0) ? 0 : ((r > 255) ? 255 : r);
            g = (g < 0) ? 0 : ((g > 255) ? 255 : g);
            b = (b < 0) ? 0 : ((b > 255) ? 255 : b);

            rgb[index_rgb + 3] = r;
            rgb[index_rgb + 4] = g;
            rgb[index_rgb + 5] = b;
        }
    }
}

内容的提问来源于stack exchange,提问作者Hristos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 04:35:55