void Mixture::convolve(const HOGPyramid & pyramid, vector<HOGPyramid::Matrix> & scores, vector<Indices> & argmaxes, vector<vector<vector<Model::Positions> > > * positions) const { if(empty() || pyramid.empty()) { scores.clear(); argmaxes.clear(); if(positions) positions->clear(); return; } const int nbModels = models_.size(); const int nbLevels = pyramid.levels().size(); // Convolve with all the models vector<vector<HOGPyramid::Matrix> > tmp(nbModels); convolve(pyramid, tmp, positions); // In case of error if(tmp.empty()) { scores.clear(); argmaxes.clear(); if(positions) positions->clear(); return; } // Resize the scores and argmaxes scores.resize(nbLevels); argmaxes.resize(nbLevels); int i; #pragma omp parallel for private(i) for(i = 0; i < nbLevels; ++i) { scores[i].resize(pyramid.levels()[i].rows() - maxSize().first + 1, pyramid.levels()[i].cols() - maxSize().second + 1); argmaxes[i].resize(scores[i].rows(), scores[i].cols()); for(int y = 0; y < scores[i].rows(); ++y) { for(int x = 0; x < scores[i].cols(); ++x) { int argmax = 0; for(int j = 1; j < nbModels; ++j) if(tmp[j][i](y, x) > tmp[argmax][i](y, x)) argmax = j; scores[i](y, x) = tmp[argmax][i](y, x); argmaxes[i](y, x) = argmax; } } } }
void printHogSizes(HOGPyramid pyramid){ int nlevels = pyramid.levels().size(); for(int level = 0; level < nlevels; level++){ //const float* raw_hog = pyramid.levels()[level].data()->data(); int width = pyramid.levels()[level].cols(); int height = pyramid.levels()[level].rows(); int depth = pyramid.NbFeatures; printf("level %d: width=%d, height=%d, depth=%d \n", level, width, height, depth); } }
// nRows = 32 // nCols = width*height void writePyraToCsv(HOGPyramid pyramid){ int nlevels = pyramid.levels().size(); for(int level = 0; level < nlevels; level++){ //printf("writing to CSV: level %d \n", level); const float* raw_hog = pyramid.levels()[level].data()->data(); int width = pyramid.levels()[level].cols(); int height = pyramid.levels()[level].rows(); int depth = pyramid.NbFeatures; ostringstream fname; fname << "../piggyHOG_results/level" << level << ".csv"; //TODO: get orig img name into the CSV name. int nCols = depth; //one descriptor per row int nRows = width*height; //TODO: also write (depth, width, height) -- in some order -- to the top of the CSV file. writeCsv_2dFloat(raw_hog, nRows, nCols, fname.str()); } }
Patchwork::Patchwork(const HOGPyramid & pyramid) : padx_(pyramid.padx()), pady_(pyramid.pady()), interval_(pyramid.interval()) { // Remove the padding from the bottom/right sides since convolutions with Fourier wrap around const int nbLevels = pyramid.levels().size(); rectangles_.resize(nbLevels); for (int i = 0; i < nbLevels; ++i) { rectangles_[i].first.setWidth(pyramid.levels()[i].cols() - padx_); rectangles_[i].first.setHeight(pyramid.levels()[i].rows() - pady_); } // Build the patchwork planes const int nbPlanes = BLF(rectangles_); // Constructs an empty patchwork in case of error if (nbPlanes <= 0) return; planes_.resize(nbPlanes); for (int i = 0; i < nbPlanes; ++i) { planes_[i] = Plane::Constant(MaxRows_, HalfCols_, Cell::Zero()); Map<HOGPyramid::Level, Aligned> plane(reinterpret_cast<HOGPyramid::Cell *>(planes_[i].data()), MaxRows_, HalfCols_ * 2); // Set the last feature to 1 for (int y = 0; y < MaxRows_; ++y) for (int x = 0; x < MaxCols_; ++x) plane(y, x)(HOGPyramid::NbFeatures - 1) = 1.0f; } // Recopy the pyramid levels into the planes for (int i = 0; i < nbLevels; ++i) { Map<HOGPyramid::Level, Aligned> plane(reinterpret_cast<HOGPyramid::Cell *>(planes_[rectangles_[i].second].data()), MaxRows_, HalfCols_ * 2); plane.block(rectangles_[i].first.y(), rectangles_[i].first.x(), rectangles_[i].first.height(), rectangles_[i].first.width()) = pyramid.levels()[i].topLeftCorner(rectangles_[i].first.height(), rectangles_[i].first.width()); } // Transform the planes int i; #pragma omp parallel for private(i) for (i = 0; i < nbPlanes; ++i) #ifndef FFLD_HOGPYRAMID_DOUBLE fftwf_execute_dft_r2c(Forwards_, reinterpret_cast<float *>(planes_[i].data()->data()), reinterpret_cast<fftwf_complex *>(planes_[i].data()->data())); #else fftw_execute_dft_r2c(Forwards_, reinterpret_cast<double *>(planes_[i].data()->data()), reinterpret_cast<fftw_complex *>(planes_[i].data()->data())); #endif }
void CFFLD::detect(const Mixture & mixture, int width, int height, const HOGPyramid & pyramid, double threshold, double overlap, const string image, ostream & out, const string & images, vector<Detection> & detections, vector<DetectionResult> & vResult ) { // Compute the scores vector<HOGPyramid::Matrix> scores; vector<Mixture::Indices> argmaxes; vector<vector<vector<Model::Positions> > > positions; if (!images.empty()) { mixture.convolve(pyramid, scores, argmaxes, &positions); } else { mixture.convolve(pyramid, scores, argmaxes); } //cout<<"conv"<<endl; // Cache the size of the models vector<pair<int, int> > sizes(mixture.models().size()); for (int i = 0; i < sizes.size(); ++i) { sizes[i] = mixture.models()[i].rootSize(); } // For each scale for (int i = pyramid.interval(); i < scores.size(); ++i) { // Scale = 8 / 2^(1 - i / interval) const double scale = pow(2.0, static_cast<double>(i) / pyramid.interval() + 2.0); const int rows = scores[i].rows(); const int cols = scores[i].cols(); for (int y = 0; y < rows; ++y) { for (int x = 0; x < cols; ++x) { const float score = scores[i](y, x); if (score > threshold) { if (((y == 0) || (x == 0) || (score > scores[i](y - 1, x - 1))) && ((y == 0) || (score > scores[i](y - 1, x))) && ((y == 0) || (x == cols - 1) || (score > scores[i](y - 1, x + 1))) && ((x == 0) || (score > scores[i](y, x - 1))) && ((x == cols - 1) || (score > scores[i](y, x + 1))) && ((y == rows - 1) || (x == 0) || (score > scores[i](y + 1, x - 1))) && ((y == rows - 1) || (score > scores[i](y + 1, x))) && ((y == rows - 1) || (x == cols - 1) || (score > scores[i](y + 1, x + 1)))) { FFLD::Rectangle bndbox((x - pyramid.padx()) * scale + 0.5, (y - pyramid.pady()) * scale + 0.5, sizes[argmaxes[i](y, x)].second * scale + 0.5, sizes[argmaxes[i](y, x)].first * scale + 0.5); // Truncate the object bndbox.setX(max(bndbox.x(), 0)); bndbox.setY(max(bndbox.y(), 0)); bndbox.setWidth(min(bndbox.width(), width - bndbox.x())); bndbox.setHeight(min(bndbox.height(), height - bndbox.y())); if (!bndbox.empty()) { detections.push_back(Detection(score, i, x, y, bndbox)); } } } } } } // Non maxima suppression sort(detections.begin(), detections.end()); for (int i = 1; i < detections.size(); ++i) detections.resize(remove_if(detections.begin() + i, detections.end(), Intersector(detections[i - 1], overlap, true)) - detections.begin()); // Print the detection const size_t lastDot = image.find_last_of('.'); string id = image.substr(0, lastDot); const size_t lastSlash = id.find_last_of("/\\"); if (lastSlash != string::npos) id = id.substr(lastSlash + 1); if (out) { #pragma omp critical for (int i = 0; i < detections.size(); ++i) { out << id << ' ' << detections[i].score << ' ' << (detections[i].left() + 1) << ' ' << (detections[i].top() + 1) << ' ' << (detections[i].right() + 1) << ' ' << (detections[i].bottom() + 1) << endl; } } // Output the result to OpenCV Rect for( int j = 0; j < detections.size(); j ++ ) { DetectionResult result; // The position of the root one octave below const int argmax = argmaxes[detections[j].l](detections[j].y, detections[j].x); const int x2 = detections[j].x * 2 - pyramid.padx(); const int y2 = detections[j].y * 2 - pyramid.pady(); const int l = detections[j].l - pyramid.interval(); const double scale = pow(2.0, static_cast<double>(l) / pyramid.interval() + 2.0); //cout<<positions[argmax].size()<<endl; for (int k = 0; k < positions[argmax].size(); ++k) { const FFLD::Rectangle bndbox((positions[argmax][k][l](y2, x2)(0) - pyramid.padx()) * scale + 0.5, (positions[argmax][k][l](y2, x2)(1) - pyramid.pady()) * scale + 0.5, mixture.models()[argmax].partSize().second * scale + 0.5, mixture.models()[argmax].partSize().second * scale + 0.5 ); Rect rtPart( bndbox.x_, bndbox.y_, bndbox.width_, bndbox.height_ ); //cout<<rtPart<<endl; result.vParts.push_back( rtPart ); } Rect rtRoot( detections[j].x_, detections[j].y_, detections[j].width_, detections[j].height_ ); result.rtRoot = rtRoot; vResult. push_back(result); } }
void Mixture::convolve(const HOGPyramid & pyramid, vector<vector<HOGPyramid::Matrix> > & scores, vector<vector<vector<Model::Positions> > > * positions) const { if(empty() || pyramid.empty()) { scores.clear(); if(positions) positions->clear(); } const int nbModels = models_.size(); scores.resize(nbModels); if(positions) positions->resize(nbModels); // Transform the filters if needed #pragma omp critical if(filterCache_.empty()) cacheFilters(); while(!cached_); // Create a patchwork const Patchwork patchwork(pyramid); // Convolve the patchwork with the filters vector<vector<HOGPyramid::Matrix> > convolutions(filterCache_.size()); patchwork.convolve(filterCache_, convolutions); // In case of error if(convolutions.empty()) { scores.clear(); if(positions) positions->clear(); return; } // Save the offsets of each model in the filter list vector<int> offsets(nbModels); for(int i = 0, j = 0; i < nbModels; ++i) { offsets[i] = j; j += models_[i].parts_.size(); } // For each model int i; #pragma omp parallel for private(i) for(i = 0; i < nbModels; ++i) { vector<vector<HOGPyramid::Matrix> > tmp(models_[i].parts_.size()); for(size_t j = 0; j < tmp.size(); ++j) tmp[j].swap(convolutions[offsets[i] + j]); models_[i].convolve(pyramid, tmp, scores[i], positions ? & (*positions)[i] : 0); } }