/** ------------------------------------------------------------------- ** @brief Create a new MSER filter ** ** Initializes a new MSER filter for images of the specified ** dimensions. Images are @a ndims -dimensional arrays of dimensions ** @a dims. ** ** @param ndims number of dimensions. ** @param dims dimensions. **/ VL_EXPORT VlMserFilt* vl_mser_new (int ndims, int const* dims) { VlMserFilt* f ; int *strides, k ; f = (VlMserFilt*) vl_calloc (sizeof(VlMserFilt), 1) ; f-> ndims = ndims ; f-> dims = (int*) vl_malloc (sizeof(int) * ndims) ; f-> subs = (int*) vl_malloc (sizeof(int) * ndims) ; f-> dsubs = (int*) vl_malloc (sizeof(int) * ndims) ; f-> strides = (int*) vl_malloc (sizeof(int) * ndims) ; /* shortcuts */ strides = f-> strides ; /* copy dims to f->dims */ for(k = 0 ; k < ndims ; ++k) { f-> dims [k] = dims [k] ; } /* compute strides to move into the N-dimensional image array */ strides [0] = 1 ; for(k = 1 ; k < ndims ; ++k) { strides [k] = strides [k-1] * dims [k-1] ; } /* total number of pixels */ f-> nel = strides [ndims-1] * dims [ndims-1] ; /* dof of ellipsoids */ f-> dof = ndims * (ndims + 1) / 2 + ndims ; /* more buffers */ f-> perm = (vl_uint*) vl_malloc (sizeof(vl_uint) * f-> nel) ; f-> joins = (vl_uint*) vl_malloc (sizeof(vl_uint) * f-> nel) ; f-> r = (VlMserReg*) vl_malloc (sizeof(VlMserReg) * f-> nel) ; f-> er = 0 ; f-> rer = 0 ; f-> mer = 0 ; f-> rmer = 0 ; f-> ell = 0 ; f-> rell = 0 ; /* other parameters */ f-> delta = 5 ; f-> max_area = 0.75 ; f-> min_area = 3.0 / f-> nel ; f-> max_variation = 0.25 ; f-> min_diversity = 0.2 ; return f ; }
VL_EXPORT VlDsiftFilter * vl_dsift_new (int imWidth, int imHeight) { VlDsiftFilter * self = vl_malloc (sizeof(VlDsiftFilter)) ; self->imWidth = imWidth ; self->imHeight = imHeight ; self->stepX = 5 ; self->stepY = 5 ; self->boundMinX = 0 ; self->boundMinY = 0 ; self->boundMaxX = imWidth - 1 ; self->boundMaxY = imHeight - 1 ; self->geom.numBinX = 4 ; self->geom.numBinY = 4 ; self->geom.numBinT = 8 ; self->geom.binSizeX = 5 ; self->geom.binSizeY = 5 ; self->useFlatWindow = VL_FALSE ; self->windowSize = 2.0 ; self->convTmp1 = vl_malloc(sizeof(float) * self->imWidth * self->imHeight) ; self->convTmp2 = vl_malloc(sizeof(float) * self->imWidth * self->imHeight) ; self->numBinAlloc = 0 ; self->numFrameAlloc = 0 ; self->numGradAlloc = 0 ; self->descrSize = 0 ; self->numFrames = 0 ; self->grads = NULL ; self->frames = NULL ; self->descrs = NULL ; _vl_dsift_update_buffers(self) ; return self ; }
static void VL_XCAT(_vl_kmeans_set_centers_, SFX) (VlKMeans * self, TYPE const * centers, vl_size dimension, vl_size numCenters) { self->dimension = dimension ; self->numCenters = numCenters ; self->centers = vl_malloc (sizeof(TYPE) * dimension * numCenters) ; memcpy ((TYPE*)self->centers, centers, sizeof(TYPE) * dimension * numCenters) ; }
vl_aib_prob * vl_aib_new_Pc(vl_aib_prob * Pcx, vl_aib_node nvalues, vl_aib_node nlabels) { vl_aib_prob * Pc = vl_malloc(sizeof(vl_aib_prob)*nlabels); vl_aib_node r, c; for(c=0; c<nlabels; c++) { vl_aib_prob sum = 0; for(r=0; r<nvalues; r++) sum += Pcx[r*nlabels+c]; Pc[c] = sum; } return Pc; }
VL_EXPORT VlArray * vl_array_init (VlArray* self, vl_type type, vl_size numDimensions, vl_size const * dimensions) { assert (numDimensions <= VL_ARRAY_MAX_NUM_DIMENSIONS) ; self->type = type ; self->numDimensions = numDimensions ; memcpy(self->dimensions, dimensions, sizeof(vl_size) * numDimensions) ; self->data = vl_malloc(vl_get_type_size(type) * vl_array_get_num_elements (self)) ; self->isEnvelope = VL_FALSE ; self->isSparse = VL_FALSE ; return self ; }
double * vl_aib_new_Pc(double * Pcx, vl_uint nvalues, vl_uint nlabels) { double * Pc = vl_malloc(sizeof(double)*nlabels); vl_uint r, c; for(c=0; c<nlabels; c++) { double sum = 0; for(r=0; r<nvalues; r++) sum += Pcx[r*nlabels+c]; Pc[c] = sum; } return Pc; }
VL_EXPORT VlQS * vl_quickshift_new(vl_qs_type const * image, int height, int width, int channels) { VlQS * q = vl_malloc(sizeof(VlQS)); q->image = (vl_qs_type *)image; q->height = height; q->width = width; q->channels = channels; q->medoid = VL_FALSE; q->tau = VL_MAX(height,width)/50; q->sigma = VL_MAX(2, q->tau/3); q->dists = vl_malloc(sizeof(vl_qs_type)*height*width); q->parents = vl_malloc(sizeof(int)*height*width); q->density = vl_malloc(sizeof(vl_qs_type)*height*width); return q; }
size_t read_VlKDTree(FILE *fp, VlKDForest * self, VlKDTree *tree) { size_t n = 0; vl_size i; n += fread(&(tree->numUsedNodes), sizeof(vl_size), 1, fp); n += fread(&(tree->numAllocatedNodes), sizeof(vl_size), 1, fp); n += fread(&(tree->depth), sizeof(unsigned int), 1, fp); tree->nodes = vl_malloc(sizeof(VlKDTreeNode) * tree->numAllocatedNodes); for(i = 0;i < tree->numAllocatedNodes; i++){ n += read_VlKDTreeNode(fp, &tree->nodes[i]); } tree->dataIndex = vl_malloc(sizeof(VlKDTreeDataIndexEntry) * self->numData); for(i = 0;i < self->numData ; i++){ n += read_VlKDTreeDataIndexEntry(fp, &tree->dataIndex[i]); } return n; }
VL_EXPORT VlHIKMTree * vl_hikm_new (int method) { VlHIKMTree *f = vl_malloc (sizeof(VlHIKMTree)) ; f -> M = 0 ; f -> K = 0 ; f -> max_niters = 200 ; f -> method = method ; f -> verb = 0 ; f -> depth = 0 ; f -> root = 0 ; return f ; }
VlIKMFilt * vl_ikm_new (int method) { VlIKMFilt *f = vl_malloc (sizeof(VlIKMFilt)) ; f -> centers = 0 ; f -> inter_dist = 0 ; f -> M = 0 ; f -> K = 0 ; f -> method = method ; f -> max_niters = 200 ; f -> verb = 0 ; return f ; }
VL_EXPORT int vl_pgm_insert(FILE* f, VlPgmImage const *im, void const *data) { vl_size bpp = vl_pgm_get_bpp (im) ; vl_size data_size = vl_pgm_get_npixels (im) ; size_t c ; VlThreadSpecificState * threadState = vl_get_thread_specific_state() ; /* write preamble */ fprintf(f, "P5\n%d\n%d\n%d\n", (signed)im->width, (signed)im->height, (signed)im->max_value) ; /* take care of endianness */ #if defined(VL_ARCH_LITTLE_ENDIAN) if (bpp == 2) { vl_uindex i ; vl_uint8* temp = (vl_uint8* )vl_malloc (2 * data_size) ; memcpy(temp, data, 2 * data_size) ; for(i = 0 ; i < 2 * data_size ; i += 2) { vl_uint8 tmp = temp [i] ; temp [i] = temp [i+1] ; temp [i+1] = tmp ; } c = fwrite(temp, 2, data_size, f) ; vl_free (temp) ; } else { #endif c = fwrite(data, bpp, data_size, f) ; #if defined(VL_ARCH_LITTLE_ENDIAN) } #endif if(c != data_size) { snprintf(threadState->lastErrorMessage, VL_ERR_MSG_LEN, "Error writing PGM data") ; return (threadState->lastError = VL_ERR_PGM_IO) ; } return 0 ; }
VL_EXPORT VlKDForest * vl_kdforest_new (vl_type dataType, vl_size dimension, vl_size numTrees) { VlKDForest * self = vl_malloc (sizeof(VlKDForest)) ; assert(dataType == VL_TYPE_FLOAT || dataType == VL_TYPE_DOUBLE) ; assert(dimension >= 1) ; assert(numTrees >= 1) ; self -> rand = vl_get_rand () ; self -> dataType = dataType ; self -> numData = 0 ; self -> data = 0 ; self -> dimension = dimension ; self -> numTrees = numTrees ; self -> trees = 0 ; self -> thresholdingMethod = VL_KDTREE_MEDIAN ; self -> splitHeapSize = (numTrees == 1) ? 1 : VL_KDTREE_SPLIT_HEALP_SIZE ; self -> splitHeapNumNodes = 0 ; self -> searchHeapArray = 0 ; self -> searchHeapNumNodes = 0 ; self -> searchMaxNumComparisons = 0 ; self -> searchIdBook = 0 ; self -> searchId = 0 ; switch (self->dataType) { case VL_TYPE_FLOAT: self -> distanceFunction = (void(*)(void)) vl_get_vector_comparison_function_f (VlDistanceL2) ; break ; case VL_TYPE_DOUBLE : self -> distanceFunction = (void(*)(void)) vl_get_vector_comparison_function_d (VlDistanceL2) ; break ; default : abort() ; } return self ; }
VL_EXPORT VlKMeans * vl_kmeans_new (vl_type dataType, VlVectorComparisonType distance) { VlKMeans * self = vl_malloc(sizeof(VlKMeans)) ; self->algorithm = VlKMeansLloyd ; self->distance = distance ; self->dataType = dataType ; self->verbosity = 0 ; self->maxNumIterations = 100 ; self->numRepetitions = 1 ; self->centers = NULL ; self->centerDistances = NULL ; vl_kmeans_reset (self) ; return self ; }
float * _vl_dsift_new_kernel (int binSize, int numBins, int binIndex, double windowSize) { int filtLen = 2 * binSize - 1 ; float * ker = vl_malloc (sizeof(float) * filtLen) ; float * kerIter = ker ; float delta = binSize * (binIndex - 0.5F * (numBins - 1)) ; /* float sigma = 0.5F * ((numBins - 1) * binSize + 1) ; float sigma = 0.5F * ((numBins) * binSize) ; */ float sigma = (float) binSize * (float) windowSize ; int x ; for (x = - binSize + 1 ; x <= + binSize - 1 ; ++ x) { float z = (x - delta) / sigma ; *kerIter++ = (1.0F - fabsf(x) / binSize) * ((binIndex >= 0) ? expf(- 0.5F * z*z) : 1.0F) ; } return ker ; }
static double VL_XCAT(_vl_kmeans_update_center_distances_, SFX) (VlKMeans * self) { #if (FLT == VL_TYPE_FLOAT) VlFloatVectorComparisonFunction distFn = vl_get_vector_comparison_function_f(self->distance) ; #else VlDoubleVectorComparisonFunction distFn = vl_get_vector_comparison_function_d(self->distance) ; #endif if (! self->centerDistances) { self->centerDistances = vl_malloc (sizeof(TYPE) * self->numCenters * self->numCenters) ; } VL_XCAT(vl_eval_vector_comparison_on_all_pairs_, SFX)(self->centerDistances, self->dimension, self->centers, self->numCenters, NULL, 0, distFn) ; return self->numCenters * (self->numCenters - 1) / 2 ; }
int vl_pgm_insert(FILE* f, VlPgmImage const *im, void *data) { int bpp = vl_pgm_get_bpp (im) ; int data_size = vl_pgm_get_data_size (im) ; int c ; /* write preamble */ fprintf(f, "P5\n%d\n%d\n%d\n", im->width, im->height, im->max_value) ; /* take care of endianness */ if (bpp == 2 && ! vl_get_endianness() == VL_BIG_ENDIAN) { int i ; vl_uint8* temp = vl_malloc (2 * data_size) ; memcpy(temp, data, 2 * data_size) ; for(i = 0 ; i < 2 * data_size ; i += 2) { vl_uint8 tmp = temp [i] ; temp [i] = temp [i+1] ; temp [i+1] = tmp ; } c = fwrite(temp, 2, data_size, f) ; vl_free (temp) ; } else { c = fwrite(data, bpp, data_size, f) ; } if(c != data_size) { vl_err_no = VL_ERR_PGM_IO ; snprintf(vl_err_msg, VL_ERR_MSG_LEN, "Error writing PGM data") ; return vl_err_no ; } return 0 ; }
VL_EXPORT char * vl_configuration_to_string_copy () { char * string = 0 ; int length = 0 ; char * staticString = vl_static_configuration_to_string_copy() ; char * cpuString = #if defined(VL_ARCH_IX86) || defined(VL_ARCH_X64) || defined(VL_ARCH_IA64) _vl_x86cpu_info_to_string_copy(&vl_get_state()->cpuInfo) ; #else "Generic CPU" ; #endif #if defined(DEBUG) int const debug = 1 ; #else int const debug = 0 ; #endif while (string == 0) { if (length > 0) { string = vl_malloc(sizeof(char) * length) ; if (string == NULL) break ; } length = snprintf(string, length, "VLFeat version %s\n" " Static config: %s\n" " %d CPU(s): %s\n" " Debug: %s\n", vl_get_version_string (), staticString, vl_get_num_cpus(), cpuString, VL_YESNO(debug)) ; length += 1 ; } if (staticString) vl_free(staticString) ; if (cpuString) vl_free(cpuString) ; return string ; }
char * _vl_x86cpu_info_to_string_copy (VlX86CpuInfo const *self) { char * string = 0 ; int length = 0 ; while (string == 0) { if (length > 0) { string = vl_malloc(sizeof(char) * length) ; if (string == NULL) break ; } length = snprintf(string, length, "%s%s%s%s%s%s%s", self->vendor.string, self->hasMMX ? " MMX" : "", self->hasSSE ? " SSE" : "", self->hasSSE2 ? " SSE2" : "", self->hasSSE3 ? " SSE3" : "", self->hasSSE41 ? " SSE41" : "", self->hasSSE42 ? " SSE42" : "") ; length += 1 ; } return string ; }
bool HogExtractor::Extract(const vector<Mat> &images, Mat *feats) { if (feats == nullptr) { return false; } *feats = Mat(); for (auto image: images) { // Convert Mat to float array float image_data[image.total()]; int k = 0; for (int i = 0; i < image.rows; ++i) for (int j = 0; j < image.cols; ++j, ++k) { image_data[k] = image.at<int>(i, j); } // Extract HOG features vl_hog_put_image(hog_, image_data, image.rows, image.cols, image.channels(), cell_size_); set_feat_dim(vl_hog_get_width(hog_) * vl_hog_get_height(hog_) * vl_hog_get_dimension(hog_)); float *hog_arr = (float*)vl_malloc(feat_dim() * sizeof(float)); vl_hog_extract(hog_, hog_arr); // Convert float array to Mat Mat feat_row(1, feat_dim(), CV_32F); for (int i = 0; i < feat_dim(); ++i) { feat_row.at<float>(0, i) = hog_arr[i]; } vl_free(hog_arr); feats->push_back(feat_row); } return true; }
VL_EXPORT int vl_pgm_read_new_f (char const *name, VlPgmImage *im, float** data) { int err = 0 ; size_t npixels ; vl_uint8 *idata ; err = vl_pgm_read_new (name, im, &idata) ; if (err) { return err ; } npixels = vl_pgm_get_npixels(im) ; *data = (float*)vl_malloc (sizeof(float) * npixels) ; { size_t k ; float scale = 1.0f / im->max_value ; for (k = 0 ; k < npixels ; ++ k) (*data)[k] = scale * idata[k] ; } vl_free (idata) ; return 0 ; }
float * _vl_dsift_new_kernel (int binSize, int numBins, int binIndex) { int filtLen = 2 * binSize - 1 ; float * ker = vl_malloc (sizeof(float) * filtLen) ; float * kerIter = ker ; float delta = binSize * (binIndex - 0.5F * (numBins - 1)) ; float sigma = 0.5F * ((numBins - 1) * binSize + 1) ; /* this is what standard SIFT would use. Above is what Oxford uses float sigma = 0.5F * ((numBins) * binSize) ; */ int x ; for (x = - binSize + 1 ; x <= + binSize - 1 ; ++ x) { float z = (x - delta) / sigma ; *kerIter++ = (1.0f - fabsf(x) / binSize) * ((binIndex >= 0) ? expf(- 0.5F * z*z) : 1.0F) ; /* *kerIter++ = (1.0f - fabsf(x) / binSize) ; */ } return ker ; }
VL_EXPORT int vl_pgm_read_new (char const *name, VlPgmImage *im, vl_uint8** data) { int err = 0 ; VlThreadSpecificState * threadState = vl_get_thread_specific_state() ; FILE *f = fopen (name, "rb") ; if (! f) { snprintf(threadState->lastErrorMessage, VL_ERR_MSG_LEN, "Error opening PGM file `%s' for reading", name) ; return (threadState->lastError = VL_ERR_PGM_IO) ; } err = vl_pgm_extract_head(f, im) ; if (err) { fclose (f) ; return err ; } if (vl_pgm_get_bpp(im) > 1) { snprintf(threadState->lastErrorMessage, VL_ERR_MSG_LEN, "vl_pgm_read(): PGM with BPP > 1 not supported") ; return (threadState->lastError = VL_ERR_BAD_ARG) ; } *data = (vl_uint8*)vl_malloc (vl_pgm_get_npixels(im) * sizeof(vl_uint8)) ; err = vl_pgm_extract_data(f, im, *data) ; if (err) { vl_free (data) ; fclose (f) ; } fclose (f) ; return err ; }
VlScaleSpace * vl_scalespace_new_with_geometry (VlScaleSpaceGeometry geom) { vl_index o ; vl_size numSublevels = geom.octaveLastSubdivision - geom.octaveFirstSubdivision + 1 ; vl_size numOctaves = geom.lastOctave - geom.firstOctave + 1 ; VlScaleSpace *self ; assert(is_valid_geometry(geom)) ; numOctaves = geom.lastOctave - geom.firstOctave + 1 ; numSublevels = geom.octaveLastSubdivision - geom.octaveFirstSubdivision + 1 ; self = vl_calloc(1, sizeof(VlScaleSpace)) ; if (self == NULL) goto err_alloc_self ; self->geom = geom ; self->octaves = vl_calloc(numOctaves, sizeof(float*)) ; if (self->octaves == NULL) goto err_alloc_octave_list ; for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) { VlScaleSpaceOctaveGeometry ogeom = vl_scalespace_get_octave_geometry(self,o) ; vl_size octaveSize = ogeom.width * ogeom.height * numSublevels ; self->octaves[o - self->geom.firstOctave] = vl_malloc(octaveSize * sizeof(float)) ; if (self->octaves[o - self->geom.firstOctave] == NULL) goto err_alloc_octaves; } return self ; err_alloc_octaves: for (o = self->geom.firstOctave ; o <= self->geom.lastOctave ; ++o) { if (self->octaves[o - self->geom.firstOctave]) { vl_free(self->octaves[o - self->geom.firstOctave]) ; } } err_alloc_octave_list: vl_free(self) ; err_alloc_self: return NULL ; }
int main (int argc, char** argv) { float * X ; float * Y ; vl_size numDimensions = 1000 ; vl_size numSamples = 2000 ; float * result = vl_malloc (sizeof(float) * numSamples * numSamples) ; VlFloatVectorComparisonFunction f ; init_data (numDimensions, numSamples, &X, &Y) ; X+=1 ; Y+=1 ; vl_set_simd_enabled (VL_FALSE) ; f = vl_get_vector_comparison_function_f (VlDistanceL2) ; vl_tic () ; vl_eval_vector_comparison_on_all_pairs_f (result, numDimensions, X, numSamples, Y, numSamples, f) ; VL_PRINTF("Float L2 distnace: %.3f s\n", vl_toc ()) ; vl_set_simd_enabled (VL_TRUE) ; f = vl_get_vector_comparison_function_f (VlDistanceL2) ; vl_tic () ; vl_eval_vector_comparison_on_all_pairs_f (result, numDimensions, X, numSamples, Y, numSamples, f) ; VL_PRINTF("Float L2 distance (SIMD): %.3f s\n", vl_toc ()) ; X-- ; Y-- ; vl_free (X) ; vl_free (Y) ; vl_free (result) ; return 0 ; }
VL_EXPORT vl_size vl_kdforest_query (VlKDForest * self, VlKDForestNeighbor * neighbors, vl_size numNeighbors, void const * query) { vl_uindex i, ti ; vl_bool exactSearch = (self->searchMaxNumComparisons == 0) ; VlKDForestSearchState * searchState ; vl_size numAddedNeighbors = 0 ; assert (neighbors) ; assert (numNeighbors > 0) ; assert (query) ; /* this number is used to differentiate a query from the next */ self -> searchId += 1 ; self -> searchNumRecursions = 0 ; if (! self -> searchHeapArray) { /* count number of tree nodes */ /* add support structures */ vl_size maxNumNodes = 0 ; for (ti = 0 ; ti < self->numTrees ; ++ti) { maxNumNodes += self->trees[ti]->numUsedNodes ; } self -> searchHeapArray = vl_malloc (sizeof(VlKDForestSearchState) * maxNumNodes) ; self -> searchIdBook = vl_calloc (sizeof(vl_uindex), self->numData) ; for (ti = 0 ; ti < self->numTrees ; ++ti) { double * searchBounds = vl_malloc(sizeof(double) * 2 * self->dimension) ; double * iter = searchBounds ; double * end = iter + 2 * self->dimension ; while (iter < end) { *iter++ = - VL_INFINITY_F ; *iter++ = + VL_INFINITY_F ; } vl_kdtree_calc_bounds_recursively (self->trees[ti], 0, searchBounds) ; vl_free (searchBounds) ; } } self->searchNumComparisons = 0 ; self->searchNumSimplifications = 0 ; /* put the root node into the search heap */ self->searchHeapNumNodes = 0 ; for (ti = 0 ; ti < self->numTrees ; ++ ti) { searchState = self->searchHeapArray + self->searchHeapNumNodes ; searchState -> tree = self->trees[ti] ; searchState -> nodeIndex = 0 ; searchState -> distanceLowerBound = 0 ; vl_kdforest_search_heap_push (self->searchHeapArray, &self->searchHeapNumNodes) ; } /* branch and bound */ while (exactSearch || self->searchNumComparisons < self->searchMaxNumComparisons) { /* pop the next optimal search node */ VlKDForestSearchState * searchState ; /* break if search space completed */ if (self->searchHeapNumNodes == 0) { break ; } searchState = self->searchHeapArray + vl_kdforest_search_heap_pop (self->searchHeapArray, &self->searchHeapNumNodes) ; /* break if no better solution may exist */ if (numAddedNeighbors == numNeighbors && neighbors[0].distance < searchState->distanceLowerBound) { self->searchNumSimplifications ++ ; break ; } vl_kdforest_query_recursively (self, searchState->tree, searchState->nodeIndex, neighbors, numNeighbors, &numAddedNeighbors, searchState->distanceLowerBound, query) ; } /* sort neighbors by increasing distance */ for (i = numAddedNeighbors ; i < numNeighbors ; ++ i) { neighbors[i].index = -1 ; neighbors[i].distance = VL_NAN_F ; } while (numAddedNeighbors) { vl_kdforest_neighbor_heap_pop (neighbors, &numAddedNeighbors) ; } return self->searchNumComparisons ; }
VL_EXPORT void vl_mser_ell_fit (VlMserFilt* f) { /* shortcuts */ int nel = f-> nel ; int dof = f-> dof ; int *dims = f-> dims ; int ndims = f-> ndims ; int *subs = f-> subs ; int njoins = f-> njoins ; vl_uint *joins = f-> joins ; VlMserReg *r = f-> r ; vl_uint *mer = f-> mer ; int nmer = f-> nmer ; vl_mser_acc *acc = f-> acc ; vl_mser_acc *ell = f-> ell ; int d, index, i, j ; /* already fit ? */ if (f->nell == f->nmer) return ; /* make room */ if (f->rell < f->nmer) { if (f->ell) vl_free (f->ell) ; f->ell = vl_malloc (sizeof(float) * f->nmer * f->dof) ; f->rell = f-> nmer ; } if (f->acc == 0) { f->acc = vl_malloc (sizeof(float) * f->nel) ; } acc = f-> acc ; ell = f-> ell ; /* ----------------------------------------------------------------- * Integrate moments * -------------------------------------------------------------- */ /* for each dof */ for(d = 0 ; d < f->dof ; ++d) { /* start from the upper-left pixel (0,0,...,0) */ memset (subs, 0, sizeof(int) * ndims) ; /* step 1: fill acc pretending that each region has only one pixel */ if(d < ndims) { /* 1-order ................................................... */ for(index = 0 ; index < nel ; ++ index) { acc [index] = subs [d] ; adv(ndims, dims, subs) ; } } else { /* 2-order ................................................... */ /* map the dof d to a second order moment E[x_i x_j] */ i = d - ndims ; j = 0 ; while(i > j) { i -= j + 1 ; j ++ ; } /* initialize acc with x_i * x_j */ for(index = 0 ; index < nel ; ++ index){ acc [index] = subs [i] * subs [j] ; adv(ndims, dims, subs) ; } } /* step 2: integrate */ for(i = 0 ; i < njoins ; ++i) { vl_uint index = joins [i] ; vl_uint parent = r [index] .parent ; acc [parent] += acc [index] ; } /* step 3: save back to ellpises */ for(i = 0 ; i < nmer ; ++i) { vl_uint idx = mer [i] ; ell [d + dof*i] = acc [idx] ; } } /* next dof */ /* ----------------------------------------------------------------- * Compute central moments * -------------------------------------------------------------- */ for(index = 0 ; index < nmer ; ++index) { float *pt = ell + index * dof ; vl_uint idx = mer [index] ; float area = r [idx] .area ; for(d = 0 ; d < dof ; ++d) { pt [d] /= area ; if(d >= ndims) { /* remove squared mean from moment to get variance */ i = d - ndims ; j = 0 ; while(i > j) { i -= j + 1 ; j ++ ; } pt [d] -= pt [i] * pt [j] ; } } } /* save back */ f-> nell = nmer ; }
/** ------------------------------------------------------------------- ** @brief Process image ** ** The functions calculates the Maximally Stable Extremal Regions ** (MSERs) of image @a im using the MSER filter @a f. ** ** The filter @a f must have been initialized to be compatible with ** the dimensions of @a im. ** ** @param f MSER filter. ** @param im image data. **/ VL_EXPORT void vl_mser_process (VlMserFilt* f, vl_mser_pix const* im) { /* shortcuts */ vl_uint nel = f-> nel ; vl_uint *perm = f-> perm ; vl_uint *joins = f-> joins ; int ndims = f-> ndims ; int *dims = f-> dims ; int *subs = f-> subs ; int *dsubs = f-> dsubs ; int *strides = f-> strides ; VlMserReg *r = f-> r ; VlMserExtrReg *er = f-> er ; vl_uint *mer = f-> mer ; int delta = f-> delta ; int njoins = 0 ; int ner = 0 ; int nmer = 0 ; int nbig = 0 ; int nsmall = 0 ; int nbad = 0 ; int ndup = 0 ; int i, j, k ; /* delete any previosuly computed ellipsoid */ f-> nell = 0 ; /* ----------------------------------------------------------------- * Sort pixels by intensity * -------------------------------------------------------------- */ { vl_uint buckets [ VL_MSER_PIX_MAXVAL ] ; /* clear buckets */ memset (buckets, 0, sizeof(vl_uint) * VL_MSER_PIX_MAXVAL ) ; /* compute bucket size (how many pixels for each intensity value) */ for(i = 0 ; i < (int) nel ; ++i) { vl_mser_pix v = im [i] ; ++ buckets [v] ; } /* cumulatively add bucket sizes */ for(i = 1 ; i < VL_MSER_PIX_MAXVAL ; ++i) { buckets [i] += buckets [i-1] ; } /* empty buckets computing pixel ordering */ for(i = nel ; i >= 1 ; ) { vl_mser_pix v = im [ --i ] ; vl_uint j = -- buckets [v] ; perm [j] = i ; } } /* initialize the forest with all void nodes */ for(i = 0 ; i < (int) nel ; ++i) { r [i] .parent = VL_MSER_VOID_NODE ; } /* ----------------------------------------------------------------- * Compute regions and count extremal regions * -------------------------------------------------------------- */ /* In the following: idx : index of the current pixel val : intensity of the current pixel r_idx : index of the root of the current pixel n_idx : index of the neighbors of the current pixel nr_idx : index of the root of the neighbor of the current pixel */ /* process each pixel by increasing intensity */ for(i = 0 ; i < (int) nel ; ++i) { /* pop next node xi */ vl_uint idx = perm [i] ; vl_mser_pix val = im [idx] ; vl_uint r_idx ; /* add the pixel to the forest as a root for now */ r [idx] .parent = idx ; r [idx] .shortcut = idx ; r [idx] .area = 1 ; r [idx] .height = 1 ; r_idx = idx ; /* convert the index IDX into the subscript SUBS; also initialize DSUBS to (-1,-1,...,-1) */ { vl_uint temp = idx ; for(k = ndims - 1 ; k >= 0 ; --k) { dsubs [k] = -1 ; subs [k] = temp / strides [k] ; temp = temp % strides [k] ; } } /* examine the neighbors of the current pixel */ while (1) { vl_uint n_idx = 0 ; vl_bool good = 1 ; /* Compute the neighbor subscript as NSUBS+SUB, the corresponding neighbor index NINDEX and check that the neighbor is within the image domain. */ for(k = 0 ; k < ndims && good ; ++k) { int temp = dsubs [k] + subs [k] ; good &= (0 <= temp) && (temp < dims [k]) ; n_idx += temp * strides [k] ; } /* The neighbor should be processed if the following conditions are met: 1. The neighbor is within image boundaries. 2. The neighbor is indeed different from the current node (the opposite happens when DSUB=(0,0,...,0)). 3. The neighbor is already in the forest, meaning that it has already been processed. */ if (good && n_idx != idx && r [n_idx] .parent != VL_MSER_VOID_NODE ) { vl_mser_pix nr_val = 0 ; vl_uint nr_idx = 0 ; /* Now we join the two subtrees rooted at R_IDX = ROOT( IDX) NR_IDX = ROOT(N_IDX). Note that R_IDX = ROOT(IDX) might change as we process more neighbors, so we need keep updating it. */ r_idx = climb(r, idx) ; nr_idx = climb(r, n_idx) ; int hgt = r [ r_idx] .height ; int n_hgt = r [nr_idx] .height ; /* At this point we have three possibilities: (A) ROOT(IDX) == ROOT(NR_IDX). In this case the two trees have already been joined and we do not do anything. (B) I(ROOT(IDX)) == I(ROOT(NR_IDX)). In this case the pixel IDX is extending an extremal region with the same intensity value. Since ROOT(NR_IDX) will NOT be an extremal region of the full image, ROOT(IDX) can be safely added as children of ROOT(NR_IDX) if this reduces the height according to the union rank heuristic. (C) I(ROOT(IDX)) > I(ROOT(NR_IDX)). In this case the pixel IDX is starting a new extremal region. Thus ROOT(NR_IDX) WILL be an extremal region of the final image and the only possibility is to add ROOT(NR_IDX) as children of ROOT(IDX), which becomes parent. */ if( r_idx != nr_idx ) { /* skip if (A) */ nr_val = im [nr_idx] ; if( nr_val == val && hgt < n_hgt ) { /* ROOT(IDX) becomes the child */ r [r_idx] .parent = nr_idx ; r [r_idx] .shortcut = nr_idx ; r [nr_idx] .area += r [r_idx] .area ; r [nr_idx] .height = VL_MAX(n_hgt, hgt+1) ; joins [njoins++] = r_idx ; } else { /* cases ROOT(IDX) becomes the parent */ r [nr_idx] .parent = r_idx ; r [nr_idx] .shortcut = r_idx ; r [r_idx] .area += r [nr_idx] .area ; r [r_idx] .height = VL_MAX(hgt, n_hgt + 1) ; joins [njoins++] = nr_idx ; /* count if extremal */ if (nr_val != val) ++ ner ; } /* check b vs c */ } /* check a vs b or c */ } /* neighbor done */ /* move to next neighbor */ k = 0 ; while(++ dsubs [k] > 1) { dsubs [k++] = -1 ; if(k == ndims) goto done_all_neighbors ; } } /* next neighbor */ done_all_neighbors : ; } /* next pixel */ /* the last root is extremal too */ ++ ner ; /* save back */ f-> njoins = njoins ; f-> stats. num_extremal = ner ; /* ----------------------------------------------------------------- * Extract extremal regions * -------------------------------------------------------------- */ /* Extremal regions are extracted and stored into the array ER. The structure R is also updated so that .SHORTCUT indexes the corresponding extremal region if any (otherwise it is set to VOID). */ /* make room */ if (f-> rer < ner) { if (er) vl_free (er) ; f->er = er = vl_malloc (sizeof(VlMserExtrReg) * ner) ; f->rer = ner ; } ; /* save back */ f-> nmer = ner ; /* count again */ ner = 0 ; /* scan all regions Xi */ for(i = 0 ; i < (int) nel ; ++i) { /* pop next node xi */ vl_uint idx = perm [i] ; vl_mser_pix val = im [idx] ; vl_uint p_idx = r [idx] .parent ; vl_mser_pix p_val = im [p_idx] ; /* is extremal ? */ vl_bool is_extr = (p_val > val) || idx == p_idx ; if( is_extr ) { /* if so, add it */ er [ner] .index = idx ; er [ner] .parent = ner ; er [ner] .value = im [idx] ; er [ner] .area = r [idx] .area ; /* link this region to this extremal region */ r [idx] .shortcut = ner ; /* increase count */ ++ ner ; } else { /* link this region to void */ r [idx] .shortcut = VL_MSER_VOID_NODE ; } } /* ----------------------------------------------------------------- * Link extremal regions in a tree * -------------------------------------------------------------- */ for(i = 0 ; i < ner ; ++i) { vl_uint idx = er [i] .index ; do { idx = r[idx] .parent ; } while (r[idx] .shortcut == VL_MSER_VOID_NODE) ; er[i] .parent = r[idx] .shortcut ; er[i] .shortcut = i ; } /* ----------------------------------------------------------------- * Compute variability of +DELTA branches * -------------------------------------------------------------- */ /* For each extremal region Xi of value VAL we look for the biggest * parent that has value not greater than VAL+DELTA. This is dubbed * `top parent'. */ for(i = 0 ; i < ner ; ++i) { /* Xj is the current region the region and Xj are the parents */ int top_val = er [i] .value + delta ; int top = er [i] .shortcut ; /* examine all parents */ while (1) { int next = er [top] .parent ; int next_val = er [next] .value ; /* Break if: * - there is no node above the top or * - the next node is above the top value. */ if (next == top || next_val > top_val) break ; /* so next could be the top */ top = next ; } /* calculate branch variation */ { int area = er [i ] .area ; int area_top = er [top] .area ; er [i] .variation = (float) (area_top - area) / area ; er [i] .max_stable = 1 ; } /* Optimization: since extremal regions are processed by * increasing intensity, all next extremal regions being processed * have value at least equal to the one of Xi. If any of them has * parent the parent of Xi (this comprises the parent itself), we * can safely skip most intermediate node along the branch and * skip directly to the top to start our search. */ { int parent = er [i] .parent ; int curr = er [parent] .shortcut ; er [parent] .shortcut = VL_MAX (top, curr) ; } } /* ----------------------------------------------------------------- * Select maximally stable branches * -------------------------------------------------------------- */ nmer = ner ; for(i = 0 ; i < ner ; ++i) { vl_uint parent = er [i ] .parent ; vl_mser_pix val = er [i ] .value ; float var = er [i ] .variation ; vl_mser_pix p_val = er [parent] .value ; float p_var = er [parent] .variation ; vl_uint loser ; /* Notice that R_parent = R_{l+1} only if p_val = val + 1. If not, this and the parent region coincide and there is nothing to do. */ if(p_val > val + 1) continue ; /* decide which one to keep and put that in loser */ if(var < p_var) loser = parent ; else loser = i ; /* make loser NON maximally stable */ if(er [loser] .max_stable) { -- nmer ; er [loser] .max_stable = 0 ; } } f-> stats. num_unstable = ner - nmer ; /* ----------------------------------------------------------------- * Further filtering * -------------------------------------------------------------- */ /* It is critical for correct duplicate detection to remove regions * from the bottom (smallest one first). */ { float max_area = (float) f-> max_area * nel ; float min_area = (float) f-> min_area * nel ; float max_var = (float) f-> max_variation ; float min_div = (float) f-> min_diversity ; /* scan all extremal regions (intensity value order) */ for(i = ner-1 ; i >= 0L ; --i) { /* process only maximally stable extremal regions */ if (! er [i] .max_stable) continue ; if (er [i] .variation >= max_var ) { ++ nbad ; goto remove ; } if (er [i] .area > max_area) { ++ nbig ; goto remove ; } if (er [i] .area < min_area) { ++ nsmall ; goto remove ; } /* * Remove duplicates */ if (min_div < 1.0) { vl_uint parent = er [i] .parent ; int area, p_area ; float div ; /* check all but the root mser */ if((int) parent != i) { /* search for the maximally stable parent region */ while(! er [parent] .max_stable) { vl_uint next = er [parent] .parent ; if(next == parent) break ; parent = next ; } /* Compare with the parent region; if the current and parent * regions are too similar, keep only the parent. */ area = er [i] .area ; p_area = er [parent] .area ; div = (float) (p_area - area) / (float) p_area ; if (div < min_div) { ++ ndup ; goto remove ; } } /* remove dups end */ } continue ; remove : er [i] .max_stable = 0 ; -- nmer ; } /* check next region */ f-> stats .num_abs_unstable = nbad ; f-> stats .num_too_big = nbig ; f-> stats .num_too_small = nsmall ; f-> stats .num_duplicates = ndup ; } /* ----------------------------------------------------------------- * Save the result * -------------------------------------------------------------- */ /* make room */ if (f-> rmer < nmer) { if (mer) vl_free (mer) ; f->mer = mer = vl_malloc( sizeof(vl_uint) * nmer) ; f->rmer = nmer ; } /* save back */ f-> nmer = nmer ; j = 0 ; for (i = 0 ; i < ner ; ++i) { if (er [i] .max_stable) mer [j++] = er [i] .index ; } }
void mexFunction(int nout, mxArray *out[], int nin, const mxArray *in[]) { enum {IN_FOREST = 0, IN_DATA, IN_QUERY, IN_END} ; enum {OUT_INDEX = 0, OUT_DISTANCE} ; int verbose = 0 ; int opt ; int next = IN_END ; mxArray const *optarg ; VlKDForest * forest ; mxArray const * forest_array = in[IN_FOREST] ; mxArray const * data_array = in[IN_DATA] ; mxArray const * query_array = in[IN_QUERY] ; mxArray * index_array ; mxArray * distance_array ; void * query ; vl_uint32 * index ; void * distance ; vl_size numNeighbors = 1 ; vl_size numQueries ; vl_uindex qi, ni; unsigned int numComparisons = 0 ; unsigned int maxNumComparisons = 0 ; VlKDForestNeighbor * neighbors ; mxClassID dataClass ; VL_USE_MATLAB_ENV ; /* ----------------------------------------------------------------- * Check the arguments * -------------------------------------------------------------- */ if (nin < 3) { vlmxError(vlmxErrNotEnoughInputArguments, NULL) ; } if (nout > 2) { vlmxError(vlmxErrTooManyOutputArguments, NULL) ; } forest = new_kdforest_from_array (forest_array, data_array) ; dataClass = mxGetClassID (data_array) ; if (mxGetClassID (query_array) != dataClass) { vlmxError(vlmxErrInvalidArgument, "QUERY must have the same storage class as DATA.") ; } if (! vlmxIsReal (query_array)) { vlmxError(vlmxErrInvalidArgument, "QUERY must be real.") ; } if (! vlmxIsMatrix (query_array, forest->dimension, -1)) { vlmxError(vlmxErrInvalidArgument, "QUERY must be a matrix with TREE.NUMDIMENSIONS rows.") ; } while ((opt = vlmxNextOption (in, nin, options, &next, &optarg)) >= 0) { switch (opt) { case opt_num_neighs : if (! vlmxIsScalar(optarg) || (numNeighbors = mxGetScalar(optarg)) < 1) { vlmxError(vlmxErrInvalidArgument, "NUMNEIGHBORS must be a scalar not smaller than one.") ; } break; case opt_max_num_comparisons : if (! vlmxIsScalar(optarg)) { vlmxError(vlmxErrInvalidArgument, "MAXNUMCOMPARISONS must be a scalar.") ; } maxNumComparisons = mxGetScalar(optarg) ; break; case opt_verbose : ++ verbose ; break ; } } vl_kdforest_set_max_num_comparisons (forest, maxNumComparisons) ; neighbors = vl_malloc (sizeof(VlKDForestNeighbor) * numNeighbors) ; query = mxGetData (query_array) ; numQueries = mxGetN (query_array) ; out[OUT_INDEX] = index_array = mxCreateNumericMatrix (numNeighbors, numQueries, mxUINT32_CLASS, mxREAL) ; out[OUT_DISTANCE] = distance_array = mxCreateNumericMatrix (numNeighbors, numQueries, dataClass, mxREAL) ; index = mxGetData (index_array) ; distance = mxGetData (distance_array) ; if (verbose) { VL_PRINTF ("vl_kdforestquery: number of queries: %d\n", numQueries) ; VL_PRINTF ("vl_kdforestquery: number of neighbors per query: %d\n", numNeighbors) ; VL_PRINTF ("vl_kdforestquery: max num of comparisons per query: %d\n", vl_kdforest_get_max_num_comparisons (forest)) ; } for (qi = 0 ; qi < numQueries ; ++ qi) { numComparisons += vl_kdforest_query (forest, neighbors, numNeighbors, query) ; switch (dataClass) { case mxSINGLE_CLASS: { float * distance_ = (float*) distance ; for (ni = 0 ; ni < numNeighbors ; ++ni) { *index++ = neighbors[ni].index + 1 ; *distance_++ = neighbors[ni].distance ; } query = (float*)query + vl_kdforest_get_data_dimension (forest) ; distance = distance_ ; break ; } case mxDOUBLE_CLASS: { double * distance_ = (double*) distance ; for (ni = 0 ; ni < numNeighbors ; ++ni) { *index++ = neighbors[ni].index + 1 ; *distance_++ = neighbors[ni].distance ; } query = (double*)query + vl_kdforest_get_data_dimension (forest) ; distance = distance_ ; break ; } default: abort() ; } } if (verbose) { VL_PRINTF ("vl_kdforestquery: number of comparisons per query: %.3f\n", ((double) numComparisons) / numQueries) ; VL_PRINTF ("vl_kdforestquery: number of comparisons per neighbor: %.3f\n", ((double) numComparisons) / (numQueries * numNeighbors)) ; } vl_kdforest_delete (forest) ; vl_free (neighbors) ; }
static void VL_XCAT(_vl_fisher_encode_, SFX) (TYPE * enc, TYPE const * means, vl_size dimension, vl_size numClusters, TYPE const * covariances, TYPE const * priors, TYPE const * data, vl_size numData, int flags) { vl_size dim; vl_index i_cl, i_d; TYPE * posteriors ; TYPE * sqrtInvSigma; posteriors = vl_malloc(sizeof(TYPE) * numClusters * numData); sqrtInvSigma = vl_malloc(sizeof(TYPE) * dimension * numClusters); memset(enc, 0, sizeof(TYPE) * 2 * dimension * numClusters) ; for (i_cl = 0 ; i_cl < (signed)numClusters ; ++i_cl) { for(dim = 0; dim < dimension; dim++) { sqrtInvSigma[i_cl*dimension + dim] = sqrt(1.0 / covariances[i_cl*dimension + dim]); } } VL_XCAT(vl_get_gmm_data_posteriors_, SFX)(posteriors, numClusters, numData, priors, means, dimension, covariances, data) ; #if defined(_OPENMP) #pragma omp parallel for default(shared) private(i_cl, i_d, dim) num_threads(vl_get_max_threads()) #endif for(i_cl = 0; i_cl < (signed)numClusters; ++ i_cl) { TYPE uprefix; TYPE vprefix; TYPE * uk = enc + i_cl*dimension ; TYPE * vk = enc + i_cl*dimension + numClusters * dimension ; if (priors[i_cl] < 1e-6) { continue ; } for(i_d = 0; i_d < (signed)numData; i_d++) { TYPE p = posteriors[i_cl + i_d * numClusters] ; if (p == 0) continue ; for(dim = 0; dim < dimension; dim++) { TYPE diff = data[i_d*dimension + dim] - means[i_cl*dimension + dim] ; diff *= sqrtInvSigma[i_cl*dimension + dim] ; *(uk + dim) += p * diff ; *(vk + dim) += p * (diff * diff - 1); } } uprefix = 1/(numData*sqrt(priors[i_cl])); vprefix = 1/(numData*sqrt(2*priors[i_cl])); for(dim = 0; dim < dimension; dim++) { *(uk + dim) = *(uk + dim) * uprefix; *(vk + dim) = *(vk + dim) * vprefix; } } vl_free(posteriors); vl_free(sqrtInvSigma) ; if (flags & VL_FISHER_FLAG_SQUARE_ROOT) { for(dim = 0; dim < 2 * dimension * numClusters ; dim++) { TYPE z = enc [dim] ; if (z >= 0) { enc[dim] = VL_XCAT(vl_sqrt_, SFX)(z) ; } else { enc[dim] = - VL_XCAT(vl_sqrt_, SFX)(- z) ; } } } if (flags & VL_FISHER_FLAG_NORMALIZED) { TYPE n = 0 ; for(dim = 0 ; dim < 2 * dimension * numClusters ; dim++) { TYPE z = enc [dim] ; n += z * z ; } n = VL_XCAT(vl_sqrt_, SFX)(n) ; n = VL_MAX(n, 1e-12) ; for(dim = 0 ; dim < 2 * dimension * numClusters ; dim++) { enc[dim] /= n ; } } }
static double VL_XCAT(_vl_kmeans_refine_centers_elkan_, SFX) (VlKMeans * self, TYPE const * data, vl_size numData) { vl_size d, iteration, x ; vl_uint32 c, j ; vl_bool allDone ; TYPE * distances = vl_malloc (sizeof(TYPE) * numData) ; vl_uint32 * assignments = vl_malloc (sizeof(vl_uint32) * numData) ; vl_size * clusterMasses = vl_malloc (sizeof(vl_size) * numData) ; #if (FLT == VL_TYPE_FLOAT) VlFloatVectorComparisonFunction distFn = vl_get_vector_comparison_function_f(self->distance) ; #else VlDoubleVectorComparisonFunction distFn = vl_get_vector_comparison_function_d(self->distance) ; #endif TYPE * nextCenterDistances = vl_malloc (sizeof(TYPE) * self->numCenters) ; TYPE * pointToClosestCenterUB = vl_malloc (sizeof(TYPE) * numData) ; vl_bool * pointToClosestCenterUBIsStrict = vl_malloc (sizeof(vl_bool) * numData) ; TYPE * pointToCenterLB = vl_malloc (sizeof(TYPE) * numData * self->numCenters) ; TYPE * newCenters = vl_malloc(sizeof(TYPE) * self->dimension * self->numCenters) ; TYPE * centerToNewCenterDistances = vl_malloc (sizeof(TYPE) * self->numCenters) ; vl_uint32 * permutations = NULL ; vl_size * numSeenSoFar = NULL ; double energy ; vl_size totDistanceComputationsToInit = 0 ; vl_size totDistanceComputationsToRefreshUB = 0 ; vl_size totDistanceComputationsToRefreshLB = 0 ; vl_size totDistanceComputationsToRefreshCenterDistances = 0 ; vl_size totDistanceComputationsToNewCenters = 0 ; vl_size totDistanceComputationsToFinalize = 0 ; if (self->distance == VlDistanceL1) { permutations = vl_malloc(sizeof(vl_uint32) * numData * self->dimension) ; numSeenSoFar = vl_malloc(sizeof(vl_size) * self->numCenters) ; VL_XCAT(_vl_kmeans_sort_data_helper_, SFX)(self, permutations, data, numData) ; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* Initialization */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* An iteration is: get_new_centers + reassign + get_energy. This counts as iteration 0, where get_new_centers is assumed to be performed before calling the train function by the initialization function */ /* update distances between centers */ totDistanceComputationsToInit += VL_XCAT(_vl_kmeans_update_center_distances_, SFX)(self) ; /* assigmen points to the initial centers and initialize bounds */ memset(pointToCenterLB, 0, sizeof(TYPE) * self->numCenters * numData) ; for (x = 0 ; x < numData ; ++x) { TYPE distance ; /* do the first center */ assignments[x] = 0 ; distance = distFn(self->dimension, data + x * self->dimension, (TYPE*)self->centers + 0) ; pointToClosestCenterUB[x] = distance ; pointToClosestCenterUBIsStrict[x] = VL_TRUE ; pointToCenterLB[0 + x * self->numCenters] = distance ; totDistanceComputationsToInit += 1 ; /* do other centers */ for (c = 1 ; c < self->numCenters ; ++c) { /* Can skip if the center assigned so far is twice as close as its distance to the center under consideration */ if (((self->distance == VlDistanceL1) ? 2.0 : 4.0) * pointToClosestCenterUB[x] <= ((TYPE*)self->centerDistances) [c + assignments[x] * self->numCenters]) { continue ; } distance = distFn(self->dimension, data + x * self->dimension, (TYPE*)self->centers + c * self->dimension) ; pointToCenterLB[c + x * self->numCenters] = distance ; totDistanceComputationsToInit += 1 ; if (distance < pointToClosestCenterUB[x]) { pointToClosestCenterUB[x] = distance ; assignments[x] = c ; } } } /* compute UB on energy */ energy = 0 ; for (x = 0 ; x < numData ; ++x) { energy += pointToClosestCenterUB[x] ; } if (self->verbosity) { VL_PRINTF("kmeans: Elkan iter 0: energy = %g, dist. calc. = %d\n", energy, totDistanceComputationsToInit) ; } /* #define SANITY*/ #ifdef SANITY { int xx ; int cc ; TYPE tol = 1e-5 ; VL_PRINTF("inconsistencies after initial assignments:\n"); for (xx = 0 ; xx < numData ; ++xx) { for (cc = 0 ; cc < self->numCenters ; ++cc) { TYPE a = pointToCenterLB[cc + xx * self->numCenters] ; TYPE b = distFn(self->dimension, data + self->dimension * xx, (TYPE*)self->centers + self->dimension * cc) ; if (cc == assignments[xx]) { TYPE z = pointToClosestCenterUB[xx] ; if (z+tol<b) VL_PRINTF("UB %d %d = %f < %f\n", cc, xx, z, b) ; } if (a>b+tol) VL_PRINTF("LB %d %d = %f > %f\n", cc, xx, a, b) ; } } } #endif /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* Iterations */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ for (iteration = 1 ; 1; ++iteration) { vl_size numDistanceComputationsToRefreshUB = 0 ; vl_size numDistanceComputationsToRefreshLB = 0 ; vl_size numDistanceComputationsToRefreshCenterDistances = 0 ; vl_size numDistanceComputationsToNewCenters = 0 ; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* Compute new centers */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ memset(clusterMasses, 0, sizeof(vl_size) * numData) ; for (x = 0 ; x < numData ; ++x) { clusterMasses[assignments[x]] ++ ; } switch (self->distance) { case VlDistanceL2: memset(newCenters, 0, sizeof(TYPE) * self->dimension * self->numCenters) ; for (x = 0 ; x < numData ; ++x) { TYPE * cpt = newCenters + assignments[x] * self->dimension ; TYPE const * xpt = data + x * self->dimension ; for (d = 0 ; d < self->dimension ; ++d) { cpt[d] += xpt[d] ; } } for (c = 0 ; c < self->numCenters ; ++c) { TYPE mass = clusterMasses[c] ; TYPE * cpt = newCenters + c * self->dimension ; for (d = 0 ; d < self->dimension ; ++d) { cpt[d] /= mass ; } } break ; case VlDistanceL1: for (d = 0 ; d < self->dimension ; ++d) { vl_uint32 * perm = permutations + d * numData ; memset(numSeenSoFar, 0, sizeof(vl_size) * self->numCenters) ; for (x = 0; x < numData ; ++x) { c = assignments[perm[x]] ; if (2 * numSeenSoFar[c] < clusterMasses[c]) { newCenters [d + c * self->dimension] = data [d + perm[x] * self->dimension] ; } numSeenSoFar[c] ++ ; } } break ; default: abort(); } /* done compute centers */ /* compute the distance from the old centers to the new centers */ for (c = 0 ; c < self->numCenters ; ++c) { TYPE distance = distFn(self->dimension, newCenters + c * self->dimension, (TYPE*)self->centers + c * self->dimension) ; centerToNewCenterDistances[c] = distance ; numDistanceComputationsToNewCenters += 1 ; } /* make the new centers current */ { TYPE * tmp = self->centers ; self->centers = newCenters ; newCenters = tmp ; } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* Reassign points to a centers */ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* Update distances between centers. */ numDistanceComputationsToRefreshCenterDistances += VL_XCAT(_vl_kmeans_update_center_distances_, SFX)(self) ; for (c = 0 ; c < self->numCenters ; ++c) { nextCenterDistances[c] = (TYPE) VL_INFINITY_D ; for (j = 0 ; j < self->numCenters ; ++j) { if (j == c) continue ; nextCenterDistances[c] = VL_MIN(nextCenterDistances[c], ((TYPE*)self->centerDistances) [j + c * self->numCenters]) ; } } /* Update upper bounds on point-to-closest-center distances based on the center variation. */ for (x = 0 ; x < numData ; ++x) { TYPE a = pointToClosestCenterUB[x] ; TYPE b = centerToNewCenterDistances[assignments[x]] ; if (self->distance == VlDistanceL1) { pointToClosestCenterUB[x] = a + b ; } else { #if (FLT == VL_TYPE_FLOAT) TYPE sqrtab = sqrtf (a * b) ; #else TYPE sqrtab = sqrt (a * b) ; #endif pointToClosestCenterUB[x] = a + b + 2.0 * sqrtab ; } pointToClosestCenterUBIsStrict[x] = VL_FALSE ; } /* Update lower bounds on point-to-center distances based on the center variation. */ for (x = 0 ; x < numData ; ++x) { for (c = 0 ; c < self->numCenters ; ++c) { TYPE a = pointToCenterLB[c + x * self->numCenters] ; TYPE b = centerToNewCenterDistances[c] ; if (a < b) { pointToCenterLB[c + x * self->numCenters] = 0 ; } else { if (self->distance == VlDistanceL1) { pointToCenterLB[c + x * self->numCenters] = a - b ; } else { #if (FLT == VL_TYPE_FLOAT) TYPE sqrtab = sqrtf (a * b) ; #else TYPE sqrtab = sqrt (a * b) ; #endif pointToCenterLB[c + x * self->numCenters] = a + b - 2.0 * sqrtab ; } } } } #ifdef SANITY { int xx ; int cc ; TYPE tol = 1e-5 ; VL_PRINTF("inconsistencies before assignments:\n"); for (xx = 0 ; xx < numData ; ++xx) { for (cc = 0 ; cc < self->numCenters ; ++cc) { TYPE a = pointToCenterLB[cc + xx * self->numCenters] ; TYPE b = distFn(self->dimension, data + self->dimension * xx, (TYPE*)self->centers + self->dimension * cc) ; if (cc == assignments[xx]) { TYPE z = pointToClosestCenterUB[xx] ; if (z+tol<b) VL_PRINTF("UB %d %d = %f < %f\n", cc, xx, z, b) ; } if (a>b+tol) VL_PRINTF("LB %d %d = %f > %f (assign = %d)\n", cc, xx, a, b, assignments[xx]) ; } } } #endif /* Scan the data and to the reassignments. Use the bounds to skip as many point-to-center distance calculations as possible. */ for (allDone = VL_TRUE, x = 0 ; x < numData ; ++x) { /* A point x sticks with its current center assignmets[x] the UB to d(x, c[assigmnets[x]]) is not larger than half the distance of c[assigments[x]] to any other center c. */ if (((self->distance == VlDistanceL1) ? 2.0 : 4.0) * pointToClosestCenterUB[x] <= nextCenterDistances[assignments[x]]) { continue ; } for (c = 0 ; c < self->numCenters ; ++c) { vl_uint32 cx = assignments[x] ; TYPE distance ; /* The point is not reassigned to a given center c if either: 0 - c is already the assigned center 1 - The UB of d(x, c[assignments[x]]) is smaller than half the distance of c[assigments[x]] to c, OR 2 - The UB of d(x, c[assignmets[x]]) is smaller than the LB of the distance of x to c. */ if (cx == c) { continue ; } if (((self->distance == VlDistanceL1) ? 2.0 : 4.0) * pointToClosestCenterUB[x] <= ((TYPE*)self->centerDistances) [c + cx * self->numCenters]) { continue ; } if (pointToClosestCenterUB[x] <= pointToCenterLB [c + x * self->numCenters]) { continue ; } /* If the UB is loose, try recomputing it and test again */ if (! pointToClosestCenterUBIsStrict[x]) { distance = distFn(self->dimension, data + self->dimension * x, (TYPE*)self->centers + self->dimension * cx) ; pointToClosestCenterUB[x] = distance ; pointToClosestCenterUBIsStrict[x] = VL_TRUE ; pointToCenterLB[cx + x * self->numCenters] = distance ; numDistanceComputationsToRefreshUB += 1 ; if (((self->distance == VlDistanceL1) ? 2.0 : 4.0) * pointToClosestCenterUB[x] <= ((TYPE*)self->centerDistances) [c + cx * self->numCenters]) { continue ; } if (pointToClosestCenterUB[x] <= pointToCenterLB [c + x * self->numCenters]) { continue ; } } /* Now the UB is strict (equal to d(x, assignments[x])), but we still could not exclude that x should be reassigned to c. We therefore compute the distance, update the LB, and check if a reassigmnet must be made */ distance = distFn(self->dimension, data + x * self->dimension, (TYPE*)self->centers + c * self->dimension) ; numDistanceComputationsToRefreshLB += 1 ; pointToCenterLB[c + x * self->numCenters] = distance ; if (distance < pointToClosestCenterUB[x]) { assignments[x] = c ; pointToClosestCenterUB[x] = distance ; allDone = VL_FALSE ; /* the UB strict flag is already set here */ } } /* assign center */ } /* next data point */ totDistanceComputationsToRefreshUB += numDistanceComputationsToRefreshUB ; totDistanceComputationsToRefreshLB += numDistanceComputationsToRefreshLB ; totDistanceComputationsToRefreshCenterDistances += numDistanceComputationsToRefreshCenterDistances ; totDistanceComputationsToNewCenters += numDistanceComputationsToNewCenters ; #ifdef SANITY { int xx ; int cc ; TYPE tol = 1e-5 ; VL_PRINTF("inconsistencies after assignments:\n"); for (xx = 0 ; xx < numData ; ++xx) { for (cc = 0 ; cc < self->numCenters ; ++cc) { TYPE a = pointToCenterLB[cc + xx * self->numCenters] ; TYPE b = distFn(self->dimension, data + self->dimension * xx, (TYPE*)self->centers + self->dimension * cc) ; if (cc == assignments[xx]) { TYPE z = pointToClosestCenterUB[xx] ; if (z+tol<b) VL_PRINTF("UB %d %d = %f < %f\n", cc, xx, z, b) ; } if (a>b+tol) VL_PRINTF("LB %d %d = %f > %f (assign = %d)\n", cc, xx, a, b, assignments[xx]) ; } } } #endif /* compute UB on energy */ energy = 0 ; for (x = 0 ; x < numData ; ++x) { energy += pointToClosestCenterUB[x] ; } if (self->verbosity) { vl_size numDistanceComputations = numDistanceComputationsToRefreshUB + numDistanceComputationsToRefreshLB + numDistanceComputationsToRefreshCenterDistances + numDistanceComputationsToNewCenters ; VL_PRINTF("kmeans: Elkan iter %d: energy <= %g, dist. calc. = %d\n", iteration, energy, numDistanceComputations) ; if (self->verbosity > 1) { VL_PRINTF("kmeans: Elkan iter %d: total dist. calc. per type: " "UB: %.1f%% (%d), LB: %.1f%% (%d), " "intra_center: %.1f%% (%d), " "new_center: %.1f%% (%d)\n", iteration, 100.0 * numDistanceComputationsToRefreshUB / numDistanceComputations, numDistanceComputationsToRefreshUB, 100.0 *numDistanceComputationsToRefreshLB / numDistanceComputations, numDistanceComputationsToRefreshLB, 100.0 * numDistanceComputationsToRefreshCenterDistances / numDistanceComputations, numDistanceComputationsToRefreshCenterDistances, 100.0 * numDistanceComputationsToNewCenters / numDistanceComputations, numDistanceComputationsToNewCenters) ; } } /* check termination conditions */ if (iteration >= self->maxNumIterations) { if (self->verbosity) { VL_PRINTF("kmeans: Elkan terminating because maximum number of iterations reached\n") ; } break ; } if (allDone) { if (self->verbosity) { VL_PRINTF("kmeans: Elkan terminating because the algorithm fully converged\n") ; } break ; } } /* next Elkan iteration */ /* compute true energy */ energy = 0 ; for (x = 0 ; x < numData ; ++ x) { vl_uindex cx = assignments [x] ; energy += distFn(self->dimension, data + self->dimension * x, (TYPE*)self->centers + self->dimension * cx) ; totDistanceComputationsToFinalize += 1 ; } { vl_size totDistanceComputations = totDistanceComputationsToInit + totDistanceComputationsToRefreshUB + totDistanceComputationsToRefreshLB + totDistanceComputationsToRefreshCenterDistances + totDistanceComputationsToNewCenters + totDistanceComputationsToFinalize ; double saving = (double)totDistanceComputations / (iteration * self->numCenters * numData) ; if (self->verbosity) { VL_PRINTF("kmeans: Elkan: total dist. calc.: %d (%.2f %% of Lloyd)\n", totDistanceComputations, saving * 100.0) ; } if (self->verbosity > 1) { VL_PRINTF("kmeans: Elkan: total dist. calc. per type: " "init: %.1f%% (%d), UB: %.1f%% (%d), LB: %.1f%% (%d), " "intra_center: %.1f%% (%d), " "new_center: %.1f%% (%d), " "finalize: %.1f%% (%d)\n", 100.0 * totDistanceComputationsToInit / totDistanceComputations, totDistanceComputationsToInit, 100.0 * totDistanceComputationsToRefreshUB / totDistanceComputations, totDistanceComputationsToRefreshUB, 100.0 *totDistanceComputationsToRefreshLB / totDistanceComputations, totDistanceComputationsToRefreshLB, 100.0 * totDistanceComputationsToRefreshCenterDistances / totDistanceComputations, totDistanceComputationsToRefreshCenterDistances, 100.0 * totDistanceComputationsToNewCenters / totDistanceComputations, totDistanceComputationsToNewCenters, 100.0 * totDistanceComputationsToFinalize / totDistanceComputations, totDistanceComputationsToFinalize) ; } } if (permutations) { vl_free(permutations) ; } if (numSeenSoFar) { vl_free(numSeenSoFar) ; } vl_free(distances) ; vl_free(assignments) ; vl_free(clusterMasses) ; vl_free(nextCenterDistances) ; vl_free(pointToClosestCenterUB) ; vl_free(pointToClosestCenterUBIsStrict) ; vl_free(pointToCenterLB) ; vl_free(newCenters) ; vl_free(centerToNewCenterDistances) ; return energy ; }