PageRenderTime 85ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 1ms

/src/plugins/img_websearch/ocvsurf.cpp

https://github.com/sileht/debbot-seeks
C++ | 308 lines | 182 code | 24 blank | 102 comment | 27 complexity | 67843ce61484a115ddacdebdcd9c28e7 MD5 | raw file
  1. /**
  2. * The Seeks proxy and plugin framework are part of the SEEKS project.
  3. * Copyright (C) 2010 Emmanuel Benazera, juban@free.fr
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. * Portions of the code below appear in the sample code from OpenCV with
  19. * the following comment:
  20. * A Demo to OpenCV Implementation of SURF
  21. * Further Information Refer to "SURF: Speed-Up Robust Feature"
  22. * Author: Liu Liu
  23. * liuliu.1987+opencv@gmail.com
  24. *
  25. * The bruteMatch function was initially written by Jostein Austvik Jacobsen.
  26. */
  27. #include "ocvsurf.h"
  28. #include "miscutil.h"
  29. #include "errlog.h"
  30. #include <cxflann.h>
  31. #include <iostream>
  32. #include <fstream>
  33. #include <unistd.h>
  34. using sp::miscutil;
  35. using sp::errlog;
  36. namespace seeks_plugins
  37. {
  38. #define EXTENDED_DESCRIPTOR 1
  39. CvSURFParams ocvsurf::_surf_params = cvSURFParams(600,EXTENDED_DESCRIPTOR);
  40. void ocvsurf::generate_surf_features(const std::string *img_content,
  41. CvSeq *&objectKeypoints,
  42. CvSeq *&objectDescriptors,
  43. const img_search_snippet *sg)
  44. {
  45. // XXX: Ridiculous! OpenCV doesn't support loading an image from memory,
  46. // all hacks tried where too complicated, and deep into OpenCV. Sad.
  47. // Now we need to write every image on disk and reload it, which
  48. // is totally stupid.
  49. static std::string tmp_rep = "/tmp/"; //TODO: in configuration.
  50. // write to file.
  51. std::string tfname = tmp_rep + miscutil::to_string(sg->_id);
  52. std::ofstream ofile(tfname.c_str(),std::ofstream::binary);
  53. ofile.write(img_content->c_str(),img_content->length());
  54. IplImage *img = cvLoadImage(tfname.c_str(),CV_LOAD_IMAGE_GRAYSCALE);
  55. if (!img) // failed loading image, usually from a broken transfer earlier...
  56. {
  57. unlink(tfname.c_str());
  58. return;
  59. }
  60. // remove file.
  61. unlink(tfname.c_str());
  62. // extract SURF features.
  63. try
  64. {
  65. cvExtractSURF(img, 0, &objectKeypoints, &objectDescriptors,
  66. sg->_surf_storage, ocvsurf::_surf_params);
  67. }
  68. catch (cv::Exception &e)
  69. {
  70. errlog::log_error(LOG_LEVEL_ERROR,"Error extracting features from image loaded from %s: %s", tfname.c_str(),
  71. e.err.c_str());
  72. cvReleaseImage(&img);
  73. return; // failure.
  74. }
  75. //debug
  76. /* std::cerr << "extracted features for img " << sg->_cached << std::endl;
  77. std::cerr << "keypoints: " << objectKeypoints->total << " -- descriptors: " << objectDescriptors->total << std::endl; */
  78. //debug
  79. cvReleaseImage(&img);
  80. }
  81. /* void ocvsurf::flannFindPairs(CvSeq *o1desc,
  82. CvSeq *o2desc,
  83. std::vector<surf_pair> &ptpairs)
  84. {
  85. int length = (int)(o1desc->elem_size/sizeof(float));
  86. cv::Mat m_object(o1desc->total, length, CV_32F);
  87. cv::Mat m_image(o2desc->total, length, CV_32F);
  88. // copy descriptors
  89. CvSeqReader obj_reader;
  90. float* obj_ptr = m_object.ptr<float>(0);
  91. cvStartReadSeq( o1desc, &obj_reader );
  92. for(int i = 0; i < o1desc->total; i++ )
  93. {
  94. const float* descriptor = (const float*)obj_reader.ptr;
  95. CV_NEXT_SEQ_ELEM( obj_reader.seq->elem_size, obj_reader );
  96. memcpy(obj_ptr, descriptor, length*sizeof(float));
  97. obj_ptr += length;
  98. }
  99. CvSeqReader img_reader;
  100. float* img_ptr = m_image.ptr<float>(0);
  101. cvStartReadSeq( o2desc, &img_reader );
  102. for(int i = 0; i < o2desc->total; i++ )
  103. {
  104. const float* descriptor = (const float*)img_reader.ptr;
  105. CV_NEXT_SEQ_ELEM( img_reader.seq->elem_size, img_reader );
  106. memcpy(img_ptr, descriptor, length*sizeof(float));
  107. img_ptr += length;
  108. }
  109. // find nearest neighbors using FLANN
  110. cv::Mat m_indices(o1desc->total, 2, CV_32S);
  111. cv::Mat m_dists(o1desc->total, 2, CV_32F);
  112. cv::flann::Index flann_index(m_image, cv::flann::KDTreeIndexParams(8)); // using 8 randomized kdtrees
  113. flann_index.knnSearch(m_object, m_indices, m_dists, 2, cv::flann::SearchParams(64) ); // 64 is maximum number of leaves being checked
  114. int* indices_ptr = m_indices.ptr<int>(0);
  115. float* dists_ptr = m_dists.ptr<float>(0);
  116. for (int i=0;i<m_indices.rows;++i)
  117. { */
  118. /* if (dists_ptr[2*i]<0.6*dists_ptr[2*i+1])
  119. {
  120. ptpairs.push_back(i);
  121. ptpairs.push_back(indices_ptr[2*i]);
  122. } */
  123. /* ptpairs.push_back(surf_pair(i,indices_ptr[2*i],dists_ptr[2*i]/static_cast<double>(dists_ptr[2*i+1])));
  124. }
  125. //debug
  126. //std::cout << "ptpairs size: " << ptpairs.size() << std::endl;
  127. //debug
  128. } */
  129. #define CORRELATION_THRESHOLD 0.9
  130. // brute-force attempt at correlating the two sets of features
  131. int ocvsurf::bruteMatch(CvMat *&points1, CvMat *&points2,
  132. CvSeq *kp1, CvSeq *desc1, CvSeq *kp2, CvSeq *desc2,
  133. const bool &filter)
  134. {
  135. int i,j,k;
  136. double* avg1 = (double*)malloc(sizeof(double)*kp1->total);
  137. double* avg2 = (double*)malloc(sizeof(double)*kp2->total);
  138. double* dev1 = (double*)malloc(sizeof(double)*kp1->total);
  139. double* dev2 = (double*)malloc(sizeof(double)*kp2->total);
  140. int* best1 = (int*)malloc(sizeof(int)*kp1->total);
  141. int* best2 = (int*)malloc(sizeof(int)*kp2->total);
  142. double* best1corr = (double*)malloc(sizeof(double)*kp1->total);
  143. double* best2corr = (double*)malloc(sizeof(double)*kp2->total);
  144. float *seq1, *seq2;
  145. int descriptor_size = EXTENDED_DESCRIPTOR ? 128 : 64;
  146. for (i=0; i<kp1->total; i++)
  147. {
  148. // find average and standard deviation of each descriptor
  149. avg1[i] = 0;
  150. dev1[i] = 0;
  151. seq1 = (float*)cvGetSeqElem(desc1, i);
  152. for (k=0; k<descriptor_size; k++) avg1[i] += seq1[k];
  153. avg1[i] /= descriptor_size;
  154. for (k=0; k<descriptor_size; k++) dev1[i] +=
  155. (seq1[k]-avg1[i])*(seq1[k]-avg1[i]);
  156. dev1[i] = sqrt(dev1[i]/descriptor_size);
  157. // initialize best1 and best1corr
  158. best1[i] = -1;
  159. best1corr[i] = -1.;
  160. }
  161. for (j=0; j<kp2->total; j++)
  162. {
  163. // find average and standard deviation of each descriptor
  164. avg2[j] = 0;
  165. dev2[j] = 0;
  166. seq2 = (float*)cvGetSeqElem(desc2, j);
  167. for (k=0; k<descriptor_size; k++) avg2[j] += seq2[k];
  168. avg2[j] /= descriptor_size;
  169. for (k=0; k<descriptor_size; k++) dev2[j] +=
  170. (seq2[k]-avg2[j])*(seq2[k]-avg2[j]);
  171. dev2[j] = sqrt(dev2[j]/descriptor_size);
  172. // initialize best2 and best2corr
  173. best2[j] = -1;
  174. best2corr[j] = -1.;
  175. }
  176. double corr;
  177. for (i = 0; i < kp1->total; ++i)
  178. {
  179. seq1 = (float*)cvGetSeqElem(desc1, i);
  180. for (j = 0; j < kp2->total; ++j)
  181. {
  182. corr = 0;
  183. seq2 = (float*)cvGetSeqElem(desc2, j);
  184. for (k = 0; k < descriptor_size; ++k)
  185. corr += (seq1[k]-avg1[i])*(seq2[k]-avg2[j]);
  186. corr /= (descriptor_size-1)*dev1[i]*dev2[j];
  187. if (corr > best1corr[i])
  188. {
  189. best1corr[i] = corr;
  190. best1[i] = j;
  191. }
  192. if (corr > best2corr[j])
  193. {
  194. best2corr[j] = corr;
  195. best2[j] = i;
  196. }
  197. }
  198. }
  199. j = 0;
  200. for (i = 0; i < kp1->total; i++)
  201. if (best2[best1[i]] == i && best1corr[i] > CORRELATION_THRESHOLD)
  202. j++;
  203. if (j>0 && filter)
  204. {
  205. points1 = cvCreateMat(1,j,CV_32FC2);
  206. points2 = cvCreateMat(1,j,CV_32FC2);
  207. CvPoint2D32f *p1, *p2;
  208. j = 0;
  209. for (i = 0; i < kp1->total; i++)
  210. {
  211. if (best2[best1[i]] == i && best1corr[i] > CORRELATION_THRESHOLD)
  212. {
  213. p1 = &((CvSURFPoint*)cvGetSeqElem(kp1,i))->pt;
  214. p2 = &((CvSURFPoint*)cvGetSeqElem(kp2,best1[i]))->pt;
  215. points1->data.fl[j*2] = p1->x;
  216. points1->data.fl[j*2+1] = p1->y;
  217. points2->data.fl[j*2] = p2->x;
  218. points2->data.fl[j*2+1] = p2->y;
  219. j++;
  220. }
  221. }
  222. }
  223. free(best2corr);
  224. free(best1corr);
  225. free(best2);
  226. free(best1);
  227. free(avg1);
  228. free(avg2);
  229. free(dev1);
  230. free(dev2);
  231. return j;
  232. }
  233. int ocvsurf::removeOutliers(CvMat *&points1, CvMat *&points2)
  234. {
  235. CvMat *F = cvCreateMat(3,3,CV_32FC1); //TODO: free F ?
  236. CvMat *status = cvCreateMat(1,points1->cols,CV_8UC1); //TODO: free status.
  237. int fm_count = cvFindFundamentalMat(points1,points2,F,
  238. CV_FM_RANSAC,1.,0.99,status );
  239. // iterates the set of putative correspondences and removes correspondences
  240. // marked as outliers by cvFindFundamentalMat()
  241. int count = 0;
  242. int j = 0;
  243. for (int i = 0; i < status->cols; i++) if (CV_MAT_ELEM(*status,unsigned
  244. char,0,i)) count++;
  245. CvMat *points1_ = points1;
  246. CvMat *points2_ = points2;
  247. if (!count)
  248. { // no inliers
  249. points1 = NULL;
  250. points2 = NULL; // beware: leak ?
  251. return j;
  252. }
  253. else
  254. {
  255. points1 = cvCreateMat(1,count,CV_32FC2);
  256. points2 = cvCreateMat(1,count,CV_32FC2);
  257. int j = 0;
  258. for (int i = 0; i < status->cols; i++)
  259. {
  260. if (CV_MAT_ELEM(*status,unsigned char,0,i))
  261. {
  262. points1->data.fl[j*2] = points1_->data.fl[i*2];
  263. // //p1->x
  264. points1->data.fl[j*2+1] = points1_->data.fl[i*2+1];
  265. //p1->y
  266. points2->data.fl[j*2] = points2_->data.fl[i*2];
  267. // //p2->x
  268. points2->data.fl[j*2+1] = points2_->data.fl[i*2+1];
  269. // //p2->y
  270. j++;
  271. }
  272. }
  273. }
  274. cvReleaseMat(&points1_);
  275. cvReleaseMat(&points2_);
  276. return j;
  277. }
  278. } /* end of namespace. */