diff --git a/.vscode/settings.json b/.vscode/settings.json index 83e0d3d..41257c5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -60,6 +60,7 @@ "stop_token": "cpp", "streambuf": "cpp", "thread": "cpp", - "typeinfo": "cpp" + "typeinfo": "cpp", + "chrono": "cpp" } } \ No newline at end of file diff --git a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Build/Make/Makefile b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Build/Make/Makefile index 98af8ae..46483cb 100644 --- a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Build/Make/Makefile +++ b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Build/Make/Makefile @@ -34,7 +34,8 @@ DEFINES = CFLAGS = $(COMMON_CFLAGS) \ $(VIMBACPP_CFLAGS) \ - $(VIMBAIMAGETRANSFORM_CFLAGS) + $(VIMBAIMAGETRANSFORM_CFLAGS) \ + OBJ_FILES = $(OBJ_DIR)/ApiController.o \ $(OBJ_DIR)/FrameObserver.o \ diff --git a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.cpp b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.cpp index 2f20689..945a1c7 100644 --- a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.cpp +++ b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include namespace AVT { namespace VmbAPI { @@ -36,8 +38,9 @@ bool FrameObserver::SetupVideoDevice() m_vfmt.fmt.pix.width = 640; m_vfmt.fmt.pix.height = 480; - m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; + m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24; // 改为RGB24格式 m_vfmt.fmt.pix.field = V4L2_FIELD_NONE; + m_vfmt.fmt.pix.sizeimage = 640 * 480 * 3; // RGB24是3字节/像素 if (ioctl(m_videoFd, VIDIOC_S_FMT, &m_vfmt) < 0) { perror("Set video format"); @@ -76,8 +79,8 @@ bool FrameObserver::UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, Vmb m_vfmt.fmt.pix.sizeimage = width * height; break; case VmbPixelFormatBayerRG8: - m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SRGGB8; - m_vfmt.fmt.pix.sizeimage = width * height; + m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; // We convert Bayer to YUYV + m_vfmt.fmt.pix.sizeimage = width * height * 2; break; default: m_vfmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; @@ -92,40 +95,125 @@ bool FrameObserver::UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, Vmb return true; } -void FrameObserver::FrameReceived(const FramePtr pFrame) -{ +// Convert Bayer RGGB to RGB (simplified demosaicing) +void FrameObserver::ConvertBayerRGGBToRGB(const VmbUchar_t* bayerData, VmbUchar_t* rgbData, VmbUint32_t width, VmbUint32_t height) { + // 只处理中心像素(避免边界越界) + for (VmbUint32_t y = 1; y < height - 1; y++) { + for (VmbUint32_t x = 1; x < width - 1; x++) { + VmbUint32_t idx = y * width + x; + VmbUchar_t r, g, b; + + if (y % 2 == 0) { // Even row (RGRG...) + if (x % 2 == 0) { // R + r = bayerData[idx]; + g = (bayerData[idx - 1] + bayerData[idx + 1] + bayerData[idx - width] + bayerData[idx + width]) / 4; + b = (bayerData[idx - width - 1] + bayerData[idx - width + 1] + bayerData[idx + width - 1] + bayerData[idx + width + 1]) / 4; + } else { // G + r = (bayerData[idx - 1] + bayerData[idx + 1]) / 2; + g = bayerData[idx]; + b = (bayerData[idx - width] + bayerData[idx + width]) / 2; + } + } else { // Odd row (GBGB...) + if (x % 2 == 0) { // G + r = (bayerData[idx - width] + bayerData[idx + width]) / 2; + g = bayerData[idx]; + b = (bayerData[idx - 1] + bayerData[idx + 1]) / 2; + } else { // B + r = (bayerData[idx - width - 1] + bayerData[idx - width + 1] + bayerData[idx + width - 1] + bayerData[idx + width + 1]) / 4; + g = (bayerData[idx - 1] + bayerData[idx + 1] + bayerData[idx - width] + bayerData[idx + width]) / 4; + b = bayerData[idx]; + } + } + + rgbData[idx * 3] = r; + rgbData[idx * 3 + 1] = g; + rgbData[idx * 3 + 2] = b; + } + } +} +// Convert RGB to YUYV (4:2:2) +void FrameObserver::ConvertRGBToYUYV(const VmbUchar_t* rgbData, VmbUchar_t* yuyvData, VmbUint32_t width, VmbUint32_t height) { + for (VmbUint32_t i = 0; i < width * height; i += 2) { + VmbUchar_t r1 = rgbData[i * 3], g1 = rgbData[i * 3 + 1], b1 = rgbData[i * 3 + 2]; + VmbUchar_t r2 = rgbData[(i + 1) * 3], g2 = rgbData[(i + 1) * 3 + 1], b2 = rgbData[(i + 1) * 3 + 2]; + + // YUV转换(简化版BT.601) + VmbUchar_t y1 = 0.299f * r1 + 0.587f * g1 + 0.114f * b1; + VmbUchar_t u1 = -0.147f * r1 - 0.289f * g1 + 0.436f * b1 + 128; + VmbUchar_t v1 = 0.615f * r1 - 0.515f * g1 - 0.100f * b1 + 128; + VmbUchar_t y2 = 0.299f * r2 + 0.587f * g2 + 0.114f * b2; + + yuyvData[i * 2] = y1; + yuyvData[i * 2 + 1] = u1; + yuyvData[i * 2 + 2] = y2; + yuyvData[i * 2 + 3] = v1; + } +} +void FrameObserver::FrameReceived(const FramePtr pFrame) { auto startTime = std::chrono::high_resolution_clock::now(); + // 1. 获取帧数据 VmbUchar_t* pBuffer = nullptr; VmbUint32_t nSize = 0; VmbUint32_t width = 0, height = 0; VmbPixelFormatType pixelFormat; - + if (pFrame->GetImage(pBuffer) != VmbErrorSuccess || pFrame->GetImageSize(nSize) != VmbErrorSuccess || pFrame->GetWidth(width) != VmbErrorSuccess || pFrame->GetHeight(height) != VmbErrorSuccess || pFrame->GetPixelFormat(pixelFormat) != VmbErrorSuccess) { + std::cerr << "Failed to get frame data!" << std::endl; return; } + // 2. 检查数据有效性 + if (!pBuffer || width == 0 || height == 0) { + std::cerr << "Invalid frame data: width=" << width << ", height=" << height << std::endl; + return; + } + + // 3. 更新V4L2设备格式(确保支持当前分辨率/PixelFormat) if (!UpdateVideoFormat(width, height, pixelFormat)) { + std::cerr << "Failed to update video format!" << std::endl; return; } - ssize_t written = write(m_videoFd, pBuffer, nSize); - if (written != (ssize_t)nSize) { - perror("Write to video device"); + // 4. 处理Bayer RGGB格式(转换为YUYV) + if (pixelFormat == VmbPixelFormatBayerRG8) { + // 4.1 分配缓冲区 + std::vector rgbData(width * height * 3); // RGB24: width * height * 3 + std::vector yuyvData(width * height * 2); // YUYV: width * height * 2 + + // 4.2 Bayer RGGB -> RGB + ConvertBayerRGGBToRGB(pBuffer, rgbData.data(), width, height); + + // 4.3 RGB -> YUYV + ConvertRGBToYUYV(rgbData.data(), yuyvData.data(), width, height); + + // 4.4 写入V4L2设备 + ssize_t written = write(m_videoFd, yuyvData.data(), yuyvData.size()); + if (written != (ssize_t)yuyvData.size()) { + perror("Write YUYV to video device failed"); + } + } + // 5. 处理非Bayer格式(直接写入) + else { + ssize_t written = write(m_videoFd, pBuffer, nSize); + if (written != (ssize_t)nSize) { + perror("Write raw data to video device failed"); + } } + // 6. 统计性能 auto endTime = std::chrono::high_resolution_clock::now(); double latency = std::chrono::duration(endTime - startTime).count(); m_totalLatency.store(m_totalLatency.load() + latency); m_frameCount++; - + + // 7. 重新排队帧 m_pCamera->QueueFrame(pFrame); } - double FrameObserver::GetAverageLatency() const { return m_frameCount > 0 ? m_totalLatency.load() / m_frameCount : 0.0; } diff --git a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.h b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.h index d1aa263..be1a504 100644 --- a/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.h +++ b/Vimba_6_0/VimbaCPP/Examples/AsynchronousGrab/VirtualCameraColor/Source/FrameObserver.h @@ -8,6 +8,7 @@ #include #include #include +#include // Add for buffer storage namespace AVT { namespace VmbAPI { @@ -29,6 +30,11 @@ private: void CloseVideoDevice(); bool UpdateVideoFormat(VmbUint32_t width, VmbUint32_t height, VmbPixelFormatType pixelFormat); + // Bayer to RGB conversion + void ConvertBayerRGGBToRGB(const VmbUchar_t* bayerData, VmbUchar_t* rgbData, VmbUint32_t width, VmbUint32_t height); + // RGB to YUYV conversion + void ConvertRGBToYUYV(const VmbUchar_t* rgbData, VmbUchar_t* yuyvData, VmbUint32_t width, VmbUint32_t height); + int m_videoFd; struct v4l2_format m_vfmt; std::mutex m_deviceMutex;