LCOV - code coverage report
Current view: top level - alg - gdalmatching.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 0 89 0.0 %
Date: 2024-11-21 22:18:42 Functions: 0 2 0.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  GDAL Wrapper for image matching via correlation algorithm.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  * Author:   Andrew Migal, migal.drew@gmail.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2012, Frank Warmerdam
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "gdal_alg.h"
      15             : #include "gdal_simplesurf.h"
      16             : 
      17             : //! @cond Doxygen_Suppress
      18             : 
      19             : //! @endcond
      20             : 
      21             : // TODO(schwehr): What?  This below: "0,001"
      22             : 
      23             : /**
      24             :  * @file
      25             :  * @author Andrew Migal migal.drew@gmail.com
      26             :  * @brief Algorithms for searching corresponding points on images.
      27             :  * @details This implementation is  based on an simplified version
      28             :  * of SURF algorithm (Speeded Up Robust Features).
      29             :  * Provides capability for detection feature points
      30             :  * and finding equal points on different images.
      31             :  * As original, this realization is scale invariant, but sensitive to rotation.
      32             :  * Images should have similar rotation angles (maximum difference is up to 10-15
      33             :  * degrees), otherwise algorithm produces incorrect and very unstable results.
      34             :  */
      35             : 
      36             : /**
      37             :  * Detect feature points on provided image. Please carefully read documentation
      38             :  * below.
      39             :  *
      40             :  * @param poDataset Image on which feature points will be detected
      41             :  * @param panBands Array of 3 raster bands numbers, for Red, Green, Blue bands
      42             :  * (in that order)
      43             :  * @param nOctaveStart Number of bottom octave. Octave numbers starts from one.
      44             :  * This value directly and strongly affects to amount of recognized points
      45             :  * @param nOctaveEnd Number of top octave. Should be equal or greater than
      46             :  * octaveStart
      47             :  * @param dfThreshold Value from 0 to 1. Threshold for feature point
      48             :  * recognition.  Number of detected points is larger if threshold is lower
      49             :  *
      50             :  * @see GDALFeaturePoint, GDALSimpleSURF class for details.
      51             :  *
      52             :  * @note Every octave finds points in specific size. For small images
      53             :  * use small octave numbers, for high resolution - large.
      54             :  * For 1024x1024 images it's normal to use any octave numbers from range 1-6.
      55             :  * (for example, octave start - 1, octave end - 3, or octave start - 2, octave
      56             :  * end - 2.)
      57             :  * For larger images, try 1-10 range or even higher.
      58             :  * Pay attention that number of detected point decreases quickly per octave for
      59             :  * particular image. Algorithm finds more points in case of small octave number.
      60             :  * If method detects nothing, reduce octave start value.
      61             :  * In addition, if many feature points are required (the largest possible
      62             :  * amount), use the lowest octave start value (1) and wide octave range.
      63             :  *
      64             :  * @note Typical threshold's value is 0,001. It's pretty good for all images.
      65             :  * But this value depends on image's nature and may be various in each
      66             :  * particular case.
      67             :  * For example, value can be 0,002 or 0,005.
      68             :  * Notice that number of detected points is larger if threshold is lower.
      69             :  * But with high threshold feature points will be better - "stronger", more
      70             :  * "unique" and distinctive.
      71             :  *
      72             :  * Feel free to experiment with parameters, because character, robustness and
      73             :  * number of points entirely depend on provided range of octaves and threshold.
      74             :  *
      75             :  * NOTICE that every octave requires time to compute. Use a little range
      76             :  * or only one octave, if execution time is significant.
      77             :  *
      78             :  * @return CE_None or CE_Failure if error occurs.
      79             :  */
      80             : 
      81             : static std::vector<GDALFeaturePoint> *
      82           0 : GatherFeaturePoints(GDALDataset *poDataset, int *panBands, int nOctaveStart,
      83             :                     int nOctaveEnd, double dfThreshold)
      84             : {
      85           0 :     if (poDataset == nullptr)
      86             :     {
      87           0 :         CPLError(CE_Failure, CPLE_AppDefined, "GDALDataset isn't specified");
      88           0 :         return nullptr;
      89             :     }
      90             : 
      91           0 :     if (panBands == nullptr)
      92             :     {
      93           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Raster bands are not specified");
      94           0 :         return nullptr;
      95             :     }
      96             : 
      97           0 :     if (nOctaveStart <= 0 || nOctaveEnd < 0 || nOctaveStart > nOctaveEnd)
      98             :     {
      99           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Octave numbers are invalid");
     100           0 :         return nullptr;
     101             :     }
     102             : 
     103           0 :     if (dfThreshold < 0)
     104             :     {
     105           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     106             :                  "Threshold have to be greater than zero");
     107           0 :         return nullptr;
     108             :     }
     109             : 
     110           0 :     GDALRasterBand *poRstRedBand = poDataset->GetRasterBand(panBands[0]);
     111           0 :     GDALRasterBand *poRstGreenBand = poDataset->GetRasterBand(panBands[1]);
     112           0 :     GDALRasterBand *poRstBlueBand = poDataset->GetRasterBand(panBands[2]);
     113             : 
     114           0 :     const int nWidth = poRstRedBand->GetXSize();
     115           0 :     const int nHeight = poRstRedBand->GetYSize();
     116             : 
     117           0 :     if (nWidth == 0 || nHeight == 0)
     118             :     {
     119           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     120             :                  "Must have non-zero width and height.");
     121           0 :         return nullptr;
     122             :     }
     123             : 
     124             :     // Allocate memory for grayscale image.
     125           0 :     double **padfImg = new double *[nHeight];
     126           0 :     for (int i = 0;;)
     127             :     {
     128           0 :         padfImg[i] = new double[nWidth];
     129           0 :         for (int j = 0; j < nWidth; ++j)
     130           0 :             padfImg[i][j] = 0.0;
     131           0 :         ++i;
     132           0 :         if (i == nHeight)
     133           0 :             break;
     134           0 :     }
     135             : 
     136             :     // Create grayscale image.
     137           0 :     GDALSimpleSURF::ConvertRGBToLuminosity(poRstRedBand, poRstGreenBand,
     138             :                                            poRstBlueBand, nWidth, nHeight,
     139             :                                            padfImg, nHeight, nWidth);
     140             : 
     141             :     // Prepare integral image.
     142           0 :     GDALIntegralImage *poImg = new GDALIntegralImage();
     143           0 :     poImg->Initialize(const_cast<const double **>(padfImg), nHeight, nWidth);
     144             : 
     145             :     // Get feature points.
     146           0 :     GDALSimpleSURF *poSurf = new GDALSimpleSURF(nOctaveStart, nOctaveEnd);
     147             : 
     148             :     std::vector<GDALFeaturePoint> *poCollection =
     149           0 :         poSurf->ExtractFeaturePoints(poImg, dfThreshold);
     150             : 
     151             :     // Clean up.
     152           0 :     delete poImg;
     153           0 :     delete poSurf;
     154             : 
     155           0 :     for (int i = 0; i < nHeight; ++i)
     156           0 :         delete[] padfImg[i];
     157             : 
     158           0 :     delete[] padfImg;
     159             : 
     160           0 :     return poCollection;
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                     GDALComputeMatchingPoints()                      */
     165             : /************************************************************************/
     166             : 
     167             : /** GDALComputeMatchingPoints. TODO document */
     168           0 : GDAL_GCP CPL_DLL *GDALComputeMatchingPoints(GDALDatasetH hFirstImage,
     169             :                                             GDALDatasetH hSecondImage,
     170             :                                             char **papszOptions,
     171             :                                             int *pnGCPCount)
     172             : {
     173           0 :     *pnGCPCount = 0;
     174             : 
     175             :     /* -------------------------------------------------------------------- */
     176             :     /*      Override default algorithm parameters.                          */
     177             :     /* -------------------------------------------------------------------- */
     178             :     int nOctaveStart, nOctaveEnd;
     179             :     double dfSURFThreshold;
     180             : 
     181             :     nOctaveStart =
     182           0 :         atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_START", "2"));
     183           0 :     nOctaveEnd = atoi(CSLFetchNameValueDef(papszOptions, "OCTAVE_END", "2"));
     184             : 
     185             :     dfSURFThreshold =
     186           0 :         CPLAtof(CSLFetchNameValueDef(papszOptions, "SURF_THRESHOLD", "0.001"));
     187           0 :     const double dfMatchingThreshold = CPLAtof(
     188             :         CSLFetchNameValueDef(papszOptions, "MATCHING_THRESHOLD", "0.015"));
     189             : 
     190             :     /* -------------------------------------------------------------------- */
     191             :     /*      Identify the bands to use.  For now we are effectively          */
     192             :     /*      limited to using RGB input so if we have one band only treat    */
     193             :     /*      it as red=green=blue=band 1.  Disallow non eightbit imagery.    */
     194             :     /* -------------------------------------------------------------------- */
     195           0 :     int anBandMap1[3] = {1, 1, 1};
     196           0 :     if (GDALGetRasterCount(hFirstImage) >= 3)
     197             :     {
     198           0 :         anBandMap1[1] = 2;
     199           0 :         anBandMap1[2] = 3;
     200             :     }
     201             : 
     202           0 :     int anBandMap2[3] = {1, 1, 1};
     203           0 :     if (GDALGetRasterCount(hSecondImage) >= 3)
     204             :     {
     205           0 :         anBandMap2[1] = 2;
     206           0 :         anBandMap2[2] = 3;
     207             :     }
     208             : 
     209             :     /* -------------------------------------------------------------------- */
     210             :     /*      Collect reference points on each image.                         */
     211             :     /* -------------------------------------------------------------------- */
     212             :     std::vector<GDALFeaturePoint> *poFPCollection1 =
     213           0 :         GatherFeaturePoints(GDALDataset::FromHandle(hFirstImage), anBandMap1,
     214             :                             nOctaveStart, nOctaveEnd, dfSURFThreshold);
     215           0 :     if (poFPCollection1 == nullptr)
     216           0 :         return nullptr;
     217             : 
     218             :     std::vector<GDALFeaturePoint> *poFPCollection2 =
     219           0 :         GatherFeaturePoints(GDALDataset::FromHandle(hSecondImage), anBandMap2,
     220             :                             nOctaveStart, nOctaveEnd, dfSURFThreshold);
     221             : 
     222           0 :     if (poFPCollection2 == nullptr)
     223             :     {
     224           0 :         delete poFPCollection1;
     225           0 :         return nullptr;
     226             :     }
     227             : 
     228             :     /* -------------------------------------------------------------------- */
     229             :     /*      Try to find corresponding locations.                            */
     230             :     /* -------------------------------------------------------------------- */
     231           0 :     std::vector<GDALFeaturePoint *> oMatchPairs;
     232             : 
     233           0 :     if (CE_None != GDALSimpleSURF::MatchFeaturePoints(
     234             :                        &oMatchPairs, poFPCollection1, poFPCollection2,
     235             :                        dfMatchingThreshold))
     236             :     {
     237           0 :         delete poFPCollection1;
     238           0 :         delete poFPCollection2;
     239           0 :         return nullptr;
     240             :     }
     241             : 
     242           0 :     *pnGCPCount = static_cast<int>(oMatchPairs.size()) / 2;
     243             : 
     244             :     /* -------------------------------------------------------------------- */
     245             :     /*      Translate these into GCPs - but with the output coordinate      */
     246             :     /*      system being pixel/line on the second image.                    */
     247             :     /* -------------------------------------------------------------------- */
     248             :     GDAL_GCP *pasGCPList =
     249           0 :         static_cast<GDAL_GCP *>(CPLCalloc(*pnGCPCount, sizeof(GDAL_GCP)));
     250             : 
     251           0 :     GDALInitGCPs(*pnGCPCount, pasGCPList);
     252             : 
     253           0 :     for (int i = 0; i < *pnGCPCount; i++)
     254             :     {
     255           0 :         GDALFeaturePoint *poPoint1 = oMatchPairs[i * 2];
     256           0 :         GDALFeaturePoint *poPoint2 = oMatchPairs[i * 2 + 1];
     257             : 
     258           0 :         pasGCPList[i].dfGCPPixel = poPoint1->GetX() + 0.5;
     259           0 :         pasGCPList[i].dfGCPLine = poPoint1->GetY() + 0.5;
     260             : 
     261           0 :         pasGCPList[i].dfGCPX = poPoint2->GetX() + 0.5;
     262           0 :         pasGCPList[i].dfGCPY = poPoint2->GetY() + 0.5;
     263           0 :         pasGCPList[i].dfGCPZ = 0.0;
     264             :     }
     265             : 
     266             :     // Cleanup the feature point lists.
     267           0 :     delete poFPCollection1;
     268           0 :     delete poFPCollection2;
     269             : 
     270             :     /* -------------------------------------------------------------------- */
     271             :     /*      Optionally transform into the georef coordinates of the         */
     272             :     /*      output image.                                                   */
     273             :     /* -------------------------------------------------------------------- */
     274             :     const bool bGeorefOutput =
     275           0 :         CPLTestBool(CSLFetchNameValueDef(papszOptions, "OUTPUT_GEOREF", "NO"));
     276             : 
     277           0 :     if (bGeorefOutput)
     278             :     {
     279           0 :         double adfGeoTransform[6] = {};
     280             : 
     281           0 :         GDALGetGeoTransform(hSecondImage, adfGeoTransform);
     282             : 
     283           0 :         for (int i = 0; i < *pnGCPCount; i++)
     284             :         {
     285           0 :             GDALApplyGeoTransform(adfGeoTransform, pasGCPList[i].dfGCPX,
     286           0 :                                   pasGCPList[i].dfGCPY, &(pasGCPList[i].dfGCPX),
     287           0 :                                   &(pasGCPList[i].dfGCPY));
     288             :         }
     289             :     }
     290             : 
     291           0 :     return pasGCPList;
     292             : }

Generated by: LCOV version 1.14