使用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
相关产品推荐
相关产品推荐

