// suggest new feature point for tracking (count point are kept) bool detect ( const image::Image<unsigned char> & ima, std::vector<features::PointFeature> & pt_to_track, const size_t count ) const override { cv::Mat current_img; cv::eigen2cv(ima.GetMat(), current_img); std::vector<cv::KeyPoint> m_nextKeypoints; cv::Ptr<cv::FeatureDetector> m_detector = cv::GFTTDetector::create(count); if (m_detector == NULL) return false; m_detector->detect(current_img, m_nextKeypoints); if (m_nextKeypoints.size() >= count) { // shuffle to avoid to sample only in one bucket std::mt19937 gen(std::mt19937::default_seed); std::shuffle(m_nextKeypoints.begin(), m_nextKeypoints.end(), gen); } const size_t kept_kp_count = std::min(m_nextKeypoints.size(), count); m_nextKeypoints.resize(kept_kp_count); pt_to_track.resize(kept_kp_count); for (size_t i = 0; i < kept_kp_count; ++i) pt_to_track[i] = features::PointFeature(m_nextKeypoints[i].pt.x, m_nextKeypoints[i].pt.y); return kept_kp_count != 0; // Return false if no point can be added }
/// Try to track current point set in the provided image /// return false when tracking failed (=> to send frame to relocalization) bool track ( const image::Image<unsigned char> & ima, const std::vector<features::PointFeature> & pt_to_track, std::vector<features::PointFeature> & pt_tracked, std::vector<bool> & status ) override { cv::eigen2cv(ima.GetMat(), current_img_); if (!pt_to_track.empty()) { prevPts_.resize(pt_to_track.size()); nextPts_.resize(pt_to_track.size()); for (size_t i=0; i < pt_to_track.size(); ++i) { prevPts_[i].x = pt_to_track[i].x(); prevPts_[i].y = pt_to_track[i].y(); } std::vector<unsigned char> status_uchar; cv::calcOpticalFlowPyrLK(prev_img_, current_img_, prevPts_, nextPts_, status_uchar, error_); status.assign(status_uchar.begin(), status_uchar.end()); for (size_t i=0; i < nextPts_.size(); ++i) { pt_tracked[i].coords() << nextPts_[i].x, nextPts_[i].y; } } // swap frame for next tracking iteration current_img_.copyTo(prev_img_); const size_t tracked_point_count = std::accumulate(status.begin(), status.end(), 0); return (tracked_point_count != 0); }
/** @brief Detect regions on the image and compute their attributes (description) @param image Image. @param regions The detected regions and attributes (the caller must delete the allocated data) @param mask 8-bit gray image for keypoint filtering (optional). Non-zero values depict the region of interest. */ bool Describe(const image::Image<unsigned char>& image, std::unique_ptr<Regions> ®ions, const image::Image<unsigned char> * mask = nullptr) { // Convert for opencv cv::Mat img; cv::eigen2cv(image.GetMat(), img); // Convert mask image into cv::Mat cv::Mat m_mask; if(mask != nullptr) { cv::eigen2cv(mask->GetMat(), m_mask); } // Create a SIFT detector std::vector< cv::KeyPoint > v_keypoints; cv::Mat m_desc; cv::Ptr<cv::Feature2D> siftdetector = cv::xfeatures2d::SIFT::create(); // Process SIFT computation siftdetector->detectAndCompute(img, m_mask, v_keypoints, m_desc); Allocate(regions); // Build alias to cached data SIFT_Regions * regionsCasted = dynamic_cast<SIFT_Regions*>(regions.get()); // reserve some memory for faster keypoint saving regionsCasted->Features().reserve(v_keypoints.size()); regionsCasted->Descriptors().reserve(v_keypoints.size()); // Prepare a column vector with the sum of each descriptor cv::Mat m_siftsum; cv::reduce(m_desc, m_siftsum, 1, cv::REDUCE_SUM); // Copy keypoints and descriptors in the regions int cpt = 0; for(std::vector< cv::KeyPoint >::const_iterator i_kp = v_keypoints.begin(); i_kp != v_keypoints.end(); ++i_kp, ++cpt) { SIOPointFeature feat((*i_kp).pt.x, (*i_kp).pt.y, (*i_kp).size, (*i_kp).angle); regionsCasted->Features().push_back(feat); Descriptor<unsigned char, 128> desc; for(int j = 0; j < 128; j++) { desc[j] = static_cast<unsigned char>(512.0*sqrt(m_desc.at<float>(cpt, j)/m_siftsum.at<float>(cpt, 0))); } regionsCasted->Descriptors().push_back(desc); } return true; };
/** @brief Detect regions on the image and compute their attributes (description) @param image Image. @param regions The detected regions and attributes (the caller must delete the allocated data) @param mask 8-bit gray image for keypoint filtering (optional). Non-zero values depict the region of interest. */ bool Describe ( const image::Image<unsigned char>& image, std::unique_ptr<Regions> ®ions, const image::Image<unsigned char> * mask = nullptr ) override { const int w = image.Width(), h = image.Height(); // Convert to float in range [0;1] const image::Image<float> If(image.GetMat().cast<float>()/255.0f); // compute sift keypoints Allocate(regions); // Build alias to cached data SIFT_Regions * regionsCasted = dynamic_cast<SIFT_Regions*>(regions.get()); { using namespace openMVG::features::sift; const int supplementary_images = 3; // => in order to ensure each gaussian slice is used in the process 3 extra images are required: // +1 for dog computation // +2 for 3d discrete extrema definition HierarchicalGaussianScaleSpace octave_gen( params_.num_octaves_, params_.num_scales_, (params_.first_octave_ == -1) ? GaussianScaleSpaceParams(1.6f/2.0f, 1.0f/2.0f, 0.5f, supplementary_images) : GaussianScaleSpaceParams(1.6f, 1.0f, 0.5f, supplementary_images)); octave_gen.SetImage( If ); std::vector<Keypoint> keypoints; keypoints.reserve(5000); Octave octave; while ( octave_gen.NextOctave( octave ) ) { std::vector< Keypoint > keys; // Find Keypoints SIFT_KeypointExtractor keypointDetector( params_.peak_threshold_ / octave_gen.NbSlice(), params_.edge_threshold_); keypointDetector(octave, keys); // Find Keypoints orientation and compute their description Sift_DescriptorExtractor descriptorExtractor; descriptorExtractor(octave, keys); // Concatenate the found keypoints std::move(keys.begin(), keys.end(), std::back_inserter(keypoints)); } for (const auto & k : keypoints) { // Feature masking if (mask) { const image::Image<unsigned char> & maskIma = *mask; if (maskIma(k.y, k.x) == 0) continue; } Descriptor<unsigned char, 128> descriptor; descriptor << (k.descr.cast<unsigned char>()); { regionsCasted->Descriptors().emplace_back(descriptor); regionsCasted->Features().emplace_back(k.x, k.y, k.sigma, k.theta); } } } return true; };
/** @brief Detect regions on the image and compute their attributes (description) @param image Image. @param regions The detected regions and attributes (the caller must delete the allocated data) @param mask 8-bit gray image for keypoint filtering (optional). Non-zero values depict the region of interest. */ bool Describe(const image::Image<unsigned char>& image, std::unique_ptr<Regions> ®ions, const image::Image<unsigned char> * mask = NULL) { const int w = image.Width(), h = image.Height(); //Convert to float const image::Image<float> If(image.GetMat().cast<float>()); VlSiftFilt *filt = vl_sift_new(w, h, _params._num_octaves, _params._num_scales, _params._first_octave); if (_params._edge_threshold >= 0) vl_sift_set_edge_thresh(filt, _params._edge_threshold); if (_params._peak_threshold >= 0) vl_sift_set_peak_thresh(filt, 255*_params._peak_threshold/_params._num_scales); Descriptor<vl_sift_pix, 128> descr; Descriptor<unsigned char, 128> descriptor; // Process SIFT computation vl_sift_process_first_octave(filt, If.data()); Allocate(regions); // Build alias to cached data SIFT_Regions * regionsCasted = dynamic_cast<SIFT_Regions*>(regions.get()); // reserve some memory for faster keypoint saving regionsCasted->Features().reserve(2000); regionsCasted->Descriptors().reserve(2000); while (true) { vl_sift_detect(filt); VlSiftKeypoint const *keys = vl_sift_get_keypoints(filt); const int nkeys = vl_sift_get_nkeypoints(filt); // Update gradient before launching parallel extraction vl_sift_update_gradient(filt); #ifdef OPENMVG_USE_OPENMP #pragma omp parallel for private(descr, descriptor) #endif for (int i = 0; i < nkeys; ++i) { // Feature masking if (mask) { const image::Image<unsigned char> & maskIma = *mask; if (maskIma(keys[i].y, keys[i].x) == 0) continue; } double angles [4] = {0.0, 0.0, 0.0, 0.0}; int nangles = 1; // by default (1 upright feature) if (_bOrientation) { // compute from 1 to 4 orientations nangles = vl_sift_calc_keypoint_orientations(filt, angles, keys+i); } for (int q=0 ; q < nangles ; ++q) { vl_sift_calc_keypoint_descriptor(filt, &descr[0], keys+i, angles[q]); const SIOPointFeature fp(keys[i].x, keys[i].y, keys[i].sigma, static_cast<float>(angles[q])); siftDescToUChar(&descr[0], descriptor, _params._root_sift); #ifdef OPENMVG_USE_OPENMP #pragma omp critical #endif { regionsCasted->Descriptors().push_back(descriptor); regionsCasted->Features().push_back(fp); } } } if (vl_sift_process_next_octave(filt)) break; // Last octave } vl_sift_delete(filt); return true; };