fix: capture full frame with ROI restoration

This commit is contained in:
Ben 2026-08-26 10:19:47 +08:00
parent ee4a6625eb
commit 46f025327b
3 changed files with 206 additions and 3 deletions

View File

@ -106,6 +106,100 @@ struct VizumCamera::Impl {
#ifdef ENABLE_VIZUM_CAMERA
namespace {
class DetectRoiRestoreGuard
{
public:
DetectRoiRestoreGuard(VZNLHANDLE device,
const SVzNLROIRect& leftROI,
const SVzNLROIRect& rightROI)
: m_device(device), m_leftROI(leftROI), m_rightROI(rightROI)
{
}
bool RestoreNow()
{
if (m_restored)
return true;
const int ret = VzNL_ConfigDetectROI(m_device, &m_leftROI, &m_rightROI);
if (ret != 0) {
char errorMsg[256] = {0};
VzNL_GetErrorInfo(ret, errorMsg);
LOG_ERROR("[VizumCamera] Failed to restore detection ROI: {} (error code: {})",
errorMsg, ret);
return false;
}
m_restored = true;
LOG_INFO("[VizumCamera] Detection ROI restored: left=[{}, {}, {}, {}], "
"right=[{}, {}, {}, {}]",
m_leftROI.left, m_leftROI.right, m_leftROI.top, m_leftROI.bottom,
m_rightROI.left, m_rightROI.right, m_rightROI.top, m_rightROI.bottom);
return true;
}
~DetectRoiRestoreGuard()
{
if (!m_restored && !RestoreNow())
LOG_ERROR("[VizumCamera] Emergency detection ROI restoration failed");
}
DetectRoiRestoreGuard(const DetectRoiRestoreGuard&) = delete;
DetectRoiRestoreGuard& operator=(const DetectRoiRestoreGuard&) = delete;
private:
VZNLHANDLE m_device = nullptr;
SVzNLROIRect m_leftROI{};
SVzNLROIRect m_rightROI{};
bool m_restored = false;
};
bool NormalizeToDeviceFrame(SLImage& image, uint32_t frameWidth, uint32_t frameHeight)
{
if (image.empty() || frameWidth == 0 || frameHeight == 0
|| image.originX < 0 || image.originY < 0 || image.channels <= 0) {
return false;
}
const uint64_t right = static_cast<uint64_t>(image.originX) + image.width;
const uint64_t bottom = static_cast<uint64_t>(image.originY) + image.height;
if (right > frameWidth || bottom > frameHeight)
return false;
if (image.originX == 0 && image.originY == 0
&& image.width == frameWidth && image.height == frameHeight) {
return true;
}
const size_t bytesPerChannel = image.bitDepth > 8 ? 2u : 1u;
const size_t pixelBytes = static_cast<size_t>(image.channels) * bytesPerChannel;
const size_t sourceRowBytes = static_cast<size_t>(image.width) * pixelBytes;
if (image.data.size() < sourceRowBytes * image.height)
return false;
SLImage fullImage;
fullImage.width = frameWidth;
fullImage.height = frameHeight;
fullImage.originX = 0;
fullImage.originY = 0;
fullImage.channels = image.channels;
fullImage.bitDepth = image.bitDepth;
fullImage.data.assign(static_cast<size_t>(frameWidth) * frameHeight * pixelBytes, 0);
const size_t targetRowBytes = static_cast<size_t>(frameWidth) * pixelBytes;
const size_t targetXBytes = static_cast<size_t>(image.originX) * pixelBytes;
for (uint32_t row = 0; row < image.height; ++row) {
const size_t sourceOffset = static_cast<size_t>(row) * sourceRowBytes;
const size_t targetOffset = (static_cast<size_t>(image.originY) + row) * targetRowBytes
+ targetXBytes;
std::memcpy(fullImage.data.data() + targetOffset,
image.data.data() + sourceOffset, sourceRowBytes);
}
image = std::move(fullImage);
return true;
}
const char* DeviceStatusName(int status)
{
switch (static_cast<EVzDeviceWorkStatus>(status)) {
@ -826,6 +920,8 @@ bool VizumCamera::ConvertVzImageToSLImage(void* vzImagePtr, SLImage& slImage)
slImage.width = vzImage->nWidth;
slImage.height = vzImage->nHeight;
slImage.originX = vzImage->ptOriPos.x;
slImage.originY = vzImage->ptOriPos.y;
slImage.channels = vzImage->nChannels;
slImage.bitDepth = vzImage->byBitDepth;
@ -864,6 +960,100 @@ bool VizumCamera::GetRightImage(SLImage& image)
return !image.empty();
}
bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId)
{
image.clear();
frameId.clear();
#ifdef ENABLE_VIZUM_CAMERA
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
if (!m_isConnected || !m_deviceHandle) {
LOG_ERROR("[VizumCamera] Full-frame capture failed: device not connected");
return false;
}
SVzNLROIRect oldLeftROI{};
SVzNLROIRect oldRightROI{};
int ret = VzNL_GetConfigDetectROI(m_deviceHandle, &oldLeftROI, &oldRightROI);
if (ret != 0) {
char errorMsg[256] = {0};
VzNL_GetErrorInfo(ret, errorMsg);
LOG_ERROR("[VizumCamera] Full-frame capture cannot read detection ROI: {} "
"(error code: {})", errorMsg, ret);
return false;
}
SVzVideoResolution resolution{};
ret = VzNL_GetResolution(m_deviceHandle, &resolution);
if (ret != 0 || resolution.nFrameWidth == 0 || resolution.nFrameHeight == 0) {
char errorMsg[256] = {0};
VzNL_GetErrorInfo(ret, errorMsg);
LOG_ERROR("[VizumCamera] Full-frame capture cannot read device resolution: {} "
"(error code: {}, size={}x{})", errorMsg, ret,
resolution.nFrameWidth, resolution.nFrameHeight);
return false;
}
DetectRoiRestoreGuard restoreGuard(m_deviceHandle, oldLeftROI, oldRightROI);
const SVzNLROIRect fullLeftROI{0, static_cast<int>(resolution.nFrameWidth),
0, static_cast<int>(resolution.nFrameHeight)};
const SVzNLROIRect fullRightROI = fullLeftROI;
ret = VzNL_ConfigDetectROI(m_deviceHandle, &fullLeftROI, &fullRightROI);
if (ret != 0) {
char errorMsg[256] = {0};
VzNL_GetErrorInfo(ret, errorMsg);
LOG_ERROR("[VizumCamera] Failed to apply temporary full-frame ROI: {} "
"(error code: {}, size={}x{})", errorMsg, ret,
resolution.nFrameWidth, resolution.nFrameHeight);
return false;
}
LOG_INFO("[VizumCamera] Temporary full-frame ROI applied: {}x{}; CalibROI unchanged",
resolution.nFrameWidth, resolution.nFrameHeight);
SVzNLImageData* leftImage = nullptr;
SVzNLImageData* rightImage = nullptr;
ret = VzNL_GetEyeImage(m_deviceHandle, &leftImage, &rightImage, 3000);
bool converted = false;
if (ret == 0 && leftImage) {
converted = ConvertVzImageToSLImage(leftImage, image);
LOG_INFO("[VizumCamera] Full-frame SDK image received: size={}x{}, origin=({}, {})",
leftImage->nWidth, leftImage->nHeight,
leftImage->ptOriPos.x, leftImage->ptOriPos.y);
} else {
char errorMsg[256] = {0};
VzNL_GetErrorInfo(ret, errorMsg);
LOG_ERROR("[VizumCamera] Full-frame image acquisition failed: {} (error code: {})",
errorMsg, ret);
}
if (leftImage)
VzNL_ReleaseImage(&leftImage);
if (rightImage)
VzNL_ReleaseImage(&rightImage);
const bool normalized = converted
&& NormalizeToDeviceFrame(image, resolution.nFrameWidth, resolution.nFrameHeight);
if (converted && !normalized) {
LOG_ERROR("[VizumCamera] SDK full-frame image cannot be placed in device frame: "
"image={}x{} origin=({}, {}), device={}x{}",
image.width, image.height, image.originX, image.originY,
resolution.nFrameWidth, resolution.nFrameHeight);
}
const bool restored = restoreGuard.RestoreNow();
if (!normalized || !restored) {
image.clear();
return false;
}
const uint64_t id = ++m_frameCounter;
frameId = "VZ_FULL_" + std::to_string(id);
LOG_INFO("[VizumCamera] Full-frame capture completed and ROI restored: frameId={}, "
"output={}x{} origin=(0, 0)", frameId, image.width, image.height);
return true;
#else
return false;
#endif
}
bool VizumCamera::CapturePointCloudWithAutoDetect(PointCloud& pointCloud, int timeoutMs)
{
LOG_INFO("[VizumCamera] CapturePointCloudWithAutoDetect called, timeoutMs={}", timeoutMs);

View File

@ -125,9 +125,18 @@ public:
* @param[out] image 输出图像
* @return 是否成功
*/
bool GetRightImage(SLImage& image);
/**
bool GetRightImage(SLImage& image);
/**
* @brief 采集全幅左目图像并在返回前恢复原检测 ROI
* @param[out] image 原点归一到(0,0)的设备全分辨率图像
* @param[out] frameId 本次采集帧编号
* @return 采集、全幅归一化及左右检测 ROI 恢复是否全部成功
* @note 保持当前 CalibROI 状态不变;任何失败路径都会尝试恢复原左右 ROI
*/
bool CaptureFullFrameLeftImage(SLImage& image, std::string& frameId);
/**
* @brief 启用/禁用矫正 ROI 图像输出(VzNL_EnableCalibROI)
* @param enable true=启用;启用后 GetLeftImage/GetRightImage 返回 SDK 矫正图
* @return 是否成功

View File

@ -132,6 +132,8 @@ struct SLImage {
uint32_t height = 0;
int channels = 3;
int bitDepth = 8;
int originX = 0; // SDK ROI image top-left in the full sensor frame
int originY = 0;
size_t size() const { return data.size(); }
bool empty() const { return data.empty(); }
@ -142,6 +144,8 @@ struct SLImage {
data.shrink_to_fit();
width = 0;
height = 0;
originX = 0;
originY = 0;
}
};