1 // This file is part of OpenCV project.
2 // It is subject to the license terms in the LICENSE file found in the top-level directory
3 // of this distribution and at http://opencv.org/license.html.
4 //
5 // This file is based on code issued with the following license.
6 /*********************************************************************
7 * Software License Agreement (BSD License)
8 *
9 *  Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
10 *  Copyright (C) 2008-2013, Willow Garage Inc., all rights reserved.
11 *  Copyright (C) 2013, Evgeny Toropov, all rights reserved.
12 *  Third party copyrights are property of their respective owners.
13 *
14 *  Redistribution and use in source and binary forms, with or without
15 *  modification, are permitted provided that the following conditions
16 *  are met:
17 *
18 *   * Redistributions of source code must retain the above copyright
19 *     notice, this list of conditions and the following disclaimer.
20 *   * Redistributions in binary form must reproduce the above
21 *     copyright notice, this list of conditions and the following
22 *     disclaimer in the documentation and/or other materials provided
23 *     with the distribution.
24 *   * The name of the copyright holders may not be used to endorse
25 *     or promote products derived from this software without specific
26 *     prior written permission.
27 *
28 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
29 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
31 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
32 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
33 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
34 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
35 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
36 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
38 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
39 *  POSSIBILITY OF SUCH DAMAGE.
40 *********************************************************************/
41 
42 /*
43  Guoshen Yu, Jean-Michel Morel, ASIFT: An Algorithm for Fully Affine
44  Invariant Comparison,  Image Processing On Line, 1 (2011), pp. 11–38.
45  https://doi.org/10.5201/ipol.2011.my-asift
46  */
47 
48 #include "precomp.hpp"
49 #include <iostream>
50 namespace cv {
51 
52 class AffineFeature_Impl CV_FINAL : public AffineFeature
53 {
54 public:
55     explicit AffineFeature_Impl(const Ptr<Feature2D>& backend,
56             int maxTilt, int minTilt, float tiltStep, float rotateStepBase);
57 
descriptorSize() const58     int descriptorSize() const CV_OVERRIDE
59     {
60         return backend_->descriptorSize();
61     }
62 
descriptorType() const63     int descriptorType() const CV_OVERRIDE
64     {
65         return backend_->descriptorType();
66     }
67 
defaultNorm() const68     int defaultNorm() const CV_OVERRIDE
69     {
70         return backend_->defaultNorm();
71     }
72 
73     void detectAndCompute(InputArray image, InputArray mask, std::vector<KeyPoint>& keypoints,
74             OutputArray descriptors, bool useProvidedKeypoints=false) CV_OVERRIDE;
75 
76     void setViewParams(const std::vector<float>& tilts, const std::vector<float>& rolls) CV_OVERRIDE;
77     void getViewParams(std::vector<float>& tilts, std::vector<float>& rolls) const CV_OVERRIDE;
78 
79 protected:
80     void splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
81             std::vector< std::vector<KeyPoint> >& keypointsByView) const;
82 
83     const Ptr<Feature2D> backend_;
84     int maxTilt_;
85     int minTilt_;
86     float tiltStep_;
87     float rotateStepBase_;
88 
89     // Tilt factors.
90     std::vector<float> tilts_;
91     // Roll factors.
92     std::vector<float> rolls_;
93 
94 private:
95     AffineFeature_Impl(const AffineFeature_Impl &); // copy disabled
96     AffineFeature_Impl& operator=(const AffineFeature_Impl &); // assign disabled
97 };
98 
AffineFeature_Impl(const Ptr<FeatureDetector> & backend,int maxTilt,int minTilt,float tiltStep,float rotateStepBase)99 AffineFeature_Impl::AffineFeature_Impl(const Ptr<FeatureDetector>& backend,
100         int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
101     : backend_(backend), maxTilt_(maxTilt), minTilt_(minTilt), tiltStep_(tiltStep), rotateStepBase_(rotateStepBase)
102 {
103     int i = minTilt_;
104     if( i == 0 )
105     {
106         tilts_.push_back(1);
107         rolls_.push_back(0);
108         i++;
109     }
110     float tilt = 1;
111     for( ; i <= maxTilt_; i++ )
112     {
113         tilt *= tiltStep_;
114         float rotateStep = rotateStepBase_ / tilt;
115         int rollN = cvFloor(180.0f / rotateStep);
116         if( rollN * rotateStep == 180.0f )
117             rollN--;
118         for( int j = 0; j <= rollN; j++ )
119         {
120             tilts_.push_back(tilt);
121             rolls_.push_back(rotateStep * j);
122         }
123     }
124 }
125 
setViewParams(const std::vector<float> & tilts,const std::vector<float> & rolls)126 void AffineFeature_Impl::setViewParams(const std::vector<float>& tilts,
127         const std::vector<float>& rolls)
128 {
129     CV_Assert(tilts.size() == rolls.size());
130     tilts_ = tilts;
131     rolls_ = rolls;
132 }
133 
getViewParams(std::vector<float> & tilts,std::vector<float> & rolls) const134 void AffineFeature_Impl::getViewParams(std::vector<float>& tilts,
135         std::vector<float>& rolls) const
136 {
137     tilts = tilts_;
138     rolls = rolls_;
139 }
140 
splitKeypointsByView(const std::vector<KeyPoint> & keypoints_,std::vector<std::vector<KeyPoint>> & keypointsByView) const141 void AffineFeature_Impl::splitKeypointsByView(const std::vector<KeyPoint>& keypoints_,
142         std::vector< std::vector<KeyPoint> >& keypointsByView) const
143 {
144     for( size_t i = 0; i < keypoints_.size(); i++ )
145     {
146         const KeyPoint& kp = keypoints_[i];
147         CV_Assert( kp.class_id >= 0 && kp.class_id < (int)tilts_.size() );
148         keypointsByView[kp.class_id].push_back(kp);
149     }
150 }
151 
152 class skewedDetectAndCompute : public ParallelLoopBody
153 {
154 public:
skewedDetectAndCompute(const std::vector<float> & _tilts,const std::vector<float> & _rolls,std::vector<std::vector<KeyPoint>> & _keypointsCollection,std::vector<Mat> & _descriptorCollection,const Mat & _image,const Mat & _mask,const bool _do_keypoints,const bool _do_descriptors,const Ptr<Feature2D> & _backend)155     skewedDetectAndCompute(
156         const std::vector<float>& _tilts,
157         const std::vector<float>& _rolls,
158         std::vector< std::vector<KeyPoint> >& _keypointsCollection,
159         std::vector<Mat>& _descriptorCollection,
160         const Mat& _image,
161         const Mat& _mask,
162         const bool _do_keypoints,
163         const bool _do_descriptors,
164         const Ptr<Feature2D>& _backend)
165         : tilts(_tilts),
166           rolls(_rolls),
167           keypointsCollection(_keypointsCollection),
168           descriptorCollection(_descriptorCollection),
169           image(_image),
170           mask(_mask),
171           do_keypoints(_do_keypoints),
172           do_descriptors(_do_descriptors),
173           backend(_backend) {}
174 
operator ()(const cv::Range & range) const175     void operator()( const cv::Range& range ) const CV_OVERRIDE
176     {
177         CV_TRACE_FUNCTION();
178 
179         const int begin = range.start;
180         const int end = range.end;
181 
182         for( int a = begin; a < end; a++ )
183         {
184             Mat warpedImage, warpedMask;
185             Matx23f pose, invPose;
186             affineSkew(tilts[a], rolls[a], warpedImage, warpedMask, pose);
187             invertAffineTransform(pose, invPose);
188 
189             std::vector<KeyPoint> wKeypoints;
190             Mat wDescriptors;
191             if( !do_keypoints )
192             {
193                 const std::vector<KeyPoint>& keypointsInView = keypointsCollection[a];
194                 if( keypointsInView.size() == 0 ) // when there are no keypoints in this affine view
195                     continue;
196 
197                 std::vector<Point2f> pts_, pts;
198                 KeyPoint::convert(keypointsInView, pts_);
199                 transform(pts_, pts, pose);
200                 wKeypoints.resize(keypointsInView.size());
201                 for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
202                 {
203                     wKeypoints[wi] = keypointsInView[wi];
204                     wKeypoints[wi].pt = pts[wi];
205                 }
206             }
207             backend->detectAndCompute(warpedImage, warpedMask, wKeypoints, wDescriptors, !do_keypoints);
208             if( do_keypoints )
209             {
210                 // KeyPointsFilter::runByPixelsMask( wKeypoints, warpedMask );
211                 if( wKeypoints.size() == 0 )
212                 {
213                     keypointsCollection[a].clear();
214                     continue;
215                 }
216                 std::vector<Point2f> pts_, pts;
217                 KeyPoint::convert(wKeypoints, pts_);
218                 transform(pts_, pts, invPose);
219 
220                 keypointsCollection[a].resize(wKeypoints.size());
221                 for( size_t wi = 0; wi < wKeypoints.size(); wi++ )
222                 {
223                     keypointsCollection[a][wi] = wKeypoints[wi];
224                     keypointsCollection[a][wi].pt = pts[wi];
225                     keypointsCollection[a][wi].class_id = a;
226                 }
227             }
228             if( do_descriptors )
229                 wDescriptors.copyTo(descriptorCollection[a]);
230         }
231     }
232 private:
affineSkew(float tilt,float phi,Mat & warpedImage,Mat & warpedMask,Matx23f & pose) const233     void affineSkew(float tilt, float phi,
234             Mat& warpedImage, Mat& warpedMask, Matx23f& pose) const
235     {
236         int h = image.size().height;
237         int w = image.size().width;
238         Mat rotImage;
239 
240         Mat mask0;
241         if( mask.empty() )
242             mask0 = Mat(h, w, CV_8UC1, 255);
243         else
244             mask0 = mask;
245         pose = Matx23f(1,0,0,
246                     0,1,0);
247 
248         if( phi == 0 )
249             image.copyTo(rotImage);
250         else
251         {
252             phi = phi * (float)CV_PI / 180;
253             float s = std::sin(phi);
254             float c = std::cos(phi);
255             Matx22f A(c, -s, s, c);
256             Matx<float, 4, 2> corners(0, 0, (float)w, 0, (float)w,(float)h, 0, (float)h);
257             Mat tf(corners * A.t());
258             Mat tcorners;
259             tf.convertTo(tcorners, CV_32S);
260             Rect rect = boundingRect(tcorners);
261             h = rect.height; w = rect.width;
262             pose = Matx23f(c, -s, -(float)rect.x,
263                         s,  c, -(float)rect.y);
264             warpAffine(image, rotImage, pose, Size(w, h), INTER_LINEAR, BORDER_REPLICATE);
265         }
266         if( tilt == 1 )
267             warpedImage = rotImage;
268         else
269         {
270             float s = 0.8f * sqrt(tilt * tilt - 1);
271             GaussianBlur(rotImage, rotImage, Size(0, 0), s, 0.01);
272             resize(rotImage, warpedImage, Size(0, 0), 1.0/tilt, 1.0, INTER_NEAREST);
273             pose(0, 0) /= tilt;
274             pose(0, 1) /= tilt;
275             pose(0, 2) /= tilt;
276         }
277         if( phi != 0 || tilt != 1 )
278             warpAffine(mask0, warpedMask, pose, warpedImage.size(), INTER_NEAREST);
279     }
280 
281 
282     const std::vector<float>& tilts;
283     const std::vector<float>& rolls;
284     std::vector< std::vector<KeyPoint> >& keypointsCollection;
285     std::vector<Mat>& descriptorCollection;
286     const Mat& image;
287     const Mat& mask;
288     const bool do_keypoints;
289     const bool do_descriptors;
290     const Ptr<Feature2D>& backend;
291 };
292 
detectAndCompute(InputArray _image,InputArray _mask,std::vector<KeyPoint> & keypoints,OutputArray _descriptors,bool useProvidedKeypoints)293 void AffineFeature_Impl::detectAndCompute(InputArray _image, InputArray _mask,
294         std::vector<KeyPoint>& keypoints,
295         OutputArray _descriptors,
296         bool useProvidedKeypoints)
297 {
298     CV_TRACE_FUNCTION();
299 
300     bool do_keypoints = !useProvidedKeypoints;
301     bool do_descriptors = _descriptors.needed();
302     Mat image = _image.getMat(), mask = _mask.getMat();
303     Mat descriptors;
304 
305     if( (!do_keypoints && !do_descriptors) || _image.empty() )
306         return;
307 
308     std::vector< std::vector<KeyPoint> > keypointsCollection(tilts_.size());
309     std::vector< Mat > descriptorCollection(tilts_.size());
310 
311     if( do_keypoints )
312         keypoints.clear();
313     else
314         splitKeypointsByView(keypoints, keypointsCollection);
315 
316     parallel_for_(Range(0, (int)tilts_.size()), skewedDetectAndCompute(tilts_, rolls_, keypointsCollection, descriptorCollection,
317         image, mask, do_keypoints, do_descriptors, backend_));
318 
319     if( do_keypoints )
320         for( size_t i = 0; i < keypointsCollection.size(); i++ )
321         {
322             const std::vector<KeyPoint>& keys = keypointsCollection[i];
323             keypoints.insert(keypoints.end(), keys.begin(), keys.end());
324         }
325 
326     if( do_descriptors )
327     {
328         _descriptors.create((int)keypoints.size(), backend_->descriptorSize(), backend_->descriptorType());
329         descriptors = _descriptors.getMat();
330         int iter = 0;
331         for( size_t i = 0; i < descriptorCollection.size(); i++ )
332         {
333             const Mat& descs = descriptorCollection[i];
334             if( descs.empty() )
335                 continue;
336             Mat roi(descriptors, Rect(0, iter, descriptors.cols, descs.rows));
337             descs.copyTo(roi);
338             iter += descs.rows;
339         }
340     }
341 }
342 
343 
create(const Ptr<Feature2D> & backend,int maxTilt,int minTilt,float tiltStep,float rotateStepBase)344 Ptr<AffineFeature> AffineFeature::create(const Ptr<Feature2D>& backend,
345                                          int maxTilt, int minTilt, float tiltStep, float rotateStepBase)
346 {
347     CV_Assert(minTilt < maxTilt);
348     CV_Assert(tiltStep > 0);
349     CV_Assert(rotateStepBase > 0);
350     return makePtr<AffineFeature_Impl>(backend, maxTilt, minTilt, tiltStep, rotateStepBase);
351 }
352 
getDefaultName() const353 String AffineFeature::getDefaultName() const
354 {
355     return (Feature2D::getDefaultName() + ".AffineFeature");
356 }
357 
358 } // namespace
359