DIC源码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

581 lines
18 KiB

3 months ago
#include <numeric>
#include "oc_feature_affine.h"
namespace opencorr
{
NearestNeighbor* FeatureAffine2D::getInstance(int tid)
{
if (tid >= (int)instance_pool.size())
{
throw std::string("CPU thread ID over limit");
}
return instance_pool[tid];
}
FeatureAffine2D::FeatureAffine2D(int radius_x, int radius_y, int thread_number)
{
this->subset_radius_x = radius_x;
this->subset_radius_y = radius_y;
neighbor_search_radius = sqrt((float)(radius_x * radius_x + radius_y * radius_y));
min_neighbor_num = 7;
ransac_config.error_threshold = 1.5f;
ransac_config.sample_mumber = 3;
ransac_config.trial_number = 20;
this->thread_number = thread_number;
for (int i = 0; i < thread_number; i++)
{
NearestNeighbor* instance = new NearestNeighbor();
instance_pool.push_back(instance);
}
}
FeatureAffine2D::~FeatureAffine2D()
{
for (auto& instance : instance_pool)
{
delete instance;
}
instance_pool.clear();
}
RansacConfig FeatureAffine2D::getRansacConfig() const
{
return ransac_config;
}
float FeatureAffine2D::getSearchRadius() const
{
return neighbor_search_radius;
}
int FeatureAffine2D::getMinNeighborNumber() const
{
return min_neighbor_num;
}
void FeatureAffine2D::setSearchParameters(float neighbor_search_radius, int min_neighbor_num)
{
this->neighbor_search_radius = neighbor_search_radius;
this->min_neighbor_num = min_neighbor_num;
}
void FeatureAffine2D::setRansacConfig(RansacConfig ransac_config)
{
this->ransac_config = ransac_config;
}
void FeatureAffine2D::setKeypointPair(std::vector<Point2D>& ref_kp, std::vector<Point2D>& tar_kp)
{
this->ref_kp = ref_kp;
this->tar_kp = tar_kp;
}
void FeatureAffine2D::prepare()
{
#pragma omp parallel for
for (int i = 0; i < thread_number; i++)
{
instance_pool[i]->assignPoints(ref_kp);
instance_pool[i]->setSearchRadius(neighbor_search_radius);
instance_pool[i]->setSearchK(min_neighbor_num);
instance_pool[i]->constructKdTree();
}
}
void FeatureAffine2D::compute(POI2D* poi)
{
//set instance w.r.t. thread id
NearestNeighbor* neighbor_search = getInstance(omp_get_thread_num());
Point3D current_point(poi->x, poi->y, 0.f);
std::vector<Point2D> ref_candidates, tar_candidates;
//search the neighbor keypoints in a region of given radius
std::vector<nanoflann::ResultItem<uint32_t, float>> current_matches;
int neighbor_num = neighbor_search->radiusSearch(current_point, current_matches);
if (neighbor_num < ransac_config.sample_mumber)
{
poi->result.zncc = -1;
}
else
{
ref_candidates.resize(neighbor_num);
tar_candidates.resize(neighbor_num);
if (neighbor_num >= min_neighbor_num)
{
for (int i = 0; i < neighbor_num; i++)
{
ref_candidates[i] = ref_kp[current_matches[i].first];
tar_candidates[i] = tar_kp[current_matches[i].first];
}
}
else //try KNN search if the obtained neighbor keypoints are not enough
{
std::vector<Point2D>().swap(ref_candidates);
std::vector<Point2D>().swap(tar_candidates);
std::vector<uint32_t> k_neighbors_idx;
std::vector<float> kp_squared_distance;
neighbor_num = neighbor_search->knnSearch(current_point, k_neighbors_idx, kp_squared_distance);
ref_candidates.resize(neighbor_num);
tar_candidates.resize(neighbor_num);
for (int i = 0; i < neighbor_num; i++)
{
ref_candidates[i] = ref_kp[k_neighbors_idx[i]];
tar_candidates[i] = tar_kp[k_neighbors_idx[i]];
}
}
//use brutal force search for the last chance
if (neighbor_num < min_neighbor_num)
{
std::vector<Point2D>().swap(ref_candidates);
std::vector<Point2D>().swap(tar_candidates);
//sort the kp queue in an ascending order of distance to the POI
std::vector<KeypointIndex> ref_sorted_index;
int queue_size = (int)ref_kp.size();
for (int i = 0; i < queue_size; ++i)
{
Point2D distance = ref_kp[i] - (Point2D)*poi;
KeypointIndex current_kp_idx;
current_kp_idx.kp_idx = i;
current_kp_idx.distance = distance.vectorNorm();
ref_sorted_index.push_back(current_kp_idx);
}
std::sort(ref_sorted_index.begin(), ref_sorted_index.end(), sortByDistance);
//pick the keypoints for RANSAC procedure
int i = 0;
while (ref_sorted_index[i].distance < neighbor_search_radius || ref_candidates.size() < min_neighbor_num)
{
ref_candidates.push_back(ref_kp[ref_sorted_index[i].kp_idx]);
tar_candidates.push_back(tar_kp[ref_sorted_index[i].kp_idx]);
i++;
}
neighbor_num = (int)ref_candidates.size();
}
//convert global coordinates to the POI-centered local coordinates
for (int i = 0; i < neighbor_num; ++i)
{
ref_candidates[i] = ref_candidates[i] - (Point2D)*poi;
tar_candidates[i] = tar_candidates[i] - (Point2D)*poi;
}
//RANSAC procedure
std::vector<int> candidate_index(neighbor_num);
std::iota(candidate_index.begin(), candidate_index.end(), 0); //initialize the candidate_index with the integers in ascending order
int trial_counter = 0; //trial counter
float location_mean_error;
std::vector<int> max_set;
//random number generator
std::random_device rd;
std::mt19937_64 gen64(rd());
do
{
//randomly select samples
std::shuffle(candidate_index.begin(), candidate_index.end(), gen64);
Eigen::MatrixXf ref_neighbors(ransac_config.sample_mumber, 3);
Eigen::MatrixXf tar_neighbors(ransac_config.sample_mumber, 3);
for (int j = 0; j < ransac_config.sample_mumber; j++)
{
ref_neighbors(j, 0) = ref_candidates[candidate_index[j]].x;
ref_neighbors(j, 1) = ref_candidates[candidate_index[j]].y;
ref_neighbors(j, 2) = 1.f;
tar_neighbors(j, 0) = tar_candidates[candidate_index[j]].x;
tar_neighbors(j, 1) = tar_candidates[candidate_index[j]].y;
tar_neighbors(j, 2) = 1.f;
}
//ref * affine = tar, thus affine is the permutation of affine matrix in the paper, where Ax=x'
Eigen::Matrix3f affine_matrix = ref_neighbors.colPivHouseholderQr().solve(tar_neighbors);
//concensus
std::vector<int> trial_set;
location_mean_error = 0;
float delta_x, delta_y, estimation_error;
for (int j = 0; j < neighbor_num; j++)
{
delta_x = (float)(ref_candidates[candidate_index[j]].x * affine_matrix(0, 0)
+ ref_candidates[candidate_index[j]].y * affine_matrix(1, 0) + affine_matrix(2, 0))
- tar_candidates[candidate_index[j]].x;
delta_y = (float)(ref_candidates[candidate_index[j]].x * affine_matrix(0, 1)
+ ref_candidates[candidate_index[j]].y * affine_matrix(1, 1) + affine_matrix(2, 1))
- tar_candidates[candidate_index[j]].y;
Point2D cur_point(delta_x, delta_y);
estimation_error = cur_point.vectorNorm();
//check if the error is acceptable, keep the "good" points
if (estimation_error < ransac_config.error_threshold)
{
trial_set.push_back(candidate_index[j]);
location_mean_error += estimation_error;
}
}
//replace max_set with current trial_set if the latter is larger
if (trial_set.size() > max_set.size())
{
max_set.assign(trial_set.begin(), trial_set.end());
}
trial_counter++;
location_mean_error /= trial_set.size();
} while (trial_counter < ransac_config.trial_number &&
(max_set.size() < min_neighbor_num || location_mean_error > ransac_config.error_threshold / min_neighbor_num));
//calculate affine matrix according to the results of concensus
int max_set_size = (int)max_set.size();
if (max_set_size < 3) //essential condition to solve the equation
{
poi->result.zncc = -2;
}
else
{
Eigen::MatrixXf ref_neighbors(max_set_size, 3);
Eigen::MatrixXf tar_neighbors(max_set_size, 3);
for (int i = 0; i < max_set_size; i++)
{
ref_neighbors(i, 0) = ref_candidates[max_set[i]].x;
ref_neighbors(i, 1) = ref_candidates[max_set[i]].y;
ref_neighbors(i, 2) = 1.f;
tar_neighbors(i, 0) = tar_candidates[max_set[i]].x;
tar_neighbors(i, 1) = tar_candidates[max_set[i]].y;
tar_neighbors(i, 2) = 1.f;
}
//the method of least squares
Eigen::Matrix3f affine_matrix = ref_neighbors.colPivHouseholderQr().solve(tar_neighbors);
//calculate the 1st order deformation according to the equivalence between affine matrix and the 1st order shape function
poi->deformation.u = affine_matrix(2, 0);
poi->deformation.ux = affine_matrix(0, 0) - 1.f;
poi->deformation.uy = affine_matrix(1, 0);
poi->deformation.v = affine_matrix(2, 1);
poi->deformation.vx = affine_matrix(0, 1);
poi->deformation.vy = affine_matrix(1, 1) - 1.f;
//store results of RANSAC procedure
poi->result.iteration = (float)trial_counter;
poi->result.feature = (float)max_set_size;
poi->result.zncc = 0;
}
}
}
void FeatureAffine2D::compute(std::vector<POI2D>& poi_queue)
{
int queue_length = (int)poi_queue.size();
#pragma omp parallel for
for (int i = 0; i < queue_length; ++i)
{
compute(&poi_queue[i]);
}
}
NearestNeighbor* FeatureAffine3D::getInstance(int tid)
{
if (tid >= (int)instance_pool.size())
{
throw std::string("CPU thread ID over limit");
}
return instance_pool[tid];
}
FeatureAffine3D::FeatureAffine3D(int radius_x, int radius_y, int radius_z, int thread_number)
{
this->subset_radius_x = radius_x;
this->subset_radius_y = radius_y;
neighbor_search_radius = sqrt((float)(radius_x * radius_x + radius_y * radius_y + radius_z * radius_z));
min_neighbor_num = 16;
ransac_config.error_threshold = 3.2f;
ransac_config.sample_mumber = 4;
ransac_config.trial_number = 32;
this->thread_number = thread_number;
for (int i = 0; i < thread_number; i++)
{
NearestNeighbor* instance = new NearestNeighbor();
instance_pool.push_back(instance);
}
}
FeatureAffine3D::~FeatureAffine3D()
{
for (auto& instance : instance_pool)
{
delete instance;
}
instance_pool.clear();
}
void FeatureAffine3D::prepare()
{
#pragma omp parallel for
for (int i = 0; i < thread_number; i++)
{
instance_pool[i]->assignPoints(ref_kp);
instance_pool[i]->setSearchRadius(neighbor_search_radius);
instance_pool[i]->setSearchK(min_neighbor_num);
instance_pool[i]->constructKdTree();
}
}
void FeatureAffine3D::compute(POI3D* poi)
{
//set instance w.r.t. thread id
NearestNeighbor* neighbor_search = getInstance(omp_get_thread_num());
Point3D current_point(poi->x, poi->y, poi->z);
std::vector<Point3D> ref_candidates, tar_candidates;
//search the neighbor keypoints in a region of given radius
std::vector<nanoflann::ResultItem<uint32_t, float>> current_matches;
int neighbor_num = neighbor_search->radiusSearch(current_point, current_matches);
if (neighbor_num < ransac_config.sample_mumber)
{
poi->result.zncc = -1;
}
else
{
ref_candidates.resize(neighbor_num);
tar_candidates.resize(neighbor_num);
if (neighbor_num >= min_neighbor_num)
{
for (int i = 0; i < neighbor_num; i++)
{
ref_candidates[i] = ref_kp[current_matches[i].first];
tar_candidates[i] = tar_kp[current_matches[i].first];
}
}
else //try KNN search if the obtained neighbor keypoints are not enough
{
std::vector<Point3D>().swap(ref_candidates);
std::vector<Point3D>().swap(tar_candidates);
std::vector<uint32_t> k_neighbors_idx;
std::vector<float> kp_squared_distance;
neighbor_num = neighbor_search->knnSearch(current_point, k_neighbors_idx, kp_squared_distance);
ref_candidates.resize(neighbor_num);
tar_candidates.resize(neighbor_num);
for (int i = 0; i < neighbor_num; i++)
{
ref_candidates[i] = ref_kp[k_neighbors_idx[i]];
tar_candidates[i] = tar_kp[k_neighbors_idx[i]];
}
}
//use brutal force search for the last chance
if (neighbor_num < min_neighbor_num)
{
std::vector<Point3D>().swap(ref_candidates);
std::vector<Point3D>().swap(tar_candidates);
//sort the kp queue in an ascending order of distance to the POI
std::vector<KeypointIndex> ref_sorted_index;
int queue_size = (int)ref_kp.size();
for (int i = 0; i < queue_size; ++i)
{
Point3D distance = ref_kp[i] - (Point3D)*poi;
KeypointIndex current_kp_idx;
current_kp_idx.kp_idx = i;
current_kp_idx.distance = distance.vectorNorm();
ref_sorted_index.push_back(current_kp_idx);
}
std::sort(ref_sorted_index.begin(), ref_sorted_index.end(), sortByDistance);
//pick the keypoints for RANSAC procedure
int i = 0;
while (ref_sorted_index[i].distance < neighbor_search_radius || ref_candidates.size() < min_neighbor_num)
{
ref_candidates.push_back(ref_kp[ref_sorted_index[i].kp_idx]);
tar_candidates.push_back(tar_kp[ref_sorted_index[i].kp_idx]);
i++;
}
neighbor_num = (int)ref_candidates.size();
}
//convert global coordinates to the POI-centered local coordinates
for (int i = 0; i < neighbor_num; ++i)
{
ref_candidates[i] = ref_candidates[i] - (Point3D)*poi;
tar_candidates[i] = tar_candidates[i] - (Point3D)*poi;
}
//RANSAC procedure
std::vector<int> candidate_index(neighbor_num);
std::iota(candidate_index.begin(), candidate_index.end(), 0); //initialize the candidate_queue with value in ascending order
int trial_counter = 0; //trial counter
float location_mean_error;
std::vector<int> max_set;
//random number generator
std::random_device rd;
std::mt19937_64 gen64(rd());
do
{
//randomly select samples
std::shuffle(candidate_index.begin(), candidate_index.end(), gen64);
Eigen::MatrixXf tar_neighbors(ransac_config.sample_mumber, 4);
Eigen::MatrixXf ref_neighbors(ransac_config.sample_mumber, 4);
for (int j = 0; j < ransac_config.sample_mumber; j++) {
tar_neighbors(j, 0) = tar_candidates[candidate_index[j]].x;
tar_neighbors(j, 1) = tar_candidates[candidate_index[j]].y;
tar_neighbors(j, 2) = tar_candidates[candidate_index[j]].z;
tar_neighbors(j, 3) = 1.f;
ref_neighbors(j, 0) = ref_candidates[candidate_index[j]].x;
ref_neighbors(j, 1) = ref_candidates[candidate_index[j]].y;
ref_neighbors(j, 2) = ref_candidates[candidate_index[j]].z;
ref_neighbors(j, 3) = 1.f;
}
//ref * affine = tar, thus affine is the permutation of affine matrix in the paper, where Ax=x'
Eigen::Matrix4f affine_matrix = ref_neighbors.colPivHouseholderQr().solve(tar_neighbors);
//concensus
std::vector<int> trial_set;
location_mean_error = 0;
float delta_x, delta_y, delta_z, estimation_error;
for (int j = 0; j < neighbor_num; j++) {
delta_x = (float)(ref_candidates[candidate_index[j]].x * affine_matrix(0, 0)
+ ref_candidates[candidate_index[j]].y * affine_matrix(1, 0)
+ ref_candidates[candidate_index[j]].z * affine_matrix(2, 0) + affine_matrix(3, 0))
- tar_candidates[candidate_index[j]].x;
delta_y = (float)(ref_candidates[candidate_index[j]].x * affine_matrix(0, 1)
+ ref_candidates[candidate_index[j]].y * affine_matrix(1, 1)
+ ref_candidates[candidate_index[j]].z * affine_matrix(2, 1) + affine_matrix(3, 1))
- tar_candidates[candidate_index[j]].y;
delta_z = (float)(ref_candidates[candidate_index[j]].x * affine_matrix(0, 2)
+ ref_candidates[candidate_index[j]].y * affine_matrix(1, 2)
+ ref_candidates[candidate_index[j]].z * affine_matrix(2, 2) + affine_matrix(3, 2))
- tar_candidates[candidate_index[j]].z;
Point3D point(delta_x, delta_y, delta_z);
estimation_error = point.vectorNorm();
//check if the error is acceptable, keep the "good" points
if (estimation_error < ransac_config.error_threshold) {
trial_set.push_back(candidate_index[j]);
location_mean_error += estimation_error;
}
}
//replace max_set with current trial_set if the latter is larger
if (trial_set.size() > max_set.size()) {
max_set.assign(trial_set.begin(), trial_set.end());
}
trial_counter++;
location_mean_error /= trial_set.size();
} while (trial_counter < ransac_config.trial_number &&
(max_set.size() < min_neighbor_num || location_mean_error > ransac_config.error_threshold / min_neighbor_num));
//calculate affine matrix according to the results of concensus
int max_set_size = (int)max_set.size();
if (max_set_size < 4) //essential condition to solve the equation
{
poi->result.zncc = -2;
}
Eigen::MatrixXf tar_neighbors(max_set_size, 4);
Eigen::MatrixXf ref_neighbors(max_set_size, 4);
for (int i = 0; i < max_set_size; i++)
{
ref_neighbors(i, 0) = ref_candidates[max_set[i]].x;
ref_neighbors(i, 1) = ref_candidates[max_set[i]].y;
ref_neighbors(i, 2) = ref_candidates[max_set[i]].z;
ref_neighbors(i, 3) = 1.f;
tar_neighbors(i, 0) = tar_candidates[max_set[i]].x;
tar_neighbors(i, 1) = tar_candidates[max_set[i]].y;
tar_neighbors(i, 2) = tar_candidates[max_set[i]].z;
tar_neighbors(i, 3) = 1.f;
}
//the method of least squares
Eigen::Matrix4f affine_matrix = ref_neighbors.colPivHouseholderQr().solve(tar_neighbors);
//calculate the 1st order deformation according to the equivalence between affine matrix and 1st order shape function
poi->deformation.u = affine_matrix(3, 0);
poi->deformation.ux = affine_matrix(0, 0) - 1.f;
poi->deformation.uy = affine_matrix(1, 0);
poi->deformation.uz = affine_matrix(2, 0);
poi->deformation.v = affine_matrix(3, 1);
poi->deformation.vx = affine_matrix(0, 1);
poi->deformation.vy = affine_matrix(1, 1) - 1.f;
poi->deformation.vz = affine_matrix(2, 1);
poi->deformation.w = affine_matrix(3, 2);
poi->deformation.wx = affine_matrix(0, 2);
poi->deformation.wy = affine_matrix(1, 2);
poi->deformation.wy = affine_matrix(2, 2) - 1.f;
//store results of RANSAC procedure
poi->result.iteration = (float)trial_counter;
poi->result.feature = (float)max_set_size;
poi->result.zncc = 0;
}
}
void FeatureAffine3D::compute(std::vector<POI3D>& poi_queue)
{
int queue_length = (int)poi_queue.size();
#pragma omp parallel for
for (int i = 0; i < queue_length; ++i)
{
compute(&poi_queue[i]);
}
}
RansacConfig FeatureAffine3D::getRansacConfig() const
{
return ransac_config;
}
float FeatureAffine3D::getSearchRadius() const
{
return neighbor_search_radius;
}
int FeatureAffine3D::getMinNeighborNumber() const
{
return min_neighbor_num;
}
void FeatureAffine3D::setSearchParameters(float neighbor_search_radius, int min_neighbor_num)
{
this->neighbor_search_radius = neighbor_search_radius;
this->min_neighbor_num = min_neighbor_num;
}
void FeatureAffine3D::setRansacConfig(RansacConfig ransac_config)
{
this->ransac_config = ransac_config;
}
void FeatureAffine3D::setKeypointPair(std::vector<Point3D>& ref_kp, std::vector<Point3D>& tar_kp)
{
this->ref_kp = ref_kp;
this->tar_kp = tar_kp;
}
}//namespace opencorr