/**
  @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,
    OPENMVG_UNIQUE_PTR<Regions> &regions,
    const image::Image<unsigned char> * mask = NULL)
  {
    _params._options.fDesc_factor =
      (_params._eAkazeDescriptor == AKAZE_MSURF ||
      _params._eAkazeDescriptor == AKAZE_LIOP) ? 10.f*sqrtf(2.f)
      : 11.f*sqrtf(2.f); // MLDB

    AKAZE akaze(image, _params._options);
    akaze.Compute_AKAZEScaleSpace();
    std::vector<AKAZEKeypoint> kpts;
    kpts.reserve(5000);
    akaze.Feature_Detection(kpts);
    akaze.Do_Subpixel_Refinement(kpts);

    Allocate(regions);

    switch(_params._eAkazeDescriptor)
    {
      case AKAZE_MSURF:
      {
        // Build alias to cached data
        AKAZE_Float_Regions * regionsCasted = dynamic_cast<AKAZE_Float_Regions*>(regions.get());
        regionsCasted->Features().resize(kpts.size());
        regionsCasted->Descriptors().resize(kpts.size());

      #ifdef OPENMVG_USE_OPENMP
        #pragma omp parallel for
      #endif
        for (int i = 0; i < static_cast<int>(kpts.size()); ++i)
        {
          AKAZEKeypoint ptAkaze = kpts[i];

          // Feature masking
          if (mask)
          {
            const image::Image<unsigned char> & maskIma = *mask;
            if (maskIma(ptAkaze.y, ptAkaze.x) > 0)
              continue;
          }

          const TEvolution & cur_slice = akaze.getSlices()[ptAkaze.class_id];

          if (_bOrientation)
            akaze.Compute_Main_Orientation(ptAkaze, cur_slice.Lx, cur_slice.Ly);
          else
            ptAkaze.angle = 0.0f;

          regionsCasted->Features()[i] =
            SIOPointFeature(ptAkaze.x, ptAkaze.y, ptAkaze.size, ptAkaze.angle);

          ComputeMSURFDescriptor(cur_slice.Lx, cur_slice.Ly, ptAkaze.octave,
            regionsCasted->Features()[i],
            regionsCasted->Descriptors()[i]);
        }
      }
      break;
      case AKAZE_LIOP:
      {
        // Build alias to cached data
        AKAZE_Liop_Regions * regionsCasted = dynamic_cast<AKAZE_Liop_Regions*>(regions.get());
        regionsCasted->Features().resize(kpts.size());
        regionsCasted->Descriptors().resize(kpts.size());

        // Init LIOP extractor
        LIOP::Liop_Descriptor_Extractor liop_extractor;

      #ifdef OPENMVG_USE_OPENMP
        #pragma omp parallel for
      #endif
        for (int i = 0; i < static_cast<int>(kpts.size()); ++i)
        {
          AKAZEKeypoint ptAkaze = kpts[i];

          // Feature masking
          if (mask)
          {
            const image::Image<unsigned char> & maskIma = *mask;
            if (maskIma(ptAkaze.y, ptAkaze.x) > 0)
              continue;
          }

          const TEvolution & cur_slice = akaze.getSlices()[ptAkaze.class_id];

          if (_bOrientation)
            akaze.Compute_Main_Orientation(ptAkaze, cur_slice.Lx, cur_slice.Ly);
          else
            ptAkaze.angle = 0.0f;

          regionsCasted->Features()[i] =
            SIOPointFeature(ptAkaze.x, ptAkaze.y, ptAkaze.size, ptAkaze.angle);

          // Compute LIOP descriptor (do not need rotation computation, since
          //  LIOP descriptor is rotation invariant).
          // Rescale for LIOP patch extraction
          const SIOPointFeature fp =
              SIOPointFeature(ptAkaze.x, ptAkaze.y,
              ptAkaze.size/2.0, ptAkaze.angle);

          float desc[144];
          liop_extractor.extract(image, fp, desc);
          for(int j=0; j < 144; ++j)
            regionsCasted->Descriptors()[i][j] =
              static_cast<unsigned char>(desc[j] * 255.f +.5f);
        }
      }
      break;
      case AKAZE_MLDB:
      {
        // Build alias to cached data
        AKAZE_Binary_Regions * regionsCasted = dynamic_cast<AKAZE_Binary_Regions*>(regions.get());
        regionsCasted->Features().resize(kpts.size());
        regionsCasted->Descriptors().resize(kpts.size());

      #ifdef OPENMVG_USE_OPENMP
        #pragma omp parallel for
      #endif
        for (int i = 0; i < static_cast<int>(kpts.size()); ++i)
        {
          AKAZEKeypoint ptAkaze = kpts[i];

          // Feature masking
          if (mask)
          {
            const image::Image<unsigned char> & maskIma = *mask;
            if (maskIma(ptAkaze.y, ptAkaze.x) > 0)
              continue;
          }

          const TEvolution & cur_slice = akaze.getSlices()[ptAkaze.class_id];

          if (_bOrientation)
            akaze.Compute_Main_Orientation(ptAkaze, cur_slice.Lx, cur_slice.Ly);
          else
            ptAkaze.angle = 0.0f;

          regionsCasted->Features()[i] =
            SIOPointFeature(ptAkaze.x, ptAkaze.y, ptAkaze.size, ptAkaze.angle);

          // Compute MLDB descriptor
		      Descriptor<bool,486> desc;
      		ComputeMLDBDescriptor(cur_slice.cur, cur_slice.Lx, cur_slice.Ly,
            ptAkaze.octave, regionsCasted->Features()[i], desc);
          // convert the bool vector to the binary unsigned char array
          unsigned char * ptr = reinterpret_cast<unsigned char*>(&regionsCasted->Descriptors()[i]);
          memset(ptr, 0, regionsCasted->DescriptorLength()*sizeof(unsigned char));
          // For each byte
          for (int j = 0; j < std::ceil(486./8.); ++j, ++ptr) {
            // set the corresponding 8bits to the good values
            for (int iBit = 0; iBit < 8 && j*8+iBit < 486; ++iBit)  {
              *ptr |= desc[j*8+iBit] << iBit;
            }
          }
        }
      }
      break;
    }
    return true;
  };
Esempio n. 2
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> &regions,
                  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;
    };