头文件.h:
/**
* @brief GPU加速的Flann描述符匹配器
*
* 这个匹配器使用FLANN库来进行描述符的匹配,并使用GPU加速来提高性能。
*/
class GpuFlannBasedMatcher : public cv::DescriptorMatcher {
public:
// 构造函数
CV_WRAP GpuFlannBasedMatcher(
const cv::Ptr<cv::flann::IndexParams>& indexParams = cv::makePtr<cv::flann::KDTreeIndexParams>(),
const cv::Ptr<cv::flann::SearchParams>& searchParams = cv::makePtr<cv::flann::SearchParams>()
// int addedDescCount = 0
);
// 添加描述符到GPU
virtual void add(cv::InputArrayOfArrays descriptors) CV_OVERRIDE;
// 清除已添加的描述符
virtual void clear() CV_OVERRIDE;
// Reads matcher object from a file node
virtual void read( const FileNode& ) CV_OVERRIDE;
// Writes matcher object to a file storage
virtual void write( FileStorage& ) const CV_OVERRIDE;
// 训练FLANN索引
virtual void train() CV_OVERRIDE;
virtual bool isMaskSupported() const CV_OVERRIDE;
CV_WRAP static Ptr<GpuFlannBasedMatcher> create();
CV_NODISCARD_STD virtual Ptr<DescriptorMatcher> clone( bool emptyTrainData=false ) const CV_OVERRIDE;
// KNN匹配
CV_WRAP virtual void knnMatchImpl(cv::InputArray queryDescriptors,
std::vector<std::vector<cv::DMatch>>& matches,
int k,
cv::InputArrayOfArrays masks = cv::noArray(),
bool compactResult = false) CV_OVERRIDE;
// 半径匹配
virtual void radiusMatchImpl(cv::InputArray queryDescriptors,
std::vector<std::vector<cv::DMatch>>& matches,
float maxDistance,
cv::InputArrayOfArrays masks = cv::noArray(),
bool compactResult = false) CV_OVERRIDE;
protected:
// 将FLANN搜索结果转换为DMatch格式
static void convertToDMatches(const std::vector<cv::Mat>& descriptors,
const cv::Mat& indices,
const cv::Mat& distances,
std::vector<std::vector<cv::DMatch>>& matches);
private:
cv::Ptr<cv::flann::IndexParams> indexParams; // FLANN索引参数
cv::Ptr<cv::flann::SearchParams> searchParams; // FLANN搜索参数
cv::Ptr<cv::flann::Index> flannIndex; // FLANN索引
std::vector<cv::cuda::GpuMat> descriptorsGpu; // 存储GPU上的描述符
int addedDescCount;
};
.cpp文件函数实现:
// 构造函数实现
GpuFlannBasedMatcher::GpuFlannBasedMatcher(
const cv::Ptr<cv::flann::IndexParams>& indexParams,
const cv::Ptr<cv::flann::SearchParams>& searchParams
) : indexParams(indexParams), searchParams(searchParams){
// 初始化 flannIndex 或者其他成员变量
}
// 添加描述符
void GpuFlannBasedMatcher::add(cv::InputArrayOfArrays descriptors) {
// 将每个描述符上传到GPU内存
for (size_t i = 0; i < descriptors.total(); ++i) {
descriptorsGpu.push_back(cv::cuda::GpuMat(descriptors.getMat(i)));
}
}
// 清除描述符
void GpuFlannBasedMatcher::clear() {
descriptorsGpu.clear();
addedDescCount = 0;
}
void GpuFlannBasedMatcher::read( const FileNode& fn)
{
if (!indexParams)
indexParams = makePtr<flann::IndexParams>();
FileNode ip = fn["indexParams"];
CV_Assert(ip.type() == FileNode::SEQ);
for(int i = 0; i < (int)ip.size(); ++i)
{
CV_Assert(ip[i].type() == FileNode::MAP);
String _name = (String)ip[i]["name"];
FlannIndexType type = (FlannIndexType)(int)ip[i]["type"];
CV_CheckLE((int)type, (int)LAST_VALUE_FLANN_INDEX_TYPE, "");
switch(type)
{
case FLANN_INDEX_TYPE_8U:
case FLANN_INDEX_TYPE_8S:
case FLANN_INDEX_TYPE_16U:
case FLANN_INDEX_TYPE_16S:
case FLANN_INDEX_TYPE_32S:
indexParams->setInt(_name, (int) ip[i]["value"]);
break;
case FLANN_INDEX_TYPE_32F:
indexParams->setFloat(_name, (float) ip[i]["value"]);
break;
case FLANN_INDEX_TYPE_64F:
indexParams->setDouble(_name, (double) ip[i]["value"]);
break;
case FLANN_INDEX_TYPE_STRING:
indexParams->setString(_name, (String) ip[i]["value"]);
break;
case FLANN_INDEX_TYPE_BOOL:
indexParams->setBool(_name, (int) ip[i]["value"] != 0);
break;
case FLANN_INDEX_TYPE_ALGORITHM:
indexParams->setAlgorithm((int) ip[i]["value"]);
break;
// don't default: - compiler warning is here
};
}
if (!searchParams)
searchParams = makePtr<flann::SearchParams>();
FileNode sp = fn["searchParams"];
CV_Assert(sp.type() == FileNode::SEQ);
for(int i = 0; i < (int)sp.size(); ++i)
{
CV_Assert(sp[i].type() == FileNode::MAP);
String _name = (String)sp[i]["name"];
FlannIndexType type = (FlannIndexType)(int)sp[i]["type"];
CV_CheckLE((int)type, (int)LAST_VALUE_FLANN_INDEX_TYPE, "");
switch(type)
{
case FLANN_INDEX_TYPE_8U:
case FLANN_INDEX_TYPE_8S:
case FLANN_INDEX_TYPE_16U:
case FLANN_INDEX_TYPE_16S:
case FLANN_INDEX_TYPE_32S:
searchParams->setInt(_name, (int) sp[i]["value"]);
break;
case FLANN_INDEX_TYPE_32F:
searchParams->setFloat(_name, (float) sp[i]["value"]);
break;
case FLANN_INDEX_TYPE_64F:
searchParams->setDouble(_name, (double) sp[i]["value"]);
break;
case FLANN_INDEX_TYPE_STRING:
searchParams->setString(_name, (String) sp[i]["value"]);
break;
case FLANN_INDEX_TYPE_BOOL:
searchParams->setBool(_name, (int) sp[i]["value"] != 0);
break;
case FLANN_INDEX_TYPE_ALGORITHM:
searchParams->setAlgorithm((int) sp[i]["value"]);
break;
// don't default: - compiler warning is here
};
}
flannIndex.release();
}
void GpuFlannBasedMatcher::write( FileStorage& fs) const
{
writeFormat(fs);
fs << "indexParams" << "[";
if (indexParams)
{
std::vector<String> names;
std::vector<FlannIndexType> types;
std::vector<String> strValues;
std::vector<double> numValues;
indexParams->getAll(names, types, strValues, numValues);
for(size_t i = 0; i < names.size(); ++i)
{
fs << "{" << "name" << names[i] << "type" << types[i] << "value";
FlannIndexType type = (FlannIndexType)types[i];
if ((int)type < 0 || type > LAST_VALUE_FLANN_INDEX_TYPE)
{
fs << (double)numValues[i];
fs << "typename" << strValues[i];
fs << "}";
continue;
}
switch(type)
{
case FLANN_INDEX_TYPE_8U:
fs << (uchar)numValues[i];
break;
case FLANN_INDEX_TYPE_8S:
fs << (char)numValues[i];
break;
case FLANN_INDEX_TYPE_16U:
fs << (ushort)numValues[i];
break;
case FLANN_INDEX_TYPE_16S:
fs << (short)numValues[i];
break;
case FLANN_INDEX_TYPE_32S:
case FLANN_INDEX_TYPE_BOOL:
case FLANN_INDEX_TYPE_ALGORITHM:
fs << (int)numValues[i];
break;
case FLANN_INDEX_TYPE_32F:
fs << (float)numValues[i];
break;
case FLANN_INDEX_TYPE_64F:
fs << (double)numValues[i];
break;
case FLANN_INDEX_TYPE_STRING:
fs << strValues[i];
break;
// don't default: - compiler warning is here
}
fs << "}";
}
}
fs << "]" << "searchParams" << "[";
if (searchParams)
{
std::vector<String> names;
std::vector<FlannIndexType> types;
std::vector<String> strValues;
std::vector<double> numValues;
searchParams->getAll(names, types, strValues, numValues);
for(size_t i = 0; i < names.size(); ++i)
{
fs << "{" << "name" << names[i] << "type" << types[i] << "value";
FlannIndexType type = (FlannIndexType)types[i];
if ((int)type < 0 || type > LAST_VALUE_FLANN_INDEX_TYPE)
{
fs << (double)numValues[i];
fs << "typename" << strValues[i];
fs << "}";
continue;
}
switch(type)
{
case FLANN_INDEX_TYPE_8U:
fs << (uchar)numValues[i];
break;
case FLANN_INDEX_TYPE_8S:
fs << (char)numValues[i];
break;
case FLANN_INDEX_TYPE_16U:
fs << (ushort)numValues[i];
break;
case FLANN_INDEX_TYPE_16S:
fs << (short)numValues[i];
break;
case FLANN_INDEX_TYPE_32S:
case FLANN_INDEX_TYPE_BOOL:
case FLANN_INDEX_TYPE_ALGORITHM:
fs << (int)numValues[i];
break;
case CV_32F:
fs << (float)numValues[i];
break;
case CV_64F:
fs << (double)numValues[i];
break;
case FLANN_INDEX_TYPE_STRING:
fs << strValues[i];
break;
// don't default: - compiler warning is here
}
fs << "}";
}
}
fs << "]";
}
bool GpuFlannBasedMatcher::isMaskSupported() const
{
return false;
}
Ptr<DescriptorMatcher> GpuFlannBasedMatcher::clone( bool emptyTrainData ) const
{
Ptr<GpuFlannBasedMatcher> matcher = makePtr<GpuFlannBasedMatcher>(indexParams, searchParams);
if( !emptyTrainData )
{
CV_Error( Error::StsNotImplemented, "deep clone functionality is not implemented, because "
"Flann::Index has not copy constructor or clone method ");
#if 0
//matcher->flannIndex;
matcher->addedDescCount = addedDescCount;
matcher->mergedDescriptors = DescriptorCollection( mergedDescriptors );
std::transform( trainDescCollection.begin(), trainDescCollection.end(),
matcher->trainDescCollection.begin(), clone_op );
#endif
}
return matcher;
}
// 训练 FLANN 索引
void GpuFlannBasedMatcher::train() {
// 将所有的GPU描述符下载到CPU
std::vector<cv::Mat> cpuDescriptors;
for (size_t i = 0; i < descriptorsGpu.size(); ++i) {
cv::Mat temp;
descriptorsGpu[i].download(temp); // 修正download调用,传入一个cv::Mat作为参数
cpuDescriptors.push_back(temp);
}
// 将所有描述符合并成一个单独的矩阵
cv::Mat allDescriptors;
cv::vconcat(cpuDescriptors, allDescriptors);
// 使用合并后的描述符训练FLANN索引(索引训练在CPU上进行)
flannIndex = cv::makePtr<cv::flann::Index>(allDescriptors, *indexParams);
}
// KNN 匹配实现
void GpuFlannBasedMatcher::knnMatchImpl(
cv::InputArray queryDescriptors,
std::vector<std::vector<cv::DMatch>>& matches,
int k,
cv::InputArrayOfArrays masks,
bool compactResult
) {
// 将查询描述符上传到GPU
cv::cuda::GpuMat queryDescriptorsGpu = cv::cuda::GpuMat(queryDescriptors.getMat());
// 创建存储匹配结果的GPU矩阵
cv::cuda::GpuMat indices(queryDescriptorsGpu.rows, k, CV_32SC1); // 索引矩阵
cv::cuda::GpuMat distances(queryDescriptorsGpu.rows, k, CV_32FC1); // 距离矩阵
// 在GPU上执行FLANN的KNN搜索,返回匹配的索引和距离
flannIndex->knnSearch(queryDescriptorsGpu, indices, distances, k, *searchParams);
// 下载GPU上的搜索结果到CPU
cv::Mat indicesMat;
indices.download(indicesMat); // 修正download调用,传入cv::Mat对象
cv::Mat distancesMat;
distances.download(distancesMat); // 修正download调用,传入cv::Mat对象
// 将匹配结果转换为DMatch格式
// convertToDMatches(queryDescriptors, indicesMat, distancesMat, matches);
std::vector<cv::Mat> queryDescsVec;
queryDescriptors.getMatVector(queryDescsVec);
convertToDMatches(queryDescsVec, indicesMat, distancesMat, matches);
}
// 半径匹配实现
void GpuFlannBasedMatcher::radiusMatchImpl(
cv::InputArray queryDescriptors,
std::vector<std::vector<cv::DMatch>>& matches,
float maxDistance,
cv::InputArrayOfArrays masks,
bool compactResult
) {
// 将查询描述符传输到GPU
cv::cuda::GpuMat queryGpuDescriptors;
queryGpuDescriptors.upload(queryDescriptors.getMat());
// 创建存储结果的GPU矩阵
cv::cuda::GpuMat indicesGpu, distancesGpu;
// 如果描述符索引未创建,先创建FLANN索引
if (flannIndex.empty()) {
// 使用默认的FLANN索引类型(KDTree)
// flannIndex = cv::flann::Index(queryGpuDescriptors, *indexParams);
flannIndex = cv::makePtr<cv::flann::Index>(queryGpuDescriptors, *indexParams);
}
// 执行半径匹配
// flannIndex->radiusMatch(queryGpuDescriptors, indicesGpu, distancesGpu, maxDistance, *searchParams);
// flannIndex->radiusSearch(queryGpuDescriptors, indicesGpu, distancesGpu, maxDistance, *searchParams);
flannIndex->radiusSearch(queryGpuDescriptors, indicesGpu, distancesGpu, maxDistance, 0, *searchParams);
// 下载结果到CPU
cv::Mat indicesMat, distancesMat;
indicesGpu.download(indicesMat);
distancesGpu.download(distancesMat);
// 处理结果并转换为DMatch格式
matches.resize(indicesMat.rows);
for (int i = 0; i < indicesMat.rows; ++i) {
std::vector<cv::DMatch> matchRow;
for (int j = 0; j < indicesMat.cols; ++j) {
int index = indicesMat.at<int>(i, j);
if (index >= 0 && distancesMat.at<float>(i, j) <= maxDistance) {
matchRow.push_back(cv::DMatch(i, index, distancesMat.at<float>(i, j)));
}
}
matches[i] = matchRow;
}
}
// 将 FLANN 索引结果转换为 DMatch 格式
void GpuFlannBasedMatcher::convertToDMatches(
const std::vector<cv::Mat>& descriptors,
const cv::Mat& indices,
const cv::Mat& distances,
std::vector<std::vector<cv::DMatch>>& matches
) {
matches.clear();
for (int i = 0; i < indices.rows; ++i) {
std::vector<cv::DMatch> rowMatches;
for (int j = 0; j < indices.cols; ++j) {
int idx = indices.at<int>(i, j); // 获取匹配的索引
float dist = distances.at<float>(i, j); // 获取匹配的距离
rowMatches.push_back(cv::DMatch(i, idx, dist)); // 创建DMatch对象
}
matches.push_back(rowMatches);
}
}
编译: