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