vocabtree  0.0.1
bag_of_words.cxx
Go to the documentation of this file.
1 #include <config.hpp>
2 
3 #include "bag_of_words.hpp"
4 
5 #include <utils/filesystem.hpp>
6 #include <utils/vision.hpp>
7 #include <iostream>
8 #include <memory>
9 
10 #if ENABLE_FASTCLUSTER
11 #include <fastann/fastann.hpp>
12 #include <fastcluster/kmeans.hpp>
13 #endif
14 #if ENABLE_MPI
15 #include <mpi.h>
16 #endif
17 
19 
20 
21 }
22 
23 BagOfWords::BagOfWords(const std::string &file_path) : SearchBase(file_path) {
24  if(!filesystem::file_exists(file_path)) {
25  std::cerr << "Error reading bag of words from " << file_path << std::endl;
26  return;
27  }
28  if(!this->load(file_path)) {
29  std::cerr << "Error reading bag of words from " << file_path << std::endl;
30  }
31 }
32 
33 bool BagOfWords::load (const std::string &file_path) {
34  std::cout << "Reading bag of words from " << file_path << "..." << std::endl;
35 
36  if (!filesystem::load_cvmat(file_path, vocabulary_matrix)) {
37  std::cerr << "Failed to read vocabulary from " << file_path << std::endl;
38  return false;
39  }
40 
41  std::cout << "Done reading bag of words." << std::endl;
42 
43  return true;
44 }
45 
46 
47 bool BagOfWords::save (const std::string &file_path) const {
48  std::cout << "Writing bag of words to " << file_path << "..." << std::endl;
49 
51  if (!filesystem::write_cvmat(file_path, vocabulary_matrix)) {
52  std::cerr << "Failed to write vocabulary to " << file_path << std::endl;
53  return false;
54  }
55 
56  std::cout << "Done writing bag of words." << std::endl;
57  return true;
58 }
59 #if ENABLE_FASTCLUSTER && ENABLE_MPI
60 void load_rows_in_mem(void* p, unsigned l, unsigned r, float* out) {
61  float *fp = (float *)p;
62  memcpy(out, &fp[l * 128] , sizeof(float) * 128 * (r - l));
63 }
64 
65 void load_rows_out_mem(void* p, unsigned l, unsigned r, float* out) {
66  assert(0);
67  // float *fp = (float *)p;
68  // memcpy(out, &fp[l * 128] , sizeof(float) * 128 * (r - l));
69 }
70 
71 fastann::nn_obj<float>* build_nnobj(void* p, float* clusters, unsigned K, unsigned D) {
72  fastann::nn_obj<float>* kd_tree = fastann::nn_obj_build_kdtree(clusters, K, D, 8, 512);
73  return kd_tree;
74 }
75 #endif
76 
77 bool BagOfWords::train(Dataset &dataset, const std::shared_ptr<const TrainParamsBase> &params, const std::vector< std::shared_ptr<const Image > > &examples) {
78 
79  const std::shared_ptr<const TrainParams> &ii_params = std::static_pointer_cast<const TrainParams>(params);
80 
81  uint32_t k = ii_params->numClusters;
82  uint32_t n = ii_params->numFeatures;
83 
84  std::vector<uint64_t> all_ids(examples.size());
85  for (uint64_t i = 0; i < examples.size(); i++) {
86  all_ids[i] = examples[i]->id;
87  }
88  std::random_shuffle(all_ids.begin(), all_ids.end());
89 
90  std::vector<cv::Mat> all_descriptors;
91  uint64_t num_features = 0;
92  for (size_t i = 0; i < all_ids.size(); i++) {
93  std::shared_ptr<Image> image = std::static_pointer_cast<Image>(dataset.image(all_ids[i]));
94  if (image == nullptr) continue;
95 
96  const std::string &descriptors_location = dataset.location(image->feature_path("descriptors"));
97  if (!filesystem::file_exists(descriptors_location)) continue;
98 
99  cv::Mat descriptors, descriptorsf;
100  if (filesystem::load_cvmat(descriptors_location, descriptors)) {
101  num_features += descriptors.rows;
102  if (n > 0 && num_features > n) break;
103  descriptors.convertTo(descriptorsf, CV_32FC1);
104  all_descriptors.push_back(descriptorsf);
105  }
106  }
107  const cv::Mat merged_descriptor = vision::merge_descriptors(all_descriptors, true);
108 
109 #if ENABLE_FASTCLUSTER && ENABLE_MPI
110 
111  int rank = MPI::COMM_WORLD.Get_rank();
112 
113  uint32_t D = merged_descriptor.cols;
114  float *dataf = (float *)merged_descriptor.data;
115  vocabulary_matrix = cv::Mat(k, D, CV_32FC1);
116  float *clusters = (float *)vocabulary_matrix.data;
117 
118  // initialize the clusters (random at the moment)
119  if(rank == 0) { // initial clusters get broadcast
120  std::vector<uint64_t> indices(num_features);
121  for(size_t i=0; i<indices.size(); i++) {
122  indices[i] = i;
123  }
124  std::random_shuffle(indices.begin(), indices.end());
125  for(size_t i=0; i<k; i++) {
126  memcpy(&clusters[D * i], &dataf[D * indices[i]], sizeof(float) * D);
127  }
128  }
129 
130  fastcluster::kmeans<float>(load_rows_in_mem, (void *)merged_descriptor.data,
131  build_nnobj, (void *)merged_descriptor.data,
132  (float *)clusters, num_features, D, k, 16, 0, (char *)0);
133  return true;
134 #else
135  cv::Mat labels;
136  uint32_t attempts = 1;
137  cv::TermCriteria tc(cv::TermCriteria::COUNT | cv::TermCriteria::EPS, 16, 0.0001);
138  if (k > merged_descriptor.rows) { // k > n
139  std::cerr << "Warning: # clusters > # features, automatically setting #clusters = #features." << std::endl;
140  k = merged_descriptor.rows;
141  }
142  cv::kmeans(merged_descriptor, k, labels, tc, attempts, cv::KMEANS_PP_CENTERS, vocabulary_matrix);
143 #endif
144  return true;
145 }
146 
147 std::shared_ptr<MatchResultsBase> BagOfWords::search(Dataset &dataset, const std::shared_ptr<const SearchParamsBase> &params, const std::shared_ptr<const Image > &example) {
148  assert(0);
149  return nullptr;
150 }
151 
152 cv::Mat BagOfWords::vocabulary() const {
153  return vocabulary_matrix;
154 }
155 
156 uint32_t BagOfWords::num_clusters() const {
157  return vocabulary_matrix.rows;
158 }