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_DetectConfig.h"
|
||||||
#include "VZNL_EyeConfig.h"
|
#include "VZNL_EyeConfig.h"
|
||||||
#include "VZNL_SwingMotor.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
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,98 +110,92 @@ struct VizumCamera::Impl {
|
|||||||
#ifdef ENABLE_VIZUM_CAMERA
|
#ifdef ENABLE_VIZUM_CAMERA
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class DetectRoiRestoreGuard
|
std::mutex& OutputRoiModeMutex()
|
||||||
|
{
|
||||||
|
static std::mutex mutex;
|
||||||
|
return mutex;
|
||||||
|
}
|
||||||
|
|
||||||
|
class OutputRoiModeRestoreGuard
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DetectRoiRestoreGuard(VZNLHANDLE device,
|
OutputRoiModeRestoreGuard()
|
||||||
const SVzNLROIRect& leftROI,
|
|
||||||
const SVzNLROIRect& rightROI)
|
|
||||||
: m_device(device), m_leftROI(leftROI), m_rightROI(rightROI)
|
|
||||||
{
|
{
|
||||||
|
VzNL_SetOutputROIImage(VzFalse);
|
||||||
|
LOG_INFO("[VizumCamera] SDK image output switched to full-frame mode; detection ROI unchanged");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RestoreNow()
|
void RestoreNow()
|
||||||
{
|
{
|
||||||
if (m_restored)
|
if (m_restored)
|
||||||
return true;
|
return;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
VzNL_SetOutputROIImage(VzTrue);
|
||||||
m_restored = true;
|
m_restored = true;
|
||||||
LOG_INFO("[VizumCamera] Detection ROI restored: left=[{}, {}, {}, {}], "
|
LOG_INFO("[VizumCamera] SDK image output restored to configured-ROI mode");
|
||||||
"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()
|
~OutputRoiModeRestoreGuard()
|
||||||
{
|
{
|
||||||
if (!m_restored && !RestoreNow())
|
RestoreNow();
|
||||||
LOG_ERROR("[VizumCamera] Emergency detection ROI restoration failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DetectRoiRestoreGuard(const DetectRoiRestoreGuard&) = delete;
|
OutputRoiModeRestoreGuard(const OutputRoiModeRestoreGuard&) = delete;
|
||||||
DetectRoiRestoreGuard& operator=(const DetectRoiRestoreGuard&) = delete;
|
OutputRoiModeRestoreGuard& operator=(const OutputRoiModeRestoreGuard&) = delete;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VZNLHANDLE m_device = nullptr;
|
|
||||||
SVzNLROIRect m_leftROI{};
|
|
||||||
SVzNLROIRect m_rightROI{};
|
|
||||||
bool m_restored = false;
|
bool m_restored = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool NormalizeToDeviceFrame(SLImage& image, uint32_t frameWidth, uint32_t frameHeight)
|
class SdkImageReleaseGuard
|
||||||
{
|
{
|
||||||
if (image.empty() || frameWidth == 0 || frameHeight == 0
|
public:
|
||||||
|| image.originX < 0 || image.originY < 0 || image.channels <= 0) {
|
SdkImageReleaseGuard(SVzNLImageData*& image, const char* label)
|
||||||
return false;
|
: m_image(image), m_label(label)
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t right = static_cast<uint64_t>(image.originX) + image.width;
|
bool ReleaseNow()
|
||||||
const uint64_t bottom = static_cast<uint64_t>(image.originY) + image.height;
|
{
|
||||||
if (right > frameWidth || bottom > frameHeight)
|
if (m_attempted)
|
||||||
return false;
|
return m_succeeded;
|
||||||
|
|
||||||
if (image.originX == 0 && image.originY == 0
|
m_attempted = true;
|
||||||
&& image.width == frameWidth && image.height == frameHeight) {
|
if (!m_image) {
|
||||||
return true;
|
m_succeeded = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t bytesPerChannel = image.bitDepth > 8 ? 2u : 1u;
|
~SdkImageReleaseGuard()
|
||||||
const size_t pixelBytes = static_cast<size_t>(image.channels) * bytesPerChannel;
|
{
|
||||||
const size_t sourceRowBytes = static_cast<size_t>(image.width) * pixelBytes;
|
ReleaseNow();
|
||||||
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);
|
SdkImageReleaseGuard(const SdkImageReleaseGuard&) = delete;
|
||||||
return true;
|
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)
|
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: "
|
LOG_INFO("[VizumCamera] Device status callback registered; scan completion events: "
|
||||||
"Device_Auto_Stop or Device_Swing_Finish");
|
"Device_Auto_Stop or Device_Swing_Finish");
|
||||||
|
|
||||||
// OpenDevice 之后立即设置图像输出格式(在此之前设置对本次连接无效)
|
// 输出格式和 ROI 图开关均为 SDK 全局状态,和所有 GetEyeImage 调用共用同一把锁。
|
||||||
VzNL_SetOutputImageFormat(keVzNLImageType_BGR888);
|
{
|
||||||
LOG_INFO("[VizumCamera] Output image format set to BGR888");
|
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()) {
|
if (!UpdateDeviceInfo()) {
|
||||||
@ -850,6 +853,9 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
|||||||
{
|
{
|
||||||
LOG_INFO("[VizumCamera] CaptureAndCacheData called, captureType={}", static_cast<int>(captureType));
|
LOG_INFO("[VizumCamera] CaptureAndCacheData called, captureType={}", static_cast<int>(captureType));
|
||||||
#ifdef ENABLE_VIZUM_CAMERA
|
#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);
|
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
|
||||||
if (!m_deviceHandle) {
|
if (!m_deviceHandle) {
|
||||||
LOG_ERROR("[VizumCamera] CaptureAndCacheData failed: Device handle is null");
|
LOG_ERROR("[VizumCamera] CaptureAndCacheData failed: Device handle is null");
|
||||||
@ -871,7 +877,9 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
|||||||
if (captureType & CaptureType::Image) {
|
if (captureType & CaptureType::Image) {
|
||||||
SVzNLImageData* pLeftImage = nullptr;
|
SVzNLImageData* pLeftImage = nullptr;
|
||||||
SVzNLImageData* pRightImage = nullptr;
|
SVzNLImageData* pRightImage = nullptr;
|
||||||
|
|
||||||
|
// 每次普通取图都显式自愈到配置 ROI 输出,避免继承异常退出留下的全图模式。
|
||||||
|
VzNL_SetOutputROIImage(VzTrue);
|
||||||
int result = VzNL_GetEyeImage(m_deviceHandle, &pLeftImage, &pRightImage, 3000);
|
int result = VzNL_GetEyeImage(m_deviceHandle, &pLeftImage, &pRightImage, 3000);
|
||||||
if (result == 0) {
|
if (result == 0) {
|
||||||
if (pLeftImage) {
|
if (pLeftImage) {
|
||||||
@ -888,6 +896,10 @@ bool VizumCamera::CaptureAndCacheData(CaptureType captureType)
|
|||||||
LOG_ERROR("[VizumCamera] Failed to get image: {}", errorMsg);
|
LOG_ERROR("[VizumCamera] Failed to get image: {}", errorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 图像读取结束即可释放 SDK 全局输出锁;点云扫描只保留设备锁。
|
||||||
|
if (outputModeLock.owns_lock())
|
||||||
|
outputModeLock.unlock();
|
||||||
|
|
||||||
// 采集点云(通过激光扫描)
|
// 采集点云(通过激光扫描)
|
||||||
// 按照 VZNLCameraAdapter::scan 的逻辑,使用自动检测模式采集
|
// 按照 VZNLCameraAdapter::scan 的逻辑,使用自动检测模式采集
|
||||||
@ -965,6 +977,7 @@ bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId
|
|||||||
image.clear();
|
image.clear();
|
||||||
frameId.clear();
|
frameId.clear();
|
||||||
#ifdef ENABLE_VIZUM_CAMERA
|
#ifdef ENABLE_VIZUM_CAMERA
|
||||||
|
const std::lock_guard<std::mutex> outputModeLock(OutputRoiModeMutex());
|
||||||
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
|
const std::lock_guard<std::mutex> deviceLock(m_deviceAccessMutex);
|
||||||
if (!m_isConnected || !m_deviceHandle) {
|
if (!m_isConnected || !m_deviceHandle) {
|
||||||
LOG_ERROR("[VizumCamera] Full-frame capture failed: device not connected");
|
LOG_ERROR("[VizumCamera] Full-frame capture failed: device not connected");
|
||||||
@ -993,24 +1006,12 @@ bool VizumCamera::CaptureFullFrameLeftImage(SLImage& image, std::string& frameId
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DetectRoiRestoreGuard restoreGuard(m_deviceHandle, oldLeftROI, oldRightROI);
|
OutputRoiModeRestoreGuard outputModeGuard;
|
||||||
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* leftImage = nullptr;
|
||||||
SVzNLImageData* rightImage = nullptr;
|
SVzNLImageData* rightImage = nullptr;
|
||||||
|
SdkImageReleaseGuard leftImageGuard(leftImage, "left");
|
||||||
|
SdkImageReleaseGuard rightImageGuard(rightImage, "right");
|
||||||
ret = VzNL_GetEyeImage(m_deviceHandle, &leftImage, &rightImage, 3000);
|
ret = VzNL_GetEyeImage(m_deviceHandle, &leftImage, &rightImage, 3000);
|
||||||
bool converted = false;
|
bool converted = false;
|
||||||
if (ret == 0 && leftImage) {
|
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: {})",
|
LOG_ERROR("[VizumCamera] Full-frame image acquisition failed: {} (error code: {})",
|
||||||
errorMsg, ret);
|
errorMsg, ret);
|
||||||
}
|
}
|
||||||
if (leftImage)
|
const bool leftReleased = leftImageGuard.ReleaseNow();
|
||||||
VzNL_ReleaseImage(&leftImage);
|
const bool rightReleased = rightImageGuard.ReleaseNow();
|
||||||
if (rightImage)
|
const bool imagesReleased = leftReleased && rightReleased;
|
||||||
VzNL_ReleaseImage(&rightImage);
|
|
||||||
|
|
||||||
const bool normalized = converted
|
const bool exactFullFrame = converted
|
||||||
&& NormalizeToDeviceFrame(image, resolution.nFrameWidth, resolution.nFrameHeight);
|
&& image.originX == 0 && image.originY == 0
|
||||||
if (converted && !normalized) {
|
&& image.width == resolution.nFrameWidth
|
||||||
LOG_ERROR("[VizumCamera] SDK full-frame image cannot be placed in device frame: "
|
&& image.height == resolution.nFrameHeight;
|
||||||
"image={}x{} origin=({}, {}), device={}x{}",
|
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,
|
image.width, image.height, image.originX, image.originY,
|
||||||
resolution.nFrameWidth, resolution.nFrameHeight);
|
resolution.nFrameWidth, resolution.nFrameHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool restored = restoreGuard.RestoreNow();
|
outputModeGuard.RestoreNow();
|
||||||
if (!normalized || !restored) {
|
|
||||||
|
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();
|
image.clear();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t id = ++m_frameCounter;
|
const uint64_t id = ++m_frameCounter;
|
||||||
frameId = "VZ_FULL_" + std::to_string(id);
|
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);
|
"output={}x{} origin=(0, 0)", frameId, image.width, image.height);
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
|
|||||||
@ -128,11 +128,11 @@ public:
|
|||||||
bool GetRightImage(SLImage& image);
|
bool GetRightImage(SLImage& image);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 采集全幅左目图像并在返回前恢复原检测 ROI
|
* @brief 不修改检测 ROI,仅切换 SDK 输出模式采集全幅左目图像
|
||||||
* @param[out] image 原点归一到(0,0)的设备全分辨率图像
|
* @param[out] image SDK 原始返回的设备全分辨率图像(原点必须为(0,0))
|
||||||
* @param[out] frameId 本次采集帧编号
|
* @param[out] frameId 本次采集帧编号
|
||||||
* @return 采集、全幅归一化及左右检测 ROI 恢复是否全部成功
|
* @return 采集、SDK 图像释放、精确全幅校验、输出模式恢复及检测 ROI 未变化校验是否全部成功
|
||||||
* @note 保持当前 CalibROI 状态不变;任何失败路径都会尝试恢复原左右 ROI
|
* @note 保持当前 CalibROI 和检测 ROI 不变;任何失败路径都会恢复 ROI 图输出模式
|
||||||
*/
|
*/
|
||||||
bool CaptureFullFrameLeftImage(SLImage& image, std::string& frameId);
|
bool CaptureFullFrameLeftImage(SLImage& image, std::string& frameId);
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user