void MultiMatch_delete(MultiMatch* self) { if (self) { ArrayList_delete(self->keySets); KDTree_delete(self->globalKeyKD); ArrayList_delete(self->globalKeys); ArrayList_delete(self->globalMatches); PtrMap_delete(self->matchSets, NULL); ArrayList_delete(self->filteredMatchSets); free(self); } }
// Limited Best-Bin-First k-d-tree nearest neighbour search. // // (Using the algorithm described in the paper "Shape indexing using // approximate nearest-neighbour search in high-dimensional spaces", // available at http://www.cs.ubc.ca/spider/lowe/papers/cvpr97-abs.html) // // Find the approximate nearest neighbour to the hyperspace point 'target' // within the kd-tree using 'searchSteps' tail recursions at most (each // recursion deciding about one neighbours' fitness). // // After return 'resDist' contains the absolute distance of the // approximate nearest neighbour from the target point. The approximate // nearest neighbour is returned. SortedLimitedList* KDTree_NearestNeighbourListBBF (KDTree* self, IKDTreeDomain* target, int q, int searchSteps) { ArrayList* hrl = ArrayList_new0(HyperRectangle_delete); HyperRectangle* hr = HyperRectangle_CreateUniverseRectangle (IKDTreeDomain_GetDimensionCount(target)); ArrayList_AddItem(hrl, hr); SortedLimitedList* best = SortedLimitedList_new (q, KDTreeBestEntry_delete); best->comparator.compareTo = (int ( *)(IComparator *,const void *,const void *)) KDTreeBestEntry_CompareTo; SortedLimitedList* searchHr = SortedLimitedList_new (searchSteps, KDTreeHREntry_delete); searchHr->comparator.compareTo = (int ( *)(IComparator *,const void *,const void *))KDTreeHREntry_CompareTo; int dummyDist; /*IKDTreeDomain* nearest = */KDTree_NearestNeighbourListBBFI (self, best, q, target, hr, INT_MAX, &dummyDist, searchHr, &searchSteps, hrl); SortedLimitedList_delete(searchHr); int i; for(i=0; i<SortedLimitedList_Count(best); i++) { KDTreeBestEntry* be = (KDTreeBestEntry*) SortedLimitedList_GetItem(best, i); be->distance = sqrt ((double)be->distanceSq); } ArrayList_delete(hrl); return (best); }
SortedLimitedList* KDTree_NearestNeighbourList (KDTree* self, IKDTreeDomain* target, double* resDist, int q) { ArrayList* hrl = ArrayList_new0(HyperRectangle_delete); HyperRectangle* hr = HyperRectangle_CreateUniverseRectangle (IKDTreeDomain_GetDimensionCount(target)); ArrayList_AddItem(hrl, hr); SortedLimitedList* best = SortedLimitedList_new (q, KDTreeBestEntry_delete); best->comparator.compareTo = (int ( *)(IComparator *,const void *,const void *))KDTreeBestEntry_CompareTo; /*IKDTreeDomain* nearest = */KDTree_NearestNeighbourListI (self, best, q, target, hr, Double_PositiveInfinity, resDist, hrl); *resDist = sqrt (*resDist); int i; for(i=0; i<SortedLimitedList_Count(best); i++) { KDTreeBestEntry* be = (KDTreeBestEntry*) SortedLimitedList_GetItem(best, i); be->distance = sqrt (be->distance); } ArrayList_delete(hrl); return (best); }
int main (int argc, char* argv[]) { ArrayList* al = ArrayList_new0 (FilterPoint_delete); Random* rnd = Random_new0 (); int n; for (n = 0 ; n < 10 ; ++n) { FilterPoint* p = FilterPoint_new0 (); p->x = Random_NextDouble(rnd) * 400.0; p->y = Random_NextDouble(rnd) * 400.0; ArrayList_AddItem(al, p); } int i; for(i=0; i<ArrayList_Count(al); i++) { FilterPoint* p = ArrayList_GetItem(al, i); WriteLine ("%f %f # GNUPLOT1", p->x, p->y); } AreaFilter* conv = AreaFilter_new0 (); ArrayList* hull = AreaFilter_CreateConvexHull(conv, al); WriteLine ("\nhull: %d elements", ArrayList_Count(hull)); int j; for(j=0; j<ArrayList_Count(hull); j++) { FilterPoint* p = ArrayList_GetItem(hull, j); WriteLine ("%f %f # GNUPLOT2", p->x, p->y); } WriteLine ("\npolygon area: %f", AreaFilter_PolygonArea(conv, hull)); ArrayList_delete(al); return 0; }
void MatchSet_delete(MatchSet* self) { if (self) { ArrayList_delete(self->matches); KeypointXMLList_delete(self->keys1); KeypointXMLList_delete(self->keys2); free(self); } }
// Build a kd-tree from a list of elements. All elements must implement // the IKDTreeDomain interface and must have the same dimensionality. KDTree* KDTree_CreateKDTree (ArrayList* exset) { if (ArrayList_Count(exset) == 0) return (NULL); KDTree* cur = KDTree_new0 (); cur->dr = KDTree_GoodCandidate (exset, &cur->splitDim); ArrayList* leftElems = ArrayList_new0 (NULL); ArrayList* rightElems = ArrayList_new0 (NULL); // split the exemplar set into left/right elements relative to the // splitting dimension double bound = IKDTreeDomain_GetDimensionElement (cur->dr, cur->splitDim); int i; for(i=0; i<ArrayList_Count(exset); i++) { IKDTreeDomain* dom = (IKDTreeDomain*) ArrayList_GetItem(exset, i); // ignore the current element if (dom == cur->dr) continue; if (IKDTreeDomain_GetDimensionElement (dom, cur->splitDim) <= bound) { ArrayList_AddItem(leftElems, dom); } else { ArrayList_AddItem(rightElems, dom); } } // recurse cur->left = KDTree_CreateKDTree (leftElems); cur->right = KDTree_CreateKDTree (rightElems); ArrayList_delete(leftElems); ArrayList_delete(rightElems); return (cur); }
// Find the nearest neighbour to the hyperspace point 'target' within the // kd-tree. After return 'resDist' contains the absolute distance from the // target point. The nearest neighbour is returned. IKDTreeDomain* KDTree_NearestNeighbour (KDTree* self, IKDTreeDomain* target, double* resDist) { ArrayList* hrl = ArrayList_new0(HyperRectangle_delete); HyperRectangle* hr = HyperRectangle_CreateUniverseRectangle (IKDTreeDomain_GetDimensionCount(target)); ArrayList_AddItem(hrl, hr); IKDTreeDomain* nearest = KDTree_NearestNeighbourI (self, target, hr, Double_PositiveInfinity, resDist, hrl); *resDist = sqrt (*resDist); ArrayList_delete(hrl); return (nearest); }
ArrayList* MatchKeys_FindMatchesBBF (ArrayList* keys1, ArrayList* keys2) { // TODO: swap so smaller list is searched. ArrayList* al = ArrayList_new0 (NULL); int i; for (i=0; i<ArrayList_Count(keys2); i++) ArrayList_AddItem (al, ArrayList_GetItem(keys2, i)); KDTree* kd = KDTree_CreateKDTree (al); ArrayList_delete(al); ArrayList* matches = ArrayList_new0 (Match_delete); int j; for (j=0; j<ArrayList_Count(keys1); j++) { KeypointN* kp = (KeypointN*) ArrayList_GetItem(keys1, j); ArrayList* kpNNList = (ArrayList*)KDTree_NearestNeighbourListBBF (kd, (IKDTreeDomain*)kp, 2, 40); if (ArrayList_Count(kpNNList) < 2) FatalError ("BUG: less than two neighbours!"); KDTreeBestEntry* be1 = (KDTreeBestEntry*) ArrayList_GetItem(kpNNList, 0); KDTreeBestEntry* be2 = (KDTreeBestEntry*) ArrayList_GetItem(kpNNList, 1); if ((be1->distance / be2->distance) > 0.6) continue; KeypointN* kpN = (KeypointN*)KDTreeBestEntry_Neighbour(be1); ArrayList_AddItem(matches, Match_new (kp, kpN, be1->distance, be2->distance)); /* WriteLine ("(%d,%d) (%d,%d) %d, 2nd: %d", (int)(kp->x + 0.5), (int)(kp->y + 0.5), (int)(kpN->x + 0.5), (int)(kpN->y + 0.5), be1->distance, be2->distance); */ } return (matches); }
void _List_delete(_List *list) { ArrayList_delete(list->data); free(list); }
int main (int argc, char* argv[]) { WriteLine ("autopano-sift, Automatic panorama generation program\n"); if (argc+1 < 3) { Usage (); exit (1); } // output to stdout flag bool streamout = false; // Automatic pre-aligning of images bool preAlign = false; int bottomDefault = -1; int generateHorizon = 0; // Use RANSAC algorithm match filtration. bool useRansac = true; // Use area based weighting for final match selection. bool useAreaFiltration = true; // Truncate match coordinates to integer numbers. bool useIntegerCoordinates = false; // Use the absolute pathname of the image files in the output PTO // file. bool useAbsolutePathnames = false; // Use "keep-best" filtration, keep the maxMatches best. int maxMatches = 16; // default: 16 // Refinement options bool refine = false; bool refineMiddle = true; bool keepUnrefinable = true; int optionCount = 0; int optionN = 1; while (optionN < argc && strlen(argv[optionN]) >= 2 && argv[optionN][0] == '-' && argv[optionN][1] == '-') { char* optionStr = argv[optionN]; if (strcmp (optionStr, "--ransac") == 0) { useRansac = YesNoOption ("--ransac", argv[optionN + 1]); optionN += 2; } else if (strcmp (optionStr, "--maxmatches") == 0) { if (sscanf(argv[optionN + 1], "%d", &maxMatches) != 1) { WriteLine ("Parameter to maxmatches option invalid. See the usage help."); exit (1); } if (maxMatches < 0) { WriteLine ("Maximum number of matches must be positive or zero (unlimited)."); exit (1); } optionN += 2; } else if (strcmp (optionStr, "--disable-areafilter") == 0) { useAreaFiltration = false; optionN += 1; } else if (strcmp (optionStr, "--integer-coordinates") == 0) { useIntegerCoordinates = true; optionN += 1; } else if (strcmp (optionStr, "--absolute-pathnames") == 0) { useAbsolutePathnames = YesNoOption ("--absolute-pathnames", argv[optionN + 1]); optionN += 2; } else if (strcmp (optionStr, "--align") == 0) { preAlign = true; optionN += 1; } else if (strcmp (optionStr, "--bottom-is-left") == 0) { bottomDefault = 0; optionN += 1; } else if (strcmp (optionStr, "--bottom-is-right") == 0) { bottomDefault = 1; optionN += 1; } else if (strcmp (optionStr, "--generate-horizon") == 0) { if (sscanf(argv[optionN + 1], "%d", &generateHorizon) != 1) { WriteLine ("Parameter to generate-horizon option invalid. See the usage help."); exit (1); } if (generateHorizon < 0) { WriteLine ("The number of horizon lines to generate must be positive."); exit (1); } optionN += 2; } else if (strcmp (optionStr, "--refine") == 0) { refine = true; optionN += 1; } else if (strcmp (optionStr, "--refine-by-middle") == 0) { refineMiddle = true; optionN += 1; } else if (strcmp (optionStr, "--refine-by-mean") == 0) { refineMiddle = false; optionN += 1; } else if (strcmp (optionStr, "--keep-unrefinable") == 0) { keepUnrefinable = YesNoOption ("--keep-unrefinable", argv[optionN + 1]); optionN += 2; } else { WriteLine ("Usage error. Run \"autopano.exe\" without arguments for help."); exit (1); } } optionCount = optionN; // is there an output name and at least one input name? if( argc - optionN < 2 ){ WriteLine ("Error. Output name and at least one image name required."); Usage(); exit(1); } // next arg is either output file name or "-" for stdout // anything else beginning with '-' is an error if( argv[optionCount][0] == '-' ){ if( strcmp( argv[optionCount], "-" ) == 0 )streamout = true; else { WriteLine ("Option error. Run without arguments for help."); exit (1); } } if (bottomDefault != -1 && preAlign == false) { WriteLine ("Please enable automatic alignment (\"--align\") before using the"); WriteLine ("--bottom-is-* options. Thank you. Run \"autopano.exe\" without"); WriteLine ("arguments for usage help."); exit (1); } if (generateHorizon > 0 && preAlign == false) { WriteLine ("Please enable automatic alignment (\"--align\") before using the"); WriteLine ("--generate-horizon option. Thank you. Run \"autopano.exe\" without"); WriteLine ("arguments for usage help."); exit (1); } MultiMatch* mm = MultiMatch_new0 (); ArrayList* keyfiles = ArrayList_new0(NULL); int i; for( i=0; i<argc - 1 - optionCount; i++) { ArrayList_AddItem(keyfiles, argv[i+optionCount+1]); } WriteLine ("Loading keyfiles"); MultiMatch_LoadKeysets (mm, keyfiles); WriteLine ("\nMatching...%s", useRansac == true ? " RANSAC enabled" : ""); ArrayList* msList = MultiMatch_LocateMatchSets (mm, 3, maxMatches, useRansac, useAreaFiltration); // Connected component check WriteLine ("\nConnected component check..."); ArrayList* components = MultiMatch_ComponentCheck (mm, msList); WriteLine ("Connected component identification resulted in %d component%s:", ArrayList_Count(components), ArrayList_Count(components) > 1 ? "s" : ""); int compN = 1; int j; for(j=0; j<ArrayList_Count(components); j++) { Component* comp = (Component*) ArrayList_GetItem(components, j); char* compstr = Component_ToString(comp); WriteLine ("component %d: %s", compN++, compstr); free(compstr); } if (ArrayList_Count(components) > 1) { WriteLine (""); WriteLine ("Warning: There is one or more components that are not connected through control"); WriteLine (" points. An optimization of the resulting PTO will not be possible"); WriteLine (" without prior adding of control points between the components listed"); WriteLine (" above. Please see the manual page for autopano(1) for details."); WriteLine (""); } else WriteLine (""); // BondBall algorithm BondBall* bb = NULL; if (preAlign) { bb = MultiMatch_BuildBondBall (mm, msList, bottomDefault); if (bb == NULL) { WriteLine ("WARNING: Failed to build bondball as requested. No pre-aligning of images"); WriteLine (" takes place.\n"); } } if (refine) { WriteLine ("Refining keypoints"); RefineKeypoints (msList, refineMiddle, keepUnrefinable); } FILE* pto; if ( streamout ) { pto = stdout; } else { WriteLine ("Creating output file \"%s\"", argv[optionCount]); pto = fopen(argv[optionCount], "w"); } WritePTOFile (pto, mm, msList, bb, generateHorizon, useIntegerCoordinates, useAbsolutePathnames); ArrayList_delete(keyfiles); ArrayList_delete(components); BondBall_delete(bb); MultiMatch_delete(mm); if ( !streamout ) fclose(pto); return 0; }
void WritePTOFile (FILE* pto, MultiMatch* mm, ArrayList* msList, BondBall* bb, int generateHorizon, bool integerCoordinates, bool useAbsolutePathnames) { fprintf(pto, "# Hugin project file\n"); fprintf(pto, "# automatically generated by autopano-sift, available at\n"); fprintf(pto, "# http://cs.tu-berlin.de/~nowozin/autopano-sift/\n\n"); fprintf(pto, "p f2 w3000 h1500 v360 n\"JPEG q90\"\n"); fprintf(pto, "m g1 i0\n\n"); int imageIndex = 0; HashTable* imageNameTab = HashTable_new0 (NULL, NULL); ArrayList* resolutions = ArrayList_new0 (Resolution_delete); int i; for(i=0; i<ArrayList_Count(mm->keySets); i++) { KeypointXMLList* kx = (KeypointXMLList*) ArrayList_GetItem(mm->keySets, i); HashTable_AddItem(imageNameTab, kx->imageFile, (void*)imageIndex); ArrayList_AddItem(resolutions, Resolution_new (kx->xDim, kx->yDim)); char* imageFile = kx->imageFile; // If the resolution was already there, use the first image with // the exact same resolution as reference for camera-related // values. int refIdx; for (refIdx = 0 ; refIdx < (ArrayList_Count(resolutions) - 1) ; ++refIdx) { if (Resolution_CompareTo((Resolution*) ArrayList_GetItem(resolutions, refIdx), kx->xDim, kx->yDim) == 0) break; } if (refIdx == (ArrayList_Count(resolutions) - 1)) refIdx = -1; Position* pos = bb == NULL ? NULL : (Position*) HashTable_GetItem(bb->positions, imageFile); /* if (pos != NULL) { WriteLine ("yaw %g, pitch %g, rotation %g", pos->yaw, pos->pitch, pos->rotation); }*/ double yaw = 0.0, pitch = 0.0, rotation = 0.0; if (pos != NULL) { yaw = pos->yaw; pitch = pos->pitch; rotation = pos->rotation; } if (imageIndex == 0 || refIdx == -1) { fprintf(pto, "i w%d h%d f0 a0 b-0.01 c0 d0 e0 p%g r%g v180 y%g u10 n\"%s\"\n", kx->xDim, kx->yDim, pitch, rotation, yaw, imageFile); } else { fprintf(pto, "i w%d h%d f0 a=%d b=%d c=%d d0 e0 p%g r%g v=%d y%g u10 n\"%s\"\n", kx->xDim, kx->yDim, refIdx, refIdx, refIdx, pitch, rotation, refIdx, yaw, imageFile); } imageIndex += 1; } fprintf(pto, "\nv p1 r1 y1\n\n"); fprintf(pto, "# match list automatically generated\n"); int j; for(j=0; j<ArrayList_Count(msList); j++) { MatchSet* ms = (MatchSet*) ArrayList_GetItem(msList, j); int k; for(k=0; k<ArrayList_Count(ms->matches); k++) { Match* m = (Match*) ArrayList_GetItem(ms->matches, k); if (integerCoordinates == false) { fprintf(pto, "c n%d N%d x%.6f y%.6f X%.6f Y%.6f t0\n", (int)HashTable_GetItem(imageNameTab, ms->file1), (int)HashTable_GetItem(imageNameTab, ms->file2), m->kp1->x, m->kp1->y, m->kp2->x, m->kp2->y); } else { fprintf(pto, "c n%d N%d x%d y%d X%d Y%d t0\n", (int)HashTable_GetItem(imageNameTab, ms->file1), (int)HashTable_GetItem(imageNameTab, ms->file2), (int) (m->kp1->x + 0.5), (int) (m->kp1->y + 0.5), (int) (m->kp2->x + 0.5), (int) (m->kp2->y + 0.5)); } } } // Generate horizon if we should if (bb != NULL && generateHorizon > 0) { WriteLine ("Creating horizon..."); int kMain = 2; int hPoints = generateHorizon; int horizonPointsMade = 0; bool hasGood = true; while (hPoints > 0 && hasGood) { hasGood = false; int kStep = 2 * kMain; int p; for (p = 0 ; hPoints > 0 && p < kMain ; ++p) { double stepSize = ((double) ArrayList_Count(bb->firstRow)) / ((double) kStep); double beginIndex = p * stepSize; double endIndex = (((double) ArrayList_Count(bb->firstRow)) / (double) kMain) + p * stepSize; // Round to next integer and check if their image distance // is larger than 1. If its not, we skip over this useless // horizon point. int bi = (int) (beginIndex + 0.5); int ei = (int) (endIndex + 0.5); if ((ei - bi) <= 1) continue; hasGood = true; bi %= ArrayList_Count(bb->firstRow); ei %= ArrayList_Count(bb->firstRow); fprintf(pto, "c n%s N%s x%d y%d X%d Y%d t2\n", (char*)HashTable_GetItem(imageNameTab, ArrayList_GetItem(bb->firstRow, bi)), (char*)HashTable_GetItem(imageNameTab, ArrayList_GetItem(bb->firstRow, ei)), ((Resolution*) ArrayList_GetItem(resolutions,bi))->x / 2, ((Resolution*) ArrayList_GetItem(resolutions,bi))->y / 2, ((Resolution*) ArrayList_GetItem(resolutions,ei))->x / 2, ((Resolution*) ArrayList_GetItem(resolutions,ei))->y / 2); horizonPointsMade += 1; hPoints -= 1; } // Increase density for next generation lines kMain *= 2; } WriteLine (" made %d horizon lines.\n", horizonPointsMade); } fprintf(pto, "\n# :-)\n\n"); WriteLine ("\nYou can now load the output file into hugin."); WriteLine ("Notice: You absolutely must adjust the field-of-view value for the images"); ArrayList_delete(resolutions); HashTable_delete(imageNameTab); }
void SortedLimitedList_delete (SortedLimitedList* self) { ArrayList_delete(&self->base); }