void run_teste_integral_2(){ std::string crop_image = Config::PROJECT_PATH + "/dataset/24x24/Instancia_Sun_300_400/training_images/non_faces/group_0/crop/1403571922.05/crop_399.pgm"; std::string full_image = Config::PROJECT_PATH + "/dataset/24x24/Instancia_Sun_nocrop/training_images/non_faces/SUN2012/16155.pgm"; IntegralImage ii1(crop_image); teste_integral_2(ii1); ulong*** data; int crop_start_index = 10; int ncrops = 1; int maxCrops = 10; int crop_width = 24; int crop_height = 24; int shift_step = 24; Point img_size; int last_crop; int random_hop = 23; ulong** img_data; getImageCrops(&data,&img_data,&img_size,full_image.c_str(),&crop_start_index,ncrops,maxCrops,crop_width,crop_height,shift_step,random_hop,checkData,NULL); Point p; p.x = 24; p.y = 24; IntegralImage ii2(data[0],p,true); teste_integral_2(ii2); }
// Calculates the array of values used as the integral image, lest values need calculation. void JCHaarFinder::generateIIArray() { // if true, the gnerated II array will be printed to a file after calculation bool takeSnapshot = false; int width = curImage->getWidth(), height = curImage->getHeight(); if (_iiArray == NULL) { free(_iiArray); } _iiArray = (int*) malloc(width * height * sizeof(int)); _ii2Array = (int*) malloc(width * height * sizeof(int)); for (int i = 0; i<width*height; i++) { _iiArray[i] = 0; _ii2Array[i] = 0; } for (int row=0; row < height; row++) { int rowsum = 0; int rowsum2 = 0; /* The strategy here is to keep a sum of the current row that is calculated. * The values of the fields in the rows are continuously added to the rowsum * variable, and the columns are taken care of by looking earlier in the array * as we're building it (we look in the previous row at the same column). This * eventually yields the integral image. * * Also, we keep an integral image over all the squares of the pixel values. This * is used for the standard deviations in the normalization stage when the feature * sums are compared to their thresholds. */ for(int col=0; col < width; col++) { rowsum += i(col, row); rowsum2 += i(col, row)*i(col, row); if (row > 0) { _iiArray[row*width + col] = rowsum + ii(col, row-1); _ii2Array[row*width + col] = rowsum2 + ii2(col, row-1); } else { _iiArray[row*width + col] = rowsum; _ii2Array[row*width + col] = rowsum2; } } } // Debug functionality that writes out the integral images in CSV structures. if (takeSnapshot) { ofFile iiFile; ofFile ii2File; iiFile.open(ofToDataPath("ii.csv"), ofFile::WriteOnly, false); ii2File.open(ofToDataPath("ii2.csv"), ofFile::WriteOnly, false); for (int row= 0; row<height; row++) { for (int col = 0; col<width; col++) { iiFile << ofToString(ii(col, row)) + ";"; ii2File << ofToString(ii2(col, row)) + ";"; } iiFile << endl; ii2File << endl; } iiFile.close(); } }
// Returns a vector of rectangles that pass the requirements for faces. vector<ofxCvBlob> JCHaarFinder::getRectsFromImage(ofImage* inputImage) { curImage = inputImage; float scale = 1; float scaleMultiplier = 1.25; blobs.clear(); generateIIArray(); /* * Strategy: Loop through the image such that a window moves over the * picture, checking stages. Once the window has moved through the image * scale it a bit up, and re-run the algorithm. * * The values to be for'ed over are nested as follows: * * the scale of the cascade window * window's position on y axis * window's position on x axis * stages of the cascade * features of the stage * rectangles of the feature * * Since we don't support larger trees than the ones which only have a * root node, we can ignore traversing trees and just consider a list * of rectangles for any one feature. */ // This implementation keeps enlarging the window until it is bigger than the picture. for (scale = 1; scale*casc.height <= curImage->getHeight() || scale*casc.width <= curImage->getWidth(); scale *= scaleMultiplier) { int window_area = scale*casc.height * scale*casc.width; // Keep moving the window down as long as its offset and its height are within the image. Likewise to the left // As an experiment, we're shifting the window in chunks equal to about a tenth of the current window height and width, respectively. for (int offsetY = 0; (offsetY+scale*casc.height) < curImage->getHeight(); offsetY += (int) ((scale*casc.height)/10) ) { for (int offsetX = 0; (offsetX + scale*casc.width) < curImage->getWidth(); offsetX += (int) ((scale*casc.width)/10)) { bool passed = true; // *** LOOPING OVER STAGES for (int curStageIdx = 0; curStageIdx < casc.stages.size(); curStageIdx++) { if (passed == false) break; stage* s = &casc.stages.at(curStageIdx); float stage_sum = 0.0; // *** LOOPING OVER FEATURES for (int featureIdx = 0; featureIdx < s->features.size(); featureIdx++) { feature* f = & (s->features.at(featureIdx)); float feature_sum = 0.0; // These coordinates are for the sliding window int px1 = offsetX, py1 = offsetY, px2 = offsetX + scale*casc.width, py2 = offsetY + scale*casc.height; float mean = ((float) (ii(px2, py2) + ii(px1, py1) - ii(px1, py2) - ii(px2, py1))) / window_area; float stddev = sqrt((ii2(px2, py2) + ii2(px1, py1) - ii2(px1, py2) - ii2(px2, py1)) / window_area - mean*mean); // *** LOOPING OVER RECTANGLES for (int rectangleIdx = 0; rectangleIdx < f->rectangles.size(); rectangleIdx++) { featureRect* r = & ( f->rectangles.at(rectangleIdx)); // These coordinates are for the rectangles inside the sliding window. The coordinates // are absolute; i.e. they share the same origin as the coordinates of the window. int x1 = offsetX + (r->rectangle.x * scale), y1 = offsetY + (r->rectangle.y * scale), x2 = x1 + r->rectangle.width * scale, y2 = y1 + r->rectangle.height * scale; int thisRect = (ii(x2, y2) + ii(x1, y1) - ii(x1, y2) - ii(x2, y1)) * r->weight; feature_sum += thisRect; } // Determine in which direction the cascade should "fall". If the feature sum is less than // its threshold, fall left, otherwise right. // http://stackoverflow.com/questions/978742/what-do-the-left-and-right-values-mean-in-the-haar-cascade-xml-files if (feature_sum/window_area < f->threshold*stddev) { stage_sum += f->leftVal; } else { stage_sum += f->rightVal; } } // The stage is passed if its sum is above its threshold. passed = (stage_sum > s->threshold); } // passed will be true iff all stages passed; if so, we detected a face. if (passed) { ofLog(OF_LOG_NOTICE, "detected face (maybe)!"); blobs.push_back(makeBlob(offsetX, offsetY, scale*casc.width, scale*casc.height)); } } } } return blobs; }