1 /* 2 By downloading, copying, installing or using the software you agree to this license. 3 If you do not agree to this license, do not download, install, 4 copy or use the software. 5 6 7 License Agreement 8 For Open Source Computer Vision Library 9 (3-clause BSD License) 10 11 Copyright (C) 2000-2016, Intel Corporation, all rights reserved. 12 Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved. 13 Copyright (C) 2009-2016, NVIDIA Corporation, all rights reserved. 14 Copyright (C) 2010-2013, Advanced Micro Devices, Inc., all rights reserved. 15 Copyright (C) 2015-2016, OpenCV Foundation, all rights reserved. 16 Copyright (C) 2015-2016, Itseez Inc., all rights reserved. 17 Third party copyrights are property of their respective owners. 18 19 Redistribution and use in source and binary forms, with or without modification, 20 are permitted provided that the following conditions are met: 21 22 * Redistributions of source code must retain the above copyright notice, 23 this list of conditions and the following disclaimer. 24 25 * Redistributions in binary form must reproduce the above copyright notice, 26 this list of conditions and the following disclaimer in the documentation 27 and/or other materials provided with the distribution. 28 29 * Neither the names of the copyright holders nor the names of the contributors 30 may be used to endorse or promote products derived from this software 31 without specific prior written permission. 32 33 This software is provided by the copyright holders and contributors "as is" and 34 any express or implied warranties, including, but not limited to, the implied 35 warranties of merchantability and fitness for a particular purpose are disclaimed. 36 In no event shall copyright holders or contributors be liable for any direct, 37 indirect, incidental, special, exemplary, or consequential damages 38 (including, but not limited to, procurement of substitute goods or services; 39 loss of use, data, or profits; or business interruption) however caused 40 and on any theory of liability, whether in contract, strict liability, 41 or tort (including negligence or otherwise) arising in any way out of 42 the use of this software, even if advised of the possibility of such damage. 43 */ 44 45 /* 46 Contributed by Gregor Kovalcik <gregor dot kovalcik at gmail dot com> 47 based on code provided by Martin Krulis, Jakub Lokoc and Tomas Skopal. 48 49 References: 50 Martin Krulis, Jakub Lokoc, Tomas Skopal. 51 Efficient Extraction of Clustering-Based Feature Signatures Using GPU Architectures. 52 Multimedia tools and applications, 75(13), pp.: 8071�8103, Springer, ISSN: 1380-7501, 2016 53 54 Christian Beecks, Merih Seran Uysal, Thomas Seidl. 55 Signature quadratic form distance. 56 In Proceedings of the ACM International Conference on Image and Video Retrieval, pages 438-445. 57 ACM, 2010. 58 */ 59 #include "precomp.hpp" 60 61 #include "pct_signatures/constants.hpp" 62 #include "pct_signatures/pct_sampler.hpp" 63 #include "pct_signatures/pct_clusterizer.hpp" 64 65 using namespace cv::xfeatures2d::pct_signatures; 66 67 namespace cv 68 { 69 namespace xfeatures2d 70 { 71 namespace pct_signatures 72 { 73 class PCTSignatures_Impl : public PCTSignatures 74 { 75 public: PCTSignatures_Impl(const std::vector<Point2f> & initSamplingPoints,int initSeedCount)76 PCTSignatures_Impl(const std::vector<Point2f>& initSamplingPoints, int initSeedCount) 77 { 78 if (initSamplingPoints.size() == 0) 79 { 80 CV_Error(Error::StsBadArg, "No sampling points provided!"); 81 } 82 if (initSeedCount <= 0) 83 { 84 CV_Error(Error::StsBadArg, "Not enough initial seeds, at least 1 required."); 85 } 86 87 mSampler = PCTSampler::create(initSamplingPoints); 88 89 initSeedCount = std::min(initSeedCount, (int)initSamplingPoints.size()); 90 std::vector<int> initClusterSeedIndexes = pickRandomClusterSeedIndexes(initSeedCount); 91 mClusterizer = PCTClusterizer::create(initClusterSeedIndexes); 92 } 93 PCTSignatures_Impl(const std::vector<Point2f> & initSamplingPoints,const std::vector<int> & initClusterSeedIndexes)94 PCTSignatures_Impl( 95 const std::vector<Point2f>& initSamplingPoints, 96 const std::vector<int>& initClusterSeedIndexes) 97 { 98 if (initSamplingPoints.size() == 0) 99 { 100 CV_Error(Error::StsBadArg, "No sampling points provided!"); 101 } 102 if (initClusterSeedIndexes.size() == 0) 103 { 104 CV_Error(Error::StsBadArg, "Not enough initial seeds, at least 1 required."); 105 } 106 if (initClusterSeedIndexes.size() > initSamplingPoints.size()) 107 { 108 CV_Error(Error::StsBadArg, "Too much cluster seeds or not enough sampling points."); 109 } 110 for (int iCluster = 0; iCluster < (int)(initClusterSeedIndexes.size()); iCluster++) 111 { 112 if (initClusterSeedIndexes[iCluster] < 0 113 || initClusterSeedIndexes[iCluster] >= (int)(initSamplingPoints.size())) 114 { 115 CV_Error(Error::StsBadArg, 116 "Initial cluster seed indexes contain an index outside the range of the sampling point list."); 117 } 118 } 119 120 mSampler = PCTSampler::create(initSamplingPoints); 121 mClusterizer = PCTClusterizer::create(initClusterSeedIndexes); 122 } 123 124 void computeSignature(InputArray image, OutputArray signature) const CV_OVERRIDE; 125 126 void computeSignatures(const std::vector<Mat>& images, std::vector<Mat>& signatures) const CV_OVERRIDE; 127 128 void getGrayscaleBitmap(OutputArray _grayscaleBitmap, bool normalize) const; 129 130 /**** sampler ****/ 131 getSampleCount() const132 int getSampleCount() const CV_OVERRIDE { return mSampler->getGrayscaleBits(); } getGrayscaleBits() const133 int getGrayscaleBits() const CV_OVERRIDE { return mSampler->getGrayscaleBits(); } getWindowRadius() const134 int getWindowRadius() const CV_OVERRIDE { return mSampler->getWindowRadius(); } getWeightX() const135 float getWeightX() const CV_OVERRIDE { return mSampler->getWeightX(); } getWeightY() const136 float getWeightY() const CV_OVERRIDE { return mSampler->getWeightY(); } getWeightL() const137 float getWeightL() const CV_OVERRIDE { return mSampler->getWeightL(); } getWeightA() const138 float getWeightA() const CV_OVERRIDE { return mSampler->getWeightA(); } getWeightB() const139 float getWeightB() const CV_OVERRIDE { return mSampler->getWeightB(); } getWeightContrast() const140 float getWeightContrast() const CV_OVERRIDE { return mSampler->getWeightContrast(); } getWeightEntropy() const141 float getWeightEntropy() const CV_OVERRIDE { return mSampler->getWeightEntropy(); } 142 getSamplingPoints() const143 std::vector<Point2f> getSamplingPoints() const CV_OVERRIDE { return mSampler->getSamplingPoints(); } 144 145 setGrayscaleBits(int grayscaleBits)146 void setGrayscaleBits(int grayscaleBits) CV_OVERRIDE { mSampler->setGrayscaleBits(grayscaleBits); } setWindowRadius(int windowRadius)147 void setWindowRadius(int windowRadius) CV_OVERRIDE { mSampler->setWindowRadius(windowRadius); } setWeightX(float weight)148 void setWeightX(float weight) CV_OVERRIDE { mSampler->setWeightX(weight); } setWeightY(float weight)149 void setWeightY(float weight) CV_OVERRIDE { mSampler->setWeightY(weight); } setWeightL(float weight)150 void setWeightL(float weight) CV_OVERRIDE { mSampler->setWeightL(weight); } setWeightA(float weight)151 void setWeightA(float weight) CV_OVERRIDE { mSampler->setWeightA(weight); } setWeightB(float weight)152 void setWeightB(float weight) CV_OVERRIDE { mSampler->setWeightB(weight); } setWeightContrast(float weight)153 void setWeightContrast(float weight) CV_OVERRIDE { mSampler->setWeightContrast(weight); } setWeightEntropy(float weight)154 void setWeightEntropy(float weight) CV_OVERRIDE { mSampler->setWeightEntropy(weight); } 155 setWeight(int idx,float value)156 void setWeight(int idx, float value) CV_OVERRIDE { mSampler->setWeight(idx, value); } setWeights(const std::vector<float> & weights)157 void setWeights(const std::vector<float>& weights) CV_OVERRIDE { mSampler->setWeights(weights); } setTranslation(int idx,float value)158 void setTranslation(int idx, float value) CV_OVERRIDE { mSampler->setTranslation(idx, value); } setTranslations(const std::vector<float> & translations)159 void setTranslations(const std::vector<float>& translations) CV_OVERRIDE { mSampler->setTranslations(translations); } 160 setSamplingPoints(std::vector<Point2f> samplingPoints)161 void setSamplingPoints(std::vector<Point2f> samplingPoints) CV_OVERRIDE { mSampler->setSamplingPoints(samplingPoints); } 162 163 164 /**** clusterizer ****/ 165 getIterationCount() const166 int getIterationCount() const CV_OVERRIDE { return mClusterizer->getIterationCount(); } getInitSeedIndexes() const167 std::vector<int> getInitSeedIndexes() const CV_OVERRIDE { return mClusterizer->getInitSeedIndexes(); } getInitSeedCount() const168 int getInitSeedCount() const CV_OVERRIDE { return (int)mClusterizer->getInitSeedIndexes().size(); } getMaxClustersCount() const169 int getMaxClustersCount() const CV_OVERRIDE { return mClusterizer->getMaxClustersCount(); } getClusterMinSize() const170 int getClusterMinSize() const CV_OVERRIDE { return mClusterizer->getClusterMinSize(); } getJoiningDistance() const171 float getJoiningDistance() const CV_OVERRIDE { return mClusterizer->getJoiningDistance(); } getDropThreshold() const172 float getDropThreshold() const CV_OVERRIDE { return mClusterizer->getDropThreshold(); } getDistanceFunction() const173 int getDistanceFunction() const CV_OVERRIDE 174 { return mClusterizer->getDistanceFunction(); } 175 setIterationCount(int iterations)176 void setIterationCount(int iterations) CV_OVERRIDE { mClusterizer->setIterationCount(iterations); } setInitSeedIndexes(std::vector<int> initSeedIndexes)177 void setInitSeedIndexes(std::vector<int> initSeedIndexes) CV_OVERRIDE 178 { mClusterizer->setInitSeedIndexes(initSeedIndexes); } setMaxClustersCount(int maxClusters)179 void setMaxClustersCount(int maxClusters) CV_OVERRIDE { mClusterizer->setMaxClustersCount(maxClusters); } setClusterMinSize(int clusterMinSize)180 void setClusterMinSize(int clusterMinSize) CV_OVERRIDE { mClusterizer->setClusterMinSize(clusterMinSize); } setJoiningDistance(float joiningDistance)181 void setJoiningDistance(float joiningDistance) CV_OVERRIDE { mClusterizer->setJoiningDistance(joiningDistance); } setDropThreshold(float dropThreshold)182 void setDropThreshold(float dropThreshold) CV_OVERRIDE { mClusterizer->setDropThreshold(dropThreshold); } setDistanceFunction(int distanceFunction)183 void setDistanceFunction(int distanceFunction) CV_OVERRIDE 184 { mClusterizer->setDistanceFunction(distanceFunction); } 185 186 private: 187 /** 188 * @brief Samples used for sampling the input image and producing list of samples. 189 */ 190 Ptr<PCTSampler> mSampler; 191 192 /** 193 * @brief Clusterizer using k-means algorithm to produce list of centroids from sampled points - the image signature. 194 */ 195 Ptr<PCTClusterizer> mClusterizer; 196 197 /** 198 * @brief Creates vector of random indexes of sampling points 199 * which will be used as initial centroids for k-means clusterization. 200 * @param initSeedCount Number of indexes of initial centroids to be produced. 201 * @return The generated vector of random indexes. 202 */ pickRandomClusterSeedIndexes(int initSeedCount)203 static std::vector<int> pickRandomClusterSeedIndexes(int initSeedCount) 204 { 205 std::vector<int> seedIndexes; 206 for (int iSeed = 0; iSeed < initSeedCount; iSeed++) 207 { 208 seedIndexes.push_back(iSeed); 209 } 210 211 randShuffle(seedIndexes); 212 return seedIndexes; 213 } 214 }; 215 216 217 /** 218 * @brief Class implementing parallel computing of signatures for multiple images. 219 */ 220 class Parallel_computeSignatures : public ParallelLoopBody 221 { 222 private: 223 const PCTSignatures* mPctSignaturesAlgorithm; 224 const std::vector<Mat>* mImages; 225 std::vector<Mat>* mSignatures; 226 227 public: Parallel_computeSignatures(const PCTSignatures * pctSignaturesAlgorithm,const std::vector<Mat> * images,std::vector<Mat> * signatures)228 Parallel_computeSignatures( 229 const PCTSignatures* pctSignaturesAlgorithm, 230 const std::vector<Mat>* images, 231 std::vector<Mat>* signatures) 232 : mPctSignaturesAlgorithm(pctSignaturesAlgorithm), 233 mImages(images), 234 mSignatures(signatures) 235 { 236 mSignatures->resize(images->size()); 237 } 238 operator ()(const Range & range) const239 void operator()(const Range& range) const CV_OVERRIDE 240 { 241 for (int i = range.start; i < range.end; i++) 242 { 243 mPctSignaturesAlgorithm->computeSignature((*mImages)[i], (*mSignatures)[i]); 244 } 245 } 246 }; 247 248 249 /** 250 * @brief Computes signature for one image. 251 */ computeSignature(InputArray _image,OutputArray _signature) const252 void PCTSignatures_Impl::computeSignature(InputArray _image, OutputArray _signature) const 253 { 254 if (_image.empty()) 255 { 256 _signature.create(_image.size(), CV_32FC1); 257 return; 258 } 259 260 Mat image = _image.getMat(); 261 CV_Assert(image.depth() == CV_8U); 262 263 // TODO: OpenCL 264 //if (ocl::useOpenCL()) 265 //{ 266 //} 267 268 // sample features 269 Mat samples; 270 mSampler->sample(image, samples); // HOT PATH: 40% 271 272 // kmeans clusterize, use feature samples, produce signature clusters 273 Mat signature; 274 mClusterizer->clusterize(samples, signature); // HOT PATH: 60% 275 276 277 // set result 278 _signature.create(signature.size(), signature.type()); 279 Mat result = _signature.getMat(); 280 signature.copyTo(result); 281 } 282 283 /** 284 * @brief Parallel function computing signatures for multiple images. 285 */ computeSignatures(const std::vector<Mat> & images,std::vector<Mat> & signatures) const286 void PCTSignatures_Impl::computeSignatures(const std::vector<Mat>& images, std::vector<Mat>& signatures) const 287 { 288 parallel_for_(Range(0, (int)images.size()), Parallel_computeSignatures(this, &images, &signatures)); 289 } 290 291 } // end of namespace pct_signatures 292 293 294 create(const int initSampleCount,const int initSeedCount,const int pointDistribution)295 Ptr<PCTSignatures> PCTSignatures::create( 296 const int initSampleCount, 297 const int initSeedCount, 298 const int pointDistribution) 299 { 300 std::vector<Point2f> initPoints; 301 generateInitPoints(initPoints, initSampleCount, pointDistribution); 302 return create(initPoints, initSeedCount); 303 } 304 create(const std::vector<Point2f> & initPoints,const int initSeedCount)305 Ptr<PCTSignatures> PCTSignatures::create( 306 const std::vector<Point2f>& initPoints, 307 const int initSeedCount) 308 { 309 return makePtr<PCTSignatures_Impl>(initPoints, initSeedCount); 310 } 311 create(const std::vector<Point2f> & initPoints,const std::vector<int> & initClusterSeedIndexes)312 Ptr<PCTSignatures> PCTSignatures::create( 313 const std::vector<Point2f>& initPoints, 314 const std::vector<int>& initClusterSeedIndexes) 315 { 316 return makePtr<PCTSignatures_Impl>(initPoints, initClusterSeedIndexes); 317 } 318 319 drawSignature(InputArray _source,InputArray _signature,OutputArray _result,float radiusToShorterSideRatio,int borderThickness)320 void PCTSignatures::drawSignature( 321 InputArray _source, 322 InputArray _signature, 323 OutputArray _result, 324 float radiusToShorterSideRatio, 325 int borderThickness) 326 { 327 // check source 328 if (_source.empty()) 329 { 330 return; 331 } 332 Mat source = _source.getMat(); 333 334 // create result 335 _result.create(source.size(), source.type()); 336 Mat result = _result.getMat(); 337 source.copyTo(result); 338 339 // check signature 340 if (_signature.empty()) 341 { 342 return; 343 } 344 Mat signature = _signature.getMat(); 345 if (signature.type() != CV_32F || signature.cols != SIGNATURE_DIMENSION) 346 { 347 CV_Error_(Error::StsBadArg, ("Invalid signature format. Type must be CV_32F and signature.cols must be %d.", SIGNATURE_DIMENSION)); 348 } 349 350 // compute max radius using given ratio of shorter image side 351 float maxRadius = ((source.rows < source.cols) ? source.rows : source.cols) * radiusToShorterSideRatio; 352 353 // draw signature 354 for (int i = 0; i < signature.rows; i++) 355 { 356 Vec3f labColor( 357 signature.at<float>(i, L_IDX) * L_COLOR_RANGE, // convert Lab pixel to BGR 358 signature.at<float>(i, A_IDX) * A_COLOR_RANGE, 359 signature.at<float>(i, B_IDX) * B_COLOR_RANGE); 360 Mat labPixel(1, 1, CV_32FC3); 361 labPixel.at<Vec3f>(0, 0) = labColor; 362 Mat rgbPixel; 363 cvtColor(labPixel, rgbPixel, COLOR_Lab2BGR); 364 rgbPixel.convertTo(rgbPixel, CV_8UC3, 255); 365 Vec3b rgbColor = rgbPixel.at<Vec3b>(0, 0); 366 367 // precompute variables 368 Point center((int)(signature.at<float>(i, X_IDX) * source.cols), (int)(signature.at<float>(i, Y_IDX) * source.rows)); 369 int radius = (int)(maxRadius * signature.at<float>(i, WEIGHT_IDX)); 370 Vec3b borderColor(0, 0, 0); 371 372 // draw filled circle 373 circle(result, center, radius, rgbColor, -1); 374 // draw circle outline 375 circle(result, center, radius, borderColor, borderThickness); 376 } 377 } 378 379 generateInitPoints(std::vector<Point2f> & initPoints,const int count,const int pointDistribution)380 void PCTSignatures::generateInitPoints( 381 std::vector<Point2f>& initPoints, 382 const int count, 383 const int pointDistribution) 384 { 385 RNG random; 386 random.state = getTickCount(); 387 initPoints.resize(count); 388 389 switch (pointDistribution) 390 { 391 case UNIFORM: 392 for (int i = 0; i < count; i++) 393 { 394 // returns uniformly distributed float random number from [0, 1) range 395 initPoints[i] = (Point2f(random.uniform((float)0.0, (float)1.0), random.uniform((float)0.0, (float)1.0))); 396 } 397 break; 398 case REGULAR: 399 { 400 int gridSize = (int)ceil(sqrt((float)count)); 401 const float step = 1.0f / gridSize; 402 const float halfStep = step / 2; 403 float x = halfStep; 404 float y = halfStep; 405 for (int i = 0; i < count; i++) 406 { 407 // returns regular grid 408 initPoints[i] = Point2f(x, y); 409 if ((i + 1) % gridSize == 0) 410 { 411 x = halfStep; 412 y += step; 413 } 414 else 415 { 416 x += step; 417 } 418 } 419 break; 420 } 421 case NORMAL: 422 for (int i = 0; i < count; i++) 423 { 424 // returns normally distributed float random number from (0, 1) range with mean 0.5 425 float sigma = 0.2f; 426 float x = (float)random.gaussian(sigma); 427 float y = (float)random.gaussian(sigma); 428 while (x <= -0.5f || x >= 0.5f) 429 x = (float)random.gaussian(sigma); 430 while (y <= -0.5f || y >= 0.5f) 431 y = (float)random.gaussian(sigma); 432 initPoints[i] = Point2f(x, y) + Point2f(0.5, 0.5); 433 } 434 break; 435 default: 436 CV_Error(Error::StsNotImplemented, "Generation of this init point distribution is not implemented!"); 437 break; 438 } 439 } 440 441 442 } // end of namespace xfeatures2d 443 } // end of namespace cv 444