fix: capture full image without changing detection ROI
This commit is contained in:
parent
46f025327b
commit
d054ea7017
@ -13,6 +13,10 @@
|
||||
#include "VZNL_DetectConfig.h"
|
||||
#include "VZNL_EyeConfig.h"
|
||||
#include "VZNL_SwingMotor.h"
|
||||
|
||||
// The bundled VZNL_Common.h omits this declaration, while the bundled
|
||||
// VzNLDetect import library exports the API (newer official headers declare it).
|
||||
VZNLAPI void VzNL_SetOutputROIImage(VzBool bOutputROI);
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -106,98 +110,92 @@ struct VizumCamera::Impl {
|
||||
#ifdef ENABLE_VIZUM_CAMERA
|
||||
namespace {
|
||||
|
||||
class DetectRoiRestoreGuard
|
||||
std::mutex& OutputRoiModeMutex()
|
||||
{
|
||||
static std::mutex mutex;
|
||||
return mutex;
|
||||
}
|
||||
|
||||
class OutputRoiModeRestoreGuard
|
||||
{
|
||||
public:
|
||||
DetectRoiRestoreGuard(VZNLHANDLE device,
|
||||
const SVzNLROIRect& leftROI,
|
||||
const SVzNLROIRect& rightROI)
|
||||
: m_device(device), m_leftROI(leftROI), m_rightROI(rightROI)
|
||||
OutputRoiModeRestoreGuard()
|
||||
{
|
||||
VzNL_SetOutputROIImage(VzFalse);
|
||||
LOG_INFO("[VizumCamera] SDK image output switched to full-frame mode; detection ROI unchanged");
|
||||
}
|
||||
|
||||
bool RestoreNow()
|
||||
void 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;
|
||||
}
|
||||
return;
|
||||
|
||||
VzNL_SetOutputROIImage(VzTrue);
|
||||
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;
|
||||
LOG_INFO("[VizumCamera] SDK image output restored to configured-ROI mode");
|
||||
}
|
||||
|
||||
~DetectRoiRestoreGuard()
|
||||
~OutputRoiModeRestoreGuard()
|
||||
{
|
||||
if (!m_restored && !RestoreNow())
|
||||
LOG_ERROR("[VizumCamera] Emergency detection ROI restoration failed");
|
||||
RestoreNow();
|
||||
}
|
||||
|
||||
DetectRoiRestoreGuard(const DetectRoiRestoreGuard&) = delete;
|
||||
DetectRoiRestoreGuard& operator=(const DetectRoiRestoreGuard&) = delete;
|
||||
OutputRoiModeRestoreGuard(const OutputRoiModeRestoreGuard&) = delete;
|
||||
OutputRoiModeRestoreGuard& operator=(const OutputRoiModeRestoreGuard&) = 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)
|
||||
class SdkImageReleaseGuard
|
||||
{
|
||||
public:
|
||||
SdkImageReleaseGuard(SVzNLImageData*& image, const char* label)
|
||||
: m_image(image), m_label(label)
|
||||
{
|
||||
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;
|
||||
bool ReleaseNow()
|
||||
{
|
||||
if (m_attempted)
|
||||
return m_succeeded;
|
||||
|
||||
if (image.originX == 0 && image.originY == 0
|
||||
&& image.width == frameWidth && image.height == frameHeight) {
|
||||
m_attempted = true;
|
||||
if (!m_image) {
|
||||
m_succeeded = true;
|
||||
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);
|
||||
const int ret = VzNL_ReleaseImage(&m_image);
|
||||
m_succeeded = ret == 0;
|
||||
if (!m_succeeded) {
|
||||
char errorMsg[256] = {0};
|
||||
VzNL_GetErrorInfo(ret, errorMsg);
|
||||
LOG_ERROR("[VizumCamera] Failed to release {} SDK image: {} (error code: {})",
|
||||
m_label, errorMsg, ret);
|
||||
}
|
||||
return m_succeeded;
|
||||
}
|
||||
|
||||
image = std::move(fullImage);
|
||||
return true;
|
||||
~SdkImageReleaseGuard()
|
||||
{
|
||||
ReleaseNow();
|
||||
}
|
||||
|
||||
SdkImageReleaseGuard(const SdkImageReleaseGuard&) = delete;
|
||||
SdkImageReleaseGuard& operator=(const SdkImageReleaseGuard&) = delete;
|
||||
|
||||
private:
|
||||
SVzNLImageData*& m_image;
|
||||
const char* m_label = nullptr;
|
||||
bool m_attempted = false;
|
||||
bool m_succeeded = false;
|
||||
};
|
||||
|
||||
bool SameSdkRoi(const SVzNLROIRect& lhs, const SVzNLROIRect& rhs)
|
||||
{
|
||||
return lhs.left == rhs.left && lhs.right == rhs.right
|
||||
&& lhs.top == rhs.top && lhs.bottom == rhs.bottom;
|
||||
}
|
||||
|
||||
const char* DeviceStatusName(int status)
|
||||
@ -686,9 +684,14 @@ bool VizumCamera::Connect(const std::string& ipAddress)
|
||||
LOG_INFO("[VizumCamera] Device status callback registered; scan completion events: "
|
||||
"Device_Auto_Stop or Device_Swing_Finish");
|
||||
|
||||
// OpenDevice 之后立即设置图像输出格式(在此之前设置对本次连接无效)
|
||||
// 输出格式和 ROI 图开关均为 SDK 全局状态,和所有 GetEyeImage 调用共用同一把锁。
|
||||
{
|
||||
const std::lock_guard<std::mutex> outputModeLock(OutputRoiModeMutex());
|
||||
VzNL_SetOutputImageFormat(keVzNLImageType_BGR888);
|
||||
LOG_INFO("[VizumCamera] Output image format set to BGR888");
|
||||
VzNL_SetOutputROIImage(VzTrue);
|
||||
LOG_INFO("[VizumCamera] SDK image output initialized to configured-ROI mode");
|
||||
}
|
||||
|
||||
// 更新设备信息
|
||||
if (!UpdateDeviceInfo()) {
|
||||
@ -850,6 +853,9 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
||||
{
|
||||
LOG_INFO("[VizumCamera] CaptureAndCacheData called, captureType={}", static_cast<int>(captureType));
|
||||
#ifdef ENABLE_VIZUM_CAMERA
|
||||
std::unique_lock<std::mutex> outputModeLock(OutputRoiModeMutex(), std::defer_lock);
|
||||
if (captureType & CaptureType::Image)
|
||||
outputModeLock.lock();
|
||||
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
|
||||
if (!m_deviceHandle) {
|
||||
LOG_ERROR("[VizumCamera] CaptureAndCacheData failed: Device handle is null");
|
||||
@ -872,6 +878,8 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
||||
SVzNLImageData* pLeftImage = nullptr;
|
||||
SVzNLImageData* pRightImage = nullptr;
|
||||
|
||||
// 每次普通取图都显式自愈到配置 ROI 输出,避免继承异常退出留下的全图模式。
|
||||
VzNL_SetOutputROIImage(VzTrue);
|
||||
int result = VzNL_GetEyeImage(m_deviceHandle, &pLeftImage, &pRightImage, 3000);
|
||||
if (result == 0) {
|
||||
if (pLeftImage) {
|
||||
@ -889,6 +897,10 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
||||
}
|
||||
}
|
||||
|
||||
// 图像读取结束即可释放 SDK 全局输出锁;点云扫描只保留设备锁。
|
||||
if (outputModeLock.owns_lock())
|
||||
outputModeLock.unlock();
|
||||
|
||||
// 采集点云(通过激光扫描)
|
||||
// 按照 VZNLCameraAdapter::scan 的逻辑,使用自动检测模式采集
|
||||
if (captureType & CaptureType::PointCloud) {
|
||||
@ -965,6 +977,7 @@ bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId
|
||||
image.clear();
|
||||
frameId.clear();
|
||||
#ifdef ENABLE_VIZUM_CAMERA
|
||||
const std::lock_guard<std::mutex> outputModeLock(OutputRoiModeMutex());
|
||||
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
|
||||
if (!m_isConnected || !m_deviceHandle) {
|
||||
LOG_ERROR("[VizumCamera] Full-frame capture failed: device not connected");
|
||||
@ -993,24 +1006,12 @@ bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId
|
||||
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);
|
||||
OutputRoiModeRestoreGuard outputModeGuard;
|
||||
|
||||
SVzNLImageData* leftImage = nullptr;
|
||||
SVzNLImageData* rightImage = nullptr;
|
||||
SdkImageReleaseGuard leftImageGuard(leftImage, "left");
|
||||
SdkImageReleaseGuard rightImageGuard(rightImage, "right");
|
||||
ret = VzNL_GetEyeImage(m_deviceHandle, &leftImage, &rightImage, 3000);
|
||||
bool converted = false;
|
||||
if (ret == 0 && leftImage) {
|
||||
@ -1024,29 +1025,62 @@ bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId
|
||||
LOG_ERROR("[VizumCamera] Full-frame image acquisition failed: {} (error code: {})",
|
||||
errorMsg, ret);
|
||||
}
|
||||
if (leftImage)
|
||||
VzNL_ReleaseImage(&leftImage);
|
||||
if (rightImage)
|
||||
VzNL_ReleaseImage(&rightImage);
|
||||
const bool leftReleased = leftImageGuard.ReleaseNow();
|
||||
const bool rightReleased = rightImageGuard.ReleaseNow();
|
||||
const bool imagesReleased = leftReleased && rightReleased;
|
||||
|
||||
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{}",
|
||||
const bool exactFullFrame = converted
|
||||
&& image.originX == 0 && image.originY == 0
|
||||
&& image.width == resolution.nFrameWidth
|
||||
&& image.height == resolution.nFrameHeight;
|
||||
if (converted && !exactFullFrame) {
|
||||
LOG_ERROR("[VizumCamera] SDK output-mode switch did not return an exact full frame; "
|
||||
"capture rejected without zero-padding: 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) {
|
||||
outputModeGuard.RestoreNow();
|
||||
|
||||
SVzNLROIRect currentLeftROI{};
|
||||
SVzNLROIRect currentRightROI{};
|
||||
ret = VzNL_GetConfigDetectROI(m_deviceHandle, ¤tLeftROI, ¤tRightROI);
|
||||
const bool roiUnchanged = ret == 0
|
||||
&& SameSdkRoi(currentLeftROI, oldLeftROI)
|
||||
&& SameSdkRoi(currentRightROI, oldRightROI);
|
||||
if (ret != 0) {
|
||||
char errorMsg[256] = {0};
|
||||
VzNL_GetErrorInfo(ret, errorMsg);
|
||||
LOG_ERROR("[VizumCamera] Failed to query detection ROI after full-frame capture: {} "
|
||||
"(error code: {})", errorMsg, ret);
|
||||
} else if (!roiUnchanged) {
|
||||
LOG_ERROR("[VizumCamera] Detection ROI changed during output-only full-frame capture; "
|
||||
"before left=[{}, {}, {}, {}], right=[{}, {}, {}, {}]; "
|
||||
"after left=[{}, {}, {}, {}], right=[{}, {}, {}, {}]",
|
||||
oldLeftROI.left, oldLeftROI.right, oldLeftROI.top, oldLeftROI.bottom,
|
||||
oldRightROI.left, oldRightROI.right, oldRightROI.top, oldRightROI.bottom,
|
||||
currentLeftROI.left, currentLeftROI.right,
|
||||
currentLeftROI.top, currentLeftROI.bottom,
|
||||
currentRightROI.left, currentRightROI.right,
|
||||
currentRightROI.top, currentRightROI.bottom);
|
||||
} else {
|
||||
LOG_INFO("[VizumCamera] Detection ROI verified unchanged after full-frame capture: "
|
||||
"left=[{}, {}, {}, {}], right=[{}, {}, {}, {}]",
|
||||
currentLeftROI.left, currentLeftROI.right,
|
||||
currentLeftROI.top, currentLeftROI.bottom,
|
||||
currentRightROI.left, currentRightROI.right,
|
||||
currentRightROI.top, currentRightROI.bottom);
|
||||
}
|
||||
|
||||
if (!exactFullFrame || !imagesReleased || !roiUnchanged) {
|
||||
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={}, "
|
||||
LOG_INFO("[VizumCamera] Full-frame capture completed without writing detection ROI: frameId={}, "
|
||||
"output={}x{} origin=(0, 0)", frameId, image.width, image.height);
|
||||
return true;
|
||||
#else
|
||||
|
||||
@ -128,11 +128,11 @@ public:
|
||||
bool GetRightImage(SLImage& image);
|
||||
|
||||
/**
|
||||
* @brief 采集全幅左目图像并在返回前恢复原检测 ROI
|
||||
* @param[out] image 原点归一到(0,0)的设备全分辨率图像
|
||||
* @brief 不修改检测 ROI,仅切换 SDK 输出模式采集全幅左目图像
|
||||
* @param[out] image SDK 原始返回的设备全分辨率图像(原点必须为(0,0))
|
||||
* @param[out] frameId 本次采集帧编号
|
||||
* @return 采集、全幅归一化及左右检测 ROI 恢复是否全部成功
|
||||
* @note 保持当前 CalibROI 状态不变;任何失败路径都会尝试恢复原左右 ROI
|
||||
* @return 采集、SDK 图像释放、精确全幅校验、输出模式恢复及检测 ROI 未变化校验是否全部成功
|
||||
* @note 保持当前 CalibROI 和检测 ROI 不变;任何失败路径都会恢复 ROI 图输出模式
|
||||
*/
|
||||
bool CaptureFullFrameLeftImage(SLImage& image, std::string& frameId);
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user