可以增量BA了,有内存异常问题待查

main
wangchongwu 5 months ago
parent a855e91aed
commit 0acfe7114f

@ -188,17 +188,17 @@ int main(int, char**)
<< std::endl;
// 基于外参的快拼
// stitcher->GeoStitch(frame,info);
stitcher->GeoStitch(frame,info);
// 基于地理+BA拼接
//auto nNum = stitcher->BAStitch(frame,info);
// test接口
auto a = stitcher->ReceiveFrame(frame,info);
}
imshow("pan", mat_pan);
waitKey(1);
@ -207,9 +207,8 @@ int main(int, char**)
// 处理帧
stitcher->ProcessFrame();
imshow("pan", mat_pan);
imshow("pan_opt", mat_pan);
waitKey(0);
//waitKey(0);
}

@ -41,9 +41,9 @@ public:
// 基于特征点拼接,全局光束平差方法,分为更新和优化调整两个接口
virtual SINT32 BAStitch(GD_VIDEO_FRAME_S img, FrameInfo para) = 0;
virtual SINT32 BAOpt() = 0;
//// 基于特征点拼接,全局光束平差方法,分为更新和优化调整两个接口
//virtual SINT32 BAStitch(GD_VIDEO_FRAME_S img, FrameInfo para) = 0;
//virtual SINT32 BAOpt() = 0;

@ -1,15 +0,0 @@
#include "Arith_BAModule.h"
BA_Module::BA_Module(FileCache<FrameCache>* cache)
{
_cache = cache;
}
BA_Module::~BA_Module()
{
}
bool BA_Module::updateCacheH(KeyType Key, cv::Mat H)
{
return false;
}

@ -1,32 +0,0 @@
// 全局BA
/*********版权所有C2025武汉高德红外股份有限公司***************************************
* Arith_BAProc.h
* BA
*
*
* V0.5
* 04046wcw
* 2025/01/15
*
*****************************************************************************************/
#pragma once
#include "FileCache.h"
class BA_Module
{
public:
BA_Module(FileCache<FrameCache>* cache);
~BA_Module();
public:
// 更新缓存中的帧H矩阵并存储
bool updateCacheH(KeyType Key, cv::Mat H);
private:
FileCache<FrameCache>* _cache;
};

@ -1,15 +1,17 @@
#include "Arith_BATask.h"
#include "ceres/ceres.h"
#include "Arith_GeoSolver.h"
#include "math.h"
using namespace ceres;
// 定义残差结构体
struct HomographyResidual
struct HomographyResidual
{
HomographyResidual(const cv::KeyPoint& keypoint_i, const cv::KeyPoint& keypoint_j,const cv::Mat H1, const cv::Mat H2)
: keypoint_i_(keypoint_i), keypoint_j_(keypoint_j),H1_(H1),H2_(H2) {}
HomographyResidual(const cv::KeyPoint& keypoint_i, const cv::KeyPoint& keypoint_j, const cv::Mat H1, const cv::Mat H2)
: keypoint_i_(keypoint_i), keypoint_j_(keypoint_j), H1_(H1), H2_(H2) {
}
template <typename T>
bool operator()(const T* const h_i, const T* const h_j, T* residual) const
bool operator()(const T* const h_i, const T* const h_j, T* residual) const
{
// 残差计算逻辑
T H_i[9] = { h_i[0], h_i[1], h_i[2],
@ -26,15 +28,15 @@ struct HomographyResidual
T P_i[3] = { T(0), T(0), T(0) };
T P_j[3] = { T(0), T(0), T(0) };
for (int row = 0; row < 3; row++)
for (int row = 0; row < 3; row++)
{
for (int col = 0; col < 3; col++)
for (int col = 0; col < 3; col++)
{
P_i[row] += H_i[row * 3 + col] * p_i[col];
P_j[row] += H_j[row * 3 + col] * p_j[col];
}
}
P_i[0] /= P_i[2];
P_i[1] /= P_i[2];
P_j[0] /= P_j[2];
@ -66,69 +68,31 @@ private:
const cv::KeyPoint keypoint_j_; // 第 j 帧图像中的特征点
const cv::Mat H1_;
const cv::Mat H2_;
const cv::Mat H2_;
};
BA_Task::BA_Task(GeoSolver * pGeoTrans)
BA_Task::BA_Task(FileCache<FrameCache>* cache)
{
_GeoStitcher = pGeoTrans;
_cache = cache;
_FeaMatcher = new FeatureMatcher(DetectorType::SIFT, MatcherType::FLANN);
_imgVec.reserve(100);
}
BA_Task::~BA_Task()
{
}
SINT32 BA_Task::addFrame(GD_VIDEO_FRAME_S img, FrameInfo para)
{
cv::Mat frame = Mat(img.u32Height, img.u32Width, CV_8UC1, (void*)img.u64VirAddr[0]);
// 缓存图像帧
if (img.enPixelFormat == GD_PIXEL_FORMAT_E::GD_PIXEL_FORMAT_GRAY_Y8)
{
_imgVec.emplace_back(frame.clone());
}
// 缓存外参
_paraVec.push_back(para);
// 缓存初始H
auto H = _GeoStitcher->findHomography(para);
_origMatrix.push_back(H.clone());
_currMatrix.push_back(H.clone());
// 提取特征点
std::vector<cv::KeyPoint> keypoints;
cv::Mat descriptors;
_FeaMatcher->extractFeatures(frame,keypoints,descriptors);
_FeaPtVec.push_back(keypoints);
_FeaDespVec.push_back(descriptors);
// 缓存包围多边形
_polygon.push_back(warpRectWithH(H,cv::Size(img.u32Width,img.u32Height)));
return _imgVec.size();
}
//void BA_Task::setPanPara(PanInfo info)
//{
// _panPara = info;
//}
//#define SHOW_MATCH
void BA_Task::optimizeBA(cv::Mat H_pan)
void BA_Task::OptFrame(vector<KeyType> frameInd,cv::Mat H_map)
{
// 读取帧信息
readFrameInfo(frameInd);
// 邻接关系计算
CalMatchMat();
//remap(_origMatrix, H_pan, "_origMatrix");
// 计算匹配性矩阵
CalMatchMat(0.3);
// 开始BA
google::InitGoogleLogging("ceres");
@ -154,36 +118,7 @@ void BA_Task::optimizeBA(cv::Mat H_pan)
{
continue;
}
#ifdef SHOW_MATCH
cv::Mat image(1000, 1000, CV_8UC3, cv::Scalar(0, 0, 0));
cv::Mat imagetmp(1000, 1000, CV_8UC3, cv::Scalar(0, 0, 0));
vector<vector<cv::Point2f>> tmpPoly;
tmpPoly.push_back(_polygon[i]);
tmpPoly.push_back(_polygon[j]);
cv::warpPerspective(_imgVec[i], imagetmp, _origMatrix[i], imagetmp.size());
// 生成遮罩(全白图像,表示有效区域)
cv::Mat mask1 = cv::Mat::ones(_imgVec[i].size(), CV_8UC1) * 255;
cv::Mat warped_mask1;
cv::warpPerspective(mask1, warped_mask1, _origMatrix[i], image.size());
imagetmp.copyTo(image, warped_mask1);
cv::warpPerspective(_imgVec[j], imagetmp, _origMatrix[j], imagetmp.size());
cv::Mat mask2 = cv::Mat::ones(_imgVec[i].size(), CV_8UC1) * 255;
cv::Mat warped_mask2;
cv::warpPerspective(mask2, warped_mask2, _origMatrix[j], image.size());
imagetmp.copyTo(image, warped_mask2);
drawPolygons(image, tmpPoly);
// 显示绘制结果
cv::imshow("Polygons", image);
cv::waitKey(1);
#endif
std::vector<cv::DMatch> matches;
//_FeaMatcher->matchFeatures(_FeaDespVec[i],_FeaDespVec[j],matches);
_FeaMatcher->matchFeatures_WithH(_FeaPtVec[i], _FeaDespVec[i], _FeaPtVec[j], _FeaDespVec[j], _origMatrix[i], _origMatrix[j], matches);
@ -199,27 +134,14 @@ void BA_Task::optimizeBA(cv::Mat H_pan)
continue;
}
#ifdef SHOW_MATCH
// 可视化匹配结果
cv::Mat img_matches;
cv::drawMatches(
_imgVec[i], _FeaPtVec[i], // 第一幅图像及其特征点
_imgVec[j], _FeaPtVec[j], // 第二幅图像及其特征点
matches, // 匹配结果
img_matches, // 输出图像
cv::Scalar::all(-1), // 匹配线颜色(默认随机颜色)
cv::Scalar::all(-1), // 未匹配点颜色(默认不绘制)
std::vector<char>(), // 掩码(可选,用于筛选匹配)
cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS // 不绘制未匹配的点
);
cv::resize(img_matches, img_matches, cv::Size(1280, 512));
// 显示匹配结果
cv::imshow("Feature Matches", img_matches);
cv::waitKey(0);
// 绘制匹配结果
drawMatch(i, j, matches, H_map);
#endif
// 添加匹配点对的残差块
for (int m = 0; m < matches.size(); m++)
{
@ -230,17 +152,17 @@ void BA_Task::optimizeBA(cv::Mat H_pan)
ceres::LossFunction* loss_function = new ceres::HuberLoss(35);
cv::Mat H1 = _origMatrix[i];
cv::Mat H1 = _origMatrix[i];
cv::Mat H2 = _origMatrix[j];
ceres::CostFunction* cost_function =
new ceres::AutoDiffCostFunction<HomographyResidual, 4, 8, 8>(
new HomographyResidual(keypoint_i, keypoint_j,H1,H2));
new HomographyResidual(keypoint_i, keypoint_j, H1, H2));
problem.AddResidualBlock(cost_function, loss_function, h_list[i], h_list[j]);
}
nParaCnt += matches.size();
}
}
@ -258,50 +180,96 @@ void BA_Task::optimizeBA(cv::Mat H_pan)
// 求解
ceres::Solve(options, &problem, &summary);
//// 将优化后的参数转换回 cv::Mat
for (int i = 0; i < _currMatrix.size(); i++)
{
std::cout << "------------" << std::endl;
std::cout << _origMatrix[i] << std::endl;
std::cout << _currMatrix[i] << std::endl;
std::cout << "------------" << std::endl;
}
//for (int i = 0; i < _currMatrix.size(); i++)
//{
// std::cout << "------------" << std::endl;
// std::cout << _origMatrix[i] << std::endl;
// std::cout << _currMatrix[i] << std::endl;
// std::cout << "------------" << std::endl;
//}
// 输出结果
std::cout << summary.BriefReport() << std::endl;
remap(_currMatrix, H_pan, "_currMatrix");
// 写入缓存
writeFrameInfo();
}
cv::waitKey(0);
bool BA_Task::updateCacheH(KeyType Key, cv::Mat H)
{
auto _t_frame_cache = std::make_shared<FrameCache>();
if (_cache->get(Key, _t_frame_cache))
{
// 更新H
memcpy(_t_frame_cache->H, H.data, sizeof(double) * 9);
// 存储
_cache->set(Key, _t_frame_cache);
return true;
}
return false;
}
void BA_Task::remap(vector<cv::Mat_<double>> H_Vec, cv::Mat_<double > H_pan, std::string winname)
int BA_Task::readFrameInfo(vector<KeyType> frameInd)
{
cv::Mat image(1500, 1500, CV_8UC3, cv::Scalar(0, 0, 0));
auto _t_frame_cache = std::make_shared<FrameCache>();
for (size_t i = 0; i < _imgVec.size(); i++)
for (size_t i = 0; i < frameInd.size(); i++)
{
cv::Mat imagetmp(1500, 1500, CV_8UC3, cv::Scalar(0, 0, 0));
cv::warpPerspective(_imgVec[i], imagetmp, H_pan * H_Vec[i], imagetmp.size());
cv::Mat mask = cv::Mat::ones(_imgVec[i].size(), CV_8UC1) * 255;
cv::Mat warped_mask;
cv::warpPerspective(mask, warped_mask, H_pan * H_Vec[i], image.size());
imagetmp.copyTo(image, warped_mask);
}
KeyType key = frameInd[i];
if (_cache->get(key, _t_frame_cache))
{
// 记录key
_frameInd.push_back(key);
imshow(winname, image);
cv::waitKey(1);
// 特征点
vector<cv::KeyPoint> keypoints(_t_frame_cache->_pt, _t_frame_cache->_pt + _t_frame_cache->ptNum);
_FeaPtVec.push_back(keypoints);
// 描述子
cv::Mat descriptors(_t_frame_cache->ptNum, FEA_DES_SIZE, CV_32FC1, _t_frame_cache->_desp);
_FeaDespVec.push_back(descriptors);
// 原始外参
_paraVec.push_back(_t_frame_cache->_para);
// 初始H
cv::Mat H = cv::Mat(3, 3, CV_64FC1, _t_frame_cache->H);
_origMatrix.push_back(H.clone());
_currMatrix.push_back(H.clone());
// 缓存包围多边形
_polygon.push_back(warpRectWithH(H, cv::Size(_t_frame_cache->_frame_info.u32Width, _t_frame_cache->_frame_info.u32Height)));
#ifdef SHOW_MATCH
// 读取图像
cv::Mat img = cv::Mat(_t_frame_cache->_frame_info.u32Height, _t_frame_cache->_frame_info.u32Width,CV_8UC1, _t_frame_cache->_data);
_imgVec.push_back(img.clone());
#endif
}
}
return 0;
}
int BA_Task::writeFrameInfo()
{
for (size_t i = 0; i < _currMatrix.size(); i++)
{
auto Key = _frameInd[i];
// 更新缓存
updateCacheH(Key, _currMatrix[i]);
}
//#define SHOW_MATCH
SINT32 BA_Task::CalMatchMat(float fiou_thre)
return 0;
}
SINT32 BA_Task::CalMatchMat()
{
_IOUMat = cv::Mat::zeros(_polygon.size(),_polygon.size(),CV_32FC1);
_IOUMat = cv::Mat::zeros(_polygon.size(), _polygon.size(), CV_32FC1);
_MatchMat = cv::Mat::zeros(_polygon.size(), _polygon.size(), CV_32SC1);
@ -315,29 +283,79 @@ SINT32 BA_Task::CalMatchMat(float fiou_thre)
{
if (i == j)
{
_IOUMat.at<float>(i,j) = 1;
_IOUMat.at<float>(i, j) = 1;
continue;
}
vector<cv::Point2f> poly_j = _polygon[j];
float fiou = computeQuadrilateralIOU(poly_i,poly_j);
_IOUMat.at<float>(i,j) = fiou;
_IOUMat.at<float>(j,i) = fiou;//是对称矩阵
float fiou = computeQuadrilateralIOU(poly_i, poly_j);
_IOUMat.at<float>(i, j) = fiou;
_IOUMat.at<float>(j, i) = fiou;//是对称矩阵
}
}
return 0;
}
void BA_Task::drawMatch(int i, int j, std::vector<cv::DMatch> matches, cv::Mat H_map)
{
cv::Mat image(1000, 1000, CV_8UC3, cv::Scalar(0, 0, 0));
cv::Mat imagetmp(1000, 1000, CV_8UC3, cv::Scalar(0, 0, 0));
vector<vector<cv::Point2f>> tmpPoly;
tmpPoly.push_back(_polygon[i]);
tmpPoly.push_back(_polygon[j]);
cv::warpPerspective(_imgVec[i], imagetmp, H_map * _origMatrix[i], imagetmp.size());
// 生成遮罩(全白图像,表示有效区域)
cv::Mat mask1 = cv::Mat::ones(_imgVec[i].size(), CV_8UC1) * 255;
cv::Mat warped_mask1;
cv::warpPerspective(mask1, warped_mask1, H_map * _origMatrix[i], image.size());
imagetmp.copyTo(image, warped_mask1);
cv::warpPerspective(_imgVec[j], imagetmp, H_map * _origMatrix[j], imagetmp.size());
cv::Mat mask2 = cv::Mat::ones(_imgVec[i].size(), CV_8UC1) * 255;
cv::Mat warped_mask2;
cv::warpPerspective(mask2, warped_mask2, H_map * _origMatrix[j], image.size());
imagetmp.copyTo(image, warped_mask2);
drawPolygons(image, tmpPoly);
// 显示绘制结果
cv::imshow("Polygons", image);
cv::waitKey(1);
// 可视化匹配结果
cv::Mat img_matches;
cv::drawMatches(
_imgVec[i], _FeaPtVec[i], // 第一幅图像及其特征点
_imgVec[j], _FeaPtVec[j], // 第二幅图像及其特征点
matches, // 匹配结果
img_matches, // 输出图像
cv::Scalar::all(-1), // 匹配线颜色(默认随机颜色)
cv::Scalar::all(-1), // 未匹配点颜色(默认不绘制)
std::vector<char>(), // 掩码(可选,用于筛选匹配)
cv::DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS // 不绘制未匹配的点
);
cv::resize(img_matches, img_matches, cv::Size(1280, 512));
// 显示匹配结果
cv::imshow("Feature Matches", img_matches);
cv::waitKey(0);
}
// 函数:绘制多边形
void drawPolygons(cv::Mat& image, const std::vector<std::vector<cv::Point2f>>& polygons)
void drawPolygons(cv::Mat& image, const std::vector<std::vector<cv::Point2f>>& polygons)
{
// 定义颜色列表
std::vector<cv::Scalar> colors = {

@ -9,39 +9,50 @@
* 2025/01/15
*
*****************************************************************************************/
#pragma once
#include "StitchStruct.h"
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include "Arith_GeoSolver.h"
#include "FileCache.h"
#include "Arith_FeaMatch.h"
using cv::Mat_;
class BA_Task
//#define SHOW_MATCH
class BA_Task
{
public:
BA_Task(GeoSolver* pGeoTrans);
BA_Task(FileCache<FrameCache>* cache);
~BA_Task();
// 加入优化帧
SINT32 addFrame(GD_VIDEO_FRAME_S img, FrameInfo para);
// BA优
void optimizeBA(cv::Mat H_pan);
// 优化指定帧并更新H到缓存中,H_map用于优化过程可视化
void OptFrame(vector<KeyType> frameInd, cv::Mat H_map);
// 使用地理系H矩阵和全景图H重投影所有帧
void remap(vector<cv::Mat_<double>>H_Vec, cv::Mat_<double > H_pan, std::string winname);
// 更新缓存中的帧H矩阵并存储
bool updateCacheH(KeyType Key, cv::Mat H);
private:
SINT32 CalMatchMat(float fiou_thre);//计算匹配性矩阵(以IOU约束)
private:
GeoSolver* _GeoStitcher;//外参计算
// 从缓存读取指定帧信息(不需要图像)
int readFrameInfo(vector<KeyType> frameInd);
// 输出优化结果到cache
int writeFrameInfo();
SINT32 CalMatchMat();//计算匹配性矩阵
// 调试图像
vector<cv::Mat> _imgVec;//图像缓存
vector<KeyType> _frameInd;//帧索引,BA模块按照线性存储完成后利用索引更新实际帧。注意传入的索引有可能是空的需要处理
FeatureMatcher* _FeaMatcher;//特征匹配
private:
FileCache<FrameCache>* _cache;
Mat_<int> _MatchMat;//配准点邻接表
Mat_<float> _IOUMat;//交汇邻接表
@ -49,17 +60,17 @@ private:
vector<cv::Mat_<double>> _currMatrix;//当前H矩阵
//超大空间考虑文件cache todo
private:
vector<cv::Mat> _imgVec;//图像缓存
vector<vector<cv::Point2f>> _polygon;//帧包围四边形
vector<vector<cv::KeyPoint>> _FeaPtVec;//特征点缓存
vector<cv::Mat> _FeaDespVec;//特征点描述子
vector<FrameInfo> _paraVec;
vector<vector<cv::Point2f>> _polygon;//帧包围四边形
private:
// 调试:绘制匹配关系
void drawMatch(int i, int j, std::vector<cv::DMatch> match,cv::Mat H_map);
};
void drawPolygons(cv::Mat& image, const std::vector<std::vector<cv::Point2f>>& polygons);

@ -33,6 +33,7 @@ void FeatureMatcher::matchFeatures(cv::Mat& descriptors1, cv::Mat& descriptors2,
void FeatureMatcher::matchFeatures_WithH(vector<KeyPoint> keypoints1, cv::Mat& descriptors1, vector<KeyPoint> keypoints2, cv::Mat& descriptors2, cv::Mat H1, cv::Mat H2, std::vector<cv::DMatch>& matches)
{
std::vector<cv::DMatch> _matches;
if (matcherType_ == FLANN)
{
flannMatcher_->match(descriptors1, descriptors2, _matches);

@ -25,11 +25,11 @@ VideoStitch::VideoStitch(SINT32 nWidth, SINT32 nHeight)
{
_GeoSolver = new GeoSolver();
_BATask = new BA_Task(_GeoSolver);
_FeaMatcher = new FeatureMatcher(DetectorType::SIFT, MatcherType::FLANN);
_cache = new FileCache<FrameCache>(50,"./cache");
_cache = new FileCache<FrameCache>(20,"./cache");
_BATask = new BA_Task(_cache);
_panPara = { 0 };
@ -39,7 +39,6 @@ VideoStitch::VideoStitch(SINT32 nWidth, SINT32 nHeight)
VideoStitch::~VideoStitch()
{
delete _GeoSolver;
delete _BATask;
delete _cache;
}
@ -83,8 +82,6 @@ PanInfo VideoStitch::Init(FrameInfo info)
{
_panPara = InitMap(info);
//_BATask->setPanPara(_panPara);
_pan = cv::Mat::zeros(_panPara.m_pan_height, _panPara.m_pan_width, CV_8UC1);
return _panPara;
@ -166,17 +163,9 @@ BYTE8 VideoStitch::GeoStitch(GD_VIDEO_FRAME_S img, FrameInfo para)
return 0;
}
SINT32 VideoStitch::BAStitch(GD_VIDEO_FRAME_S img, FrameInfo para)
{
SINT32 nFrameCnt = _BATask->addFrame(img,para);
return 0;
}
SINT32 VideoStitch::BAOpt()
{
_BATask->optimizeBA(getAffineFromGeo2Pan(_panPara));
return 0;
}
SINT32 VideoStitch::ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para)
{
@ -206,6 +195,9 @@ SINT32 VideoStitch::ReceiveFrame(GD_VIDEO_FRAME_S img, FrameInfo para)
_FeaMatcher->extractFeatures(cv::Mat(img.u32Height, img.u32Width,CV_8UC1, _t_frame_cache->_data), keypoints, descriptors);
size_t keyNum = MIN(keypoints.size(), FEA_NUM_MAX);
_t_frame_cache->ptNum = keyNum;
// 保存特征点
memcpy(_t_frame_cache->_pt, keypoints.data(), sizeof(cv::KeyPoint) * keyNum);
@ -230,20 +222,15 @@ SINT32 VideoStitch::ProcessFrame()
{
vector<KeyType> vec;
for (size_t i = 10; i < 300; i+=10)
{
vec.push_back(i);
}
vec.push_back(10);
vec.push_back(20);
//vec.push_back(50);
//vec.push_back(70);
vec.push_back(120);
vec.push_back(220);
//vec.push_back(320);
//vec.push_back(420);
//vec.push_back(520);
_BATask->OptFrame(vec, _H_pan);
mapFrame(vec);
return SINT32();
}
@ -289,11 +276,6 @@ void VideoStitch::mapFrame(vector<KeyType> frameInd)
}
void VideoStitch::OptFrame(vector<KeyType> frameInd)
{
}
GD_VIDEO_FRAME_S VideoStitch::ExportPanAddr()

@ -27,15 +27,11 @@ public:
// 投影显示
void mapFrame(vector<KeyType> frameInd);
// 优化帧
void OptFrame(vector<KeyType> frameInd);
public:
SINT32 BAStitch(GD_VIDEO_FRAME_S img, FrameInfo para);
SINT32 BAOpt();
GD_VIDEO_FRAME_S ExportPanAddr();
private:
@ -53,6 +49,7 @@ private:
BA_Task* _BATask;//BA
PanInfo _panPara;//全景图配置
cv::Mat _H_pan;//全景图投影矩阵:从地理系到全景地图

@ -12,6 +12,11 @@
//
using KeyType = int;
// 通用文件缓存类:HASH部分目前没有针对不同类型的数据进行处理多个键值一样的数据缓存会文件覆盖
// todo:落盘部分增加异步写入任务,提高性能
template <typename T>
class FileCache
{

@ -51,8 +51,9 @@ struct FrameCache
FrameInfo _para;
GD_VIDEO_FRAME_S _frame_info;
BYTE8 _data[IMG_CACHE_SIZE];
SINT32 ptNum;
cv::KeyPoint _pt[FEA_NUM_MAX];
FLOAT32 _desp[FEA_NUM_MAX * FEA_DES_SIZE];
DOUBLE64 H[9];// H矩阵
};
Loading…
Cancel
Save