From: <da...@us...> - 2007-05-29 23:51:21
|
Revision: 2029 http://hugin.svn.sourceforge.net/hugin/?rev=2029&view=rev Author: dangelo Date: 2007-05-29 16:51:18 -0700 (Tue, 29 May 2007) Log Message: ----------- moved files to trunk directory Added Paths: ----------- autopano-sift-C/trunk/AreaFilter.c autopano-sift-C/trunk/ArrayList.c autopano-sift-C/trunk/AutoPano.c autopano-sift-C/trunk/AutoPanoSift.h autopano-sift-C/trunk/BondBall.c autopano-sift-C/trunk/ChangeLog autopano-sift-C/trunk/DisplayImage.c autopano-sift-C/trunk/GaussianConvolution.c autopano-sift-C/trunk/GenerateKeys.c autopano-sift-C/trunk/HashTable.c autopano-sift-C/trunk/ImageMap.c autopano-sift-C/trunk/ImageMatchModel.c autopano-sift-C/trunk/KDTree.c autopano-sift-C/trunk/KeypointXML.c autopano-sift-C/trunk/LICENSE autopano-sift-C/trunk/LoweDetector.c autopano-sift-C/trunk/Makefile autopano-sift-C/trunk/MatchKeys.c autopano-sift-C/trunk/RANSAC.c autopano-sift-C/trunk/README autopano-sift-C/trunk/README.1ST autopano-sift-C/trunk/Random.c autopano-sift-C/trunk/ScaleSpace.c autopano-sift-C/trunk/SimpleMatrix.c autopano-sift-C/trunk/TestArray.c autopano-sift-C/trunk/TestKDTree.c autopano-sift-C/trunk/TestRandom.c autopano-sift-C/trunk/Transform.c autopano-sift-C/trunk/Utils.c autopano-sift-C/trunk/autopano-c-complete.sh autopano-sift-C/trunk/doc/ Copied: autopano-sift-C/trunk/AreaFilter.c (from rev 2027, autopano-sift-C/trunk/autopano-sift-C/AreaFilter.c) =================================================================== --- autopano-sift-C/trunk/AreaFilter.c (rev 0) +++ autopano-sift-C/trunk/AreaFilter.c 2007-05-29 23:51:18 UTC (rev 2029) @@ -0,0 +1,208 @@ + +/* autopano-sift, Automatic panorama image creation + * Copyright (C) 2004 -- Sebastian Nowozin + * + * This program is free software released under the GNU General Public + * License, which is included in this software package (doc/LICENSE). + */ + +/* AreaFilter.cs + * + * Area maximizing match filtration using convex hull and polygon-area + * maximization. + * + * (C) Copyright 2004 -- Sebastian Nowozin (no...@cs...) + * + * Convex hull code based on Ken Clarksons original C implementation. + * Polygon area formula by Paul Bourke. + */ + +#include "AutoPanoSift.h" + +#ifdef TEST_MAIN +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; +} + +#else + +AreaFilter* AreaFilter_new0() +{ + AreaFilter* self = (AreaFilter*)malloc(sizeof(AreaFilter)); + return self; +} + +void AreaFilter_delete(AreaFilter* self) +{ + if (self) { + free(self); + } +} + +// Measure the absolute area of a non self-intersecting polygon. +// Formula by Paul Bourke +// (http://astronomy.swin.edu.au/~pbourke/geometry/polyarea/) +// +// The input points must be ordered (clock- or counter-clockwise). The +// polygon is closed automatically between the first and the last point, +// so there should not be two same points in the polygon point set. +double AreaFilter_PolygonArea (AreaFilter* self, ArrayList* orderedPoints) +{ + double A = 0.0; + + int n; + for (n = 0 ; n < ArrayList_Count(orderedPoints) ; ++n) { + FilterPoint* p0 = ArrayList_GetItem(orderedPoints,n); + FilterPoint* p1 = ArrayList_GetItem(orderedPoints, (n + 1) % ArrayList_Count(orderedPoints)); + + A += p0->x * p1->y - p1->x * p0->y; + } + + A *= 0.5; + + return (abs (A)); +} + +// Create the convex hull of a point set. +ArrayList* AreaFilter_CreateConvexHull (AreaFilter* self, ArrayList* points) +{ + int chn = AreaFilter_CreateHull (self, points); + + ArrayList* hull = ArrayList_new0 (NULL); + int k; + for (k = 0 ; k < chn ; ++k) + ArrayList_AddItem(hull, ArrayList_GetItem (points, k)); + + return (hull); +} + +int AreaFilter_CompareLow (const FilterPoint* p1, const FilterPoint* p2) +{ + + double v = p1->x - p2->x; + if (v > 0) + return (1); + if (v < 0) + return (-1); + + v = p2->y - p1->y; + if (v > 0) + return (1); + if (v < 0) + return (-1); + + // Equal point + return (0); +} + +int AreaFilter_CompareHigh (const FilterPoint* p1, const FilterPoint* p2) +{ + return AreaFilter_CompareLow(p2, p1); +} + + +bool AreaFilter_ccw (ArrayList* points, int i, int j, int k) +{ + double a = ((FilterPoint*)ArrayList_GetItem(points, i))->x - ((FilterPoint*)ArrayList_GetItem(points, j))->x; + double b = ((FilterPoint*)ArrayList_GetItem(points, i))->y - ((FilterPoint*)ArrayList_GetItem(points, j))->y; + double c = ((FilterPoint*)ArrayList_GetItem(points, k))->x - ((FilterPoint*)ArrayList_GetItem(points, j))->x; + double d = ((FilterPoint*)ArrayList_GetItem(points, k))->y - ((FilterPoint*)ArrayList_GetItem(points, j))->y; + + return ((a * d - b * c) <= 0.0); +} + +int AreaFilter_MakeChain (AreaFilter* self, ArrayList* points, int (*comp)(const FilterPoint*, const FilterPoint*)) +{ + IComparator comparator; + comparator.compareTo = (void*)comp; + ArrayList_Sort(points, &comparator); + + int s = 1; + int pCount = ArrayList_Count(points); + int i; + for (i = 2 ; i < pCount ; ++i) { + int j; + for (j = s ; j >= 1 && AreaFilter_ccw (points, i, j, j - 1) ; --j) + ; + + s = j + 1; + + // Swap + FilterPoint* t = ArrayList_GetItem(points, s); + ArrayList_SetItem(points, s, ArrayList_GetItem(points, i)); + ArrayList_SetItem(points, i, t); + } + + return (s); +} + +int AreaFilter_CreateHull (AreaFilter* self, ArrayList* points) +{ + int u = AreaFilter_MakeChain (self, points, AreaFilter_CompareLow); + + if (ArrayList_Count(points) == 0) + return (0); + /* + int k; + for (k = 0 ; k < u ; ++k) + WriteLine ("point %d: %f %f", k, ((FilterPoint*)ArrayList_GetItem(points, k)[k])->x, + ((FilterPoint*)ArrayList_GetItem(points, k)[k])->y); + */ + + ArrayList* pointsHigh = ArrayList_new(ArrayList_Count(points)+1-u, NULL); + //WriteLine ("points.Length = %d, u = %d", ArrayList_Count(points), u); + + ArrayList_Copy (points, u, pointsHigh, 0, ArrayList_Count(points) - u); + ArrayList_SetItem(pointsHigh, ArrayList_Count(pointsHigh) - 1, ArrayList_GetItem(points, 0)); + + int h = AreaFilter_MakeChain (self, pointsHigh, AreaFilter_CompareHigh); + + int p; + for ( p = u ; p < ArrayList_Count(points) ; ++p) { + ArrayList_SetItem(points, p, ArrayList_GetItem(pointsHigh, p-u)); + } + + /* + WriteLine ("h = %d, u = %d", h, u); + int k; + for (k = 0 ; k < (h + u) ; ++k) + WriteLine ("point %d: %f %f", k, ((FilterPoint*)ArrayList_GetItem(points, k)[k])->x, + ((FilterPoint*)ArrayList_GetItem(points, k)[k])->y); + */ + + return (h + u); +} + +#endif + Copied: autopano-sift-C/trunk/ArrayList.c (from rev 2027, autopano-sift-C/trunk/autopano-sift-C/ArrayList.c) =================================================================== --- autopano-sift-C/trunk/ArrayList.c (rev 0) +++ autopano-sift-C/trunk/ArrayList.c 2007-05-29 23:51:18 UTC (rev 2029) @@ -0,0 +1,185 @@ +#include "AutoPanoSift.h" + +/* This is a minimal subset of the C# Container Class ArrayList */ + +void ArrayList_init(ArrayList* self, void* deletefn) +{ + self->count = 0; + self->reserved = 0; + self->items = NULL; + self->deletefn = deletefn; +} + +ArrayList* ArrayList_new0(void* deletefn) +{ + ArrayList* self = (ArrayList*)malloc(sizeof(ArrayList)); + ArrayList_init(self, deletefn); + return self; +} + +ArrayList* ArrayList_new(int dim, void* deletefn) +{ + ArrayList* self = ArrayList_new0(deletefn); + self->count = dim; + self->reserved = dim; + self->items = (void**)malloc(self->reserved * sizeof(void*)); + int index; + for(index=0; index<self->count; index++) { + self->items[index] = NULL; + } + return self; +} + +ArrayList* ArrayList_clone(ArrayList* self) +{ + ArrayList* cl = ArrayList_new0(self->deletefn); + cl->count = self->count; + cl->reserved = self->reserved; + cl->items = (void**)malloc(cl->reserved * sizeof(void*)); + memcpy(cl->items, self->items, self->count * sizeof(void*)); + return cl; +} + +void ArrayList_delete(ArrayList* self) +{ + if (self != NULL) { + if (self->items != NULL) { + if (self->deletefn != NULL) { + int index=0; + void (*_deletefn)(void*) = self->deletefn; + for(index=0; index<self->count; index++) { + if (self->items[index]) { + _deletefn(self->items[index]); + self->items[index] = NULL; + } + } + } + free(self->items); + self->items = NULL; + } + self->count = 0; + self->reserved = 0; + free(self); + } +} + +int ArrayList_Count(ArrayList* self) +{ + return self->count; +} + +void* ArrayList_GetItem(ArrayList* self, int index) +{ + if (index < 0 || index >= self->count) { + FatalError("Array out of bounds"); + return NULL; + } + return self->items[index]; +} + +void ArrayList_SetItem(ArrayList* self, int index, void* item) +{ + if (index < 0 || index >= self->count) { + WriteError("Array out of bounds"); + return; + } + self->items[index] = item; +} + +int ArrayList_IndexOf(ArrayList* self, void* item) +{ + int index; + for(index=0; index<self->count; index++) { + if( self->items[index] == item) { + return index; + } + } + return -1; +} + +bool ArrayList_Contains(ArrayList* self, void* item) +{ + return ArrayList_IndexOf(self, item) >= 0; +} + +void ArrayList_AddItem(ArrayList* self, void* item) +{ + if (self->count+1 > self->reserved) { + void* tmp = self->items; + self->reserved = self->reserved * 5 + 10; + self->items = (void**)malloc(self->reserved * sizeof(void*)); + if (tmp != NULL) { + memcpy(self->items, tmp, self->count * sizeof(void*)); + free(tmp); + } + } + self->items[self->count] = item; + self->count++; +} + +void ArrayList_AddRange(ArrayList* self, ArrayList* list) +{ + if (self->count+list->count > self->reserved) { + void* tmp = self->items; + self->reserved = self->count + list->count + 10; + self->items = (void**)malloc(self->reserved * sizeof(void*)); + if (tmp != NULL) { + memcpy(self->items, tmp, self->count * sizeof(void*)); + free(tmp); + } + } + memcpy(self->items+self->count, list->items, list->count * sizeof(void*)); + self->count += list->count; +} + +void ArrayList_Copy(ArrayList* self, int start, ArrayList* dest, int offset, int len) +{ + if (dest->reserved < len) { + if (dest->reserved>0) { + free(dest->items); + } + dest->reserved = len+10; + dest->items = (void*)malloc(dest->reserved * sizeof(void*)); + } + memcpy(dest->items, self->items+start, len*sizeof(void*)); +} + + +void ArrayList_RemoveItem(ArrayList* self, void* value) +{ + int index; + for(index=0; index<self->count; index++) { + if (self->items[index] == value) { + memmove(self->items+index, self->items+index+1, (self->count-(index+1))*sizeof(void*)); + self->count--; + return; + } + } +} + +void ArrayList_RemoveAt(ArrayList* self, int index) +{ + memmove(self->items+index, self->items+index+1, (self->count-(index+1))*sizeof(void*)); + self->count--; +} + +void ArrayList_RemoveRange(ArrayList* self, int start, int len) +{ + memmove(self->items+start, self->items+start+len, (self->count-(start+len))*sizeof(void*)); + self->count-=len; +} + +IComparator* ArrayList_comparator = NULL; + +int ArrayList_compareTo(const void** obj1, const void** obj2) { + return ArrayList_comparator->compareTo(ArrayList_comparator, *obj1, *obj2); +} + +void ArrayList_Sort(ArrayList* self, IComparator* cmp) { + if (ArrayList_comparator != NULL) { + FatalError("ArrayList_Sort called recursively"); + } + ArrayList_comparator = cmp; + qsort(self->items, self->count, sizeof(void*), ArrayList_compareTo); + ArrayList_comparator = NULL; +} Copied: autopano-sift-C/trunk/AutoPano.c (from rev 2027, autopano-sift-C/trunk/autopano-sift-C/AutoPano.c) =================================================================== --- autopano-sift-C/trunk/AutoPano.c (rev 0) +++ autopano-sift-C/trunk/AutoPano.c 2007-05-29 23:51:18 UTC (rev 2029) @@ -0,0 +1,707 @@ + +/* autopano-sift, Automatic panorama image creation + * Copyright (C) 2004 -- Sebastian Nowozin + * + * This program is free software released under the GNU General Public + * License, which is included in this software package (doc/LICENSE). + */ + +/* Autopano.cs + * + * Keypoint file correlation and hugin panorama file creation utility. + * + * (C) Copyright 2004 -- Sebastian Nowozin (no...@cs...) + * + * "The University of British Columbia has applied for a patent on the SIFT + * algorithm in the United States. Commercial applications of this software + * may require a license from the University of British Columbia." + * For more information, see the LICENSE file supplied with the distribution. + */ + +#include "AutoPanoSift.h" + +void Usage () +{ + WriteLine ("usage: autopano.exe [options] output.pto keys1.xml keys2.xml [..]\n"); + WriteLine ("Options"); + WriteLine (" --ransac <on|off|1|0> Switch RANSAC filtration on or off (default: on)"); + WriteLine (" --maxmatches <matches> Use no more than the given number of matches"); + WriteLine (" (default: 16, use zero for unlimited)"); + + WriteLine (" --disable-areafilter Do not use max-area filtration, which is default."); + WriteLine (" See manpage for details."); + WriteLine (" --integer-coordinates Truncate match coordinates to integer numbers."); + WriteLine (" --absolute-pathnames <on|off|1|0> Use the absolute pathname of the image"); + WriteLine (" file in the PTO output file. Disabled by default."); + WriteLine (""); + + WriteLine ("Alignment options"); + WriteLine (" --align Automatically pre-align images in PTO file."); + WriteLine (" --bottom-is-left"); + WriteLine (" --bottom-is-right Use in case the automatic algorithm fails."); + WriteLine (" --generate-horizon <c> Generate up to 'c' horizon lines."); + WriteLine (""); + + WriteLine ("Refinement options"); + WriteLine (" --refine Refine the found control points using the"); + WriteLine (" original images."); + WriteLine (" --refine-by-middle Use the best middle point to refine (default)."); + WriteLine (" --refine-by-mean Use the mean of the patches control points."); + WriteLine (" --keep-unrefinable <on|off|1|0>"); + WriteLine (" Keep unrefinable matches (default: on)."); + + WriteLine ("output.pto: The output PTO panorama project file."); + WriteLine (" The filename can be \"-\", then stdout is used"); + WriteLine ("key<n>.xml: The keypoint input files."); + WriteLine (" The input files can be gzip compressed, but require the \".gz\" extension\n then."); + WriteLine (""); + WriteLine ("Notice: for the aligning to work, the input images shall be"); + WriteLine (" 1. All of the same dimension and scale"); + WriteLine (" 2. The first images must be an ordered row. See manpage."); + WriteLine (""); +} + +typedef struct Resolution Resolution; +struct Resolution +{ + int x, y; +}; + +Resolution* Resolution_new0() +{ + Resolution* self = (Resolution*)malloc(sizeof(Resolution)); + return self; +} + +Resolution* Resolution_new(int x, int y) +{ + Resolution* self = Resolution_new0(); + self->x = x; + self->y = y; + return self; +} + +void Resolution_delete(Resolution* self) +{ + if (self) { + free(self); + } +} + +int Resolution_CompareTo (Resolution* self, int x, int y) +{ + + if (self->x == x && self->y == y) + return (0); + + return (1); +} + + + + +// The maximum radius to consider around a keypoint that is refined. That +// is, at most a patch of a maximum size of twice this value in both +// horizontal and vertical direction is extracted. +int RefinementRadiusMaximum = 96; +int (*refineHandler)(int index, int total); + + +void RefineKeypoints (ArrayList* msList, + bool selectMiddlePoint, bool neverLosePoints); +DisplayImage* ExtractPatch (DisplayImage* large, + int px, int py, double scale, int* radius); +ArrayList* ExtractKeypoints (DisplayImage* pic); +bool YesNoOption (char* optionName, char* val); + +// selectMiddlePoint: if true, select the middle point in the patch, +// otherwise build the mean +// neverLosePoints: if true, and if we cannot do the refinement, still use +// the control point. +void RefineKeypoints (ArrayList* msList, + bool selectMiddlePoint, bool neverLosePoints) +{ + DisplayImage* pic1 = NULL; + DisplayImage* pic2 = NULL; + char* pic1Name = NULL; + char* pic2Name = NULL; + + /* Keep stats for the refineHandler delegate + */ + int totalRefines = 0; + int doneRefines = 0; + int i; + for(i=0; i<ArrayList_Count(msList); i++) { + MatchSet* ms = ArrayList_GetItem(msList, i); + int j; + for(j=0; j<ArrayList_Count(ms->matches); j++) { + ArrayList_GetItem(ms->matches, j); + totalRefines += 1; + } + } + + + for(i=0; i<ArrayList_Count(msList); i++) { + MatchSet* ms = ArrayList_GetItem(msList, i); + WriteLine (" between \"%s\" and \"%s\"", + ms->file1, ms->file2); + + if (pic1Name != ms->file1) { + pic1Name = ms->file1; + pic1 = DisplayImage_new (ms->file1); + } + if (pic2Name != ms->file2) { + pic2Name = ms->file2; + pic2 = DisplayImage_new (ms->file2); + } + /*WriteLine ("pair: %s, %s, %d keypoint matches", + ms->file1, ms->file2, ArrayList_Count(ms->Matches));*/ + + ArrayList* refinedMatches = ArrayList_new0 (NULL); + + int j; + for(j=0; j<ArrayList_Count(ms->matches); j++) { + Match* m = ArrayList_GetItem(ms->matches, j); + + int p1x = (int) (m->kp1->x + 0.5); + int p1y = (int) (m->kp1->y + 0.5); + int p1radius; + DisplayImage* patch1 = ExtractPatch (pic1, p1x, p1y, + m->kp1->scale, &p1radius); + + int p2x = (int) (m->kp2->x + 0.5); + int p2y = (int) (m->kp2->y + 0.5); + int p2radius; + DisplayImage* patch2 = ExtractPatch (pic2, p2x, p2y, + m->kp2->scale, &p2radius); + + /* Call the refine handler delegate in case there is one to + * inform the callee of a single refining step (for progress + * bar displays and such). + */ + doneRefines += 1; + if (refineHandler != NULL) + refineHandler (doneRefines, totalRefines); + + // Skip over keypoint matches we cannot refine as part of the + // image lies outside. + if (patch1 == NULL || patch2 == NULL) { + if (neverLosePoints) + ArrayList_AddItem(refinedMatches, m); + DisplayImage_delete(patch1); + DisplayImage_delete(patch2); + continue; + } + + // Otherwise, run the SIFT algorithm on both small patches. + ArrayList* p1kp = ExtractKeypoints (patch1); + ArrayList* p2kp = ExtractKeypoints (patch2); + /*WriteLine ("p1kp = %d, p2kp = %d", ArrayList_Count(p1kp), + ArrayList_Count(p2kp));*/ + + // Apply the matching, RANSAC enabled. + MultiMatch* mm = MultiMatch_new0 (); + mm->verbose = false; + + ArrayList* matches = NULL; + matches = MultiMatch_TwoPatchMatch (mm, p1kp, + patch1->width, patch1->height, p2kp, patch2->width, + patch2->height, true); + DisplayImage_delete(patch1); + DisplayImage_delete(patch2); + + /* In case there are less than three keypoints in the + * two patches, we ignore them all. + */ + if (0 /*was exception ???*/ ) { + matches = NULL; + } + + if (matches == NULL || ArrayList_Count(matches) != 1) { + if (neverLosePoints) + ArrayList_AddItem(refinedMatches, m); + + continue; + } + + MatchSet* pSet = ArrayList_GetItem(matches, 0); + + // Now get the real new control point coordinates from the + // patches. We have two options and assume all points are + // equal quality-wise: + // a) Select the one that is most in the middle + // (selectMiddlePoint == true) + // b) Build the mean of all the control point matches in the + // patches (selectMiddlePoint == false). + double kp1X = 0.0; + double kp1Y = 0.0; + double kp2X = 0.0; + double kp2Y = 0.0; + double kpMidDist = Double_PositiveInfinity; + + int k; + for(k=0; k<ArrayList_Count(pSet->matches); k++) { + Match* pM = ArrayList_GetItem(pSet->matches, k); + if (selectMiddlePoint) { + double dist = sqrt ( + pow (pM->kp1->x - p1radius, 2.0) + + pow (pM->kp1->y - p1radius, 2.0)); + + if (dist < kpMidDist) { + kpMidDist = dist; + + kp1X = pM->kp1->x; + kp1Y = pM->kp1->y; + + kp2X = pM->kp2->x; + kp2Y = pM->kp2->y; + } + } else { + kp1X += pM->kp1->x; + kp1Y += pM->kp1->y; + + kp2X += pM->kp2->x; + kp2Y += pM->kp2->y; + } + + /*WriteLine ("(%g, %g) matches (%g, %g)", + pM->kp1->x, pM->kp1->y, pM->kp2->x, pM->kp2->y);*/ + } + + if (selectMiddlePoint == false) { + kp1X /= (double) ArrayList_Count(pSet->matches); + kp1Y /= (double) ArrayList_Count(pSet->matches); + kp2X /= (double) ArrayList_Count(pSet->matches); + kp2Y /= (double) ArrayList_Count(pSet->matches); + } + + kp1X += p1x - p1radius; + kp1Y += p1y - p1radius; + + kp2X += p2x - p2radius; + kp2Y += p2y - p2radius; + + Match* mn = Match_clone (m); + + // Adjust the original keypoints location to be the mean of + // all the highly precise superresolution points. + mn->kp1->x = kp1X; + mn->kp1->y = kp1Y; + + mn->kp2->x = kp2X; + mn->kp2->y = kp2Y; + + /*WriteLine ("MASTER POINT MATCH: (%g,%g) to (%g,%g)", + kp1X, kp1Y, kp2X, kp2Y);*/ + + ArrayList_AddItem(refinedMatches, mn); + /* + DisplayImage_Save (patch1, "patch-1.jpg"); + DisplayImage_Save (patch2, "patch-2.jpg"); + exit (0); + */ + } + + ms->matches = refinedMatches; + } +} + +/** Extract a small image patch from a larger image, centered at the given + * coordinates. + */ +DisplayImage* ExtractPatch (DisplayImage* large, + int px, int py, double scale, int* radius) +{ + *radius = (int) (9.0 * scale + 0.5); + if (*radius > RefinementRadiusMaximum) + *radius = RefinementRadiusMaximum; + + /*WriteLine ("patch centered at (%d,%d), scale %g, radius = %d", + px, py, scale, *radius);*/ + + int pxe = px + *radius; + int pye = py + *radius; + px -= *radius; + py -= *radius; + + if (px < 0 || py < 0 || pxe >= large->width || pye >= large->height) { + /*WriteLine (" (%d,%d)-(%d,%d) out of (0,0)-(%d,%d)", + px, py, pxe, pye, large->width, large->height);*/ + + return (NULL); + } else { + //WriteLine (" extracting patch"); + } + DisplayImage* patch = DisplayImage_Carve (large, px, py, *radius*2, *radius*2); + + return (patch); +} + +/** Produce keypoints for a small image patch. + */ +ArrayList* ExtractKeypoints (DisplayImage* pic) +{ + ImageMap* picMap = DisplayImage_ConvertToImageMap (pic); + + LoweFeatureDetector* lf = LoweFeatureDetector_new0 (); + LoweFeatureDetector_SetPrintWarning(false); + LoweFeatureDetector_SetVerbose(false); + LoweFeatureDetector_DetectFeatures (lf, picMap); + + ArrayList* res = LoweFeatureDetector_GlobalNaturalKeypoints(lf); + lf->globalNaturalKeypoints = NULL; // Make sure res won't get deleted. + LoweFeatureDetector_delete(lf); + return res; +} + +bool YesNoOption (char* optionName, char* val) +{ + if (strcmp (val, "1") == 0 || strcmp (val, "on") == 0) + return (true); + else if (strcmp (val, "0") == 0 || strcmp (val, "off") == 0) + return (false); + + FatalError ("'%s' is no valid truth value for option '%s'. Please see manpage for help.", + val, optionName); + return false; +} + +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 = 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(ArrayList_GetItem(resolutions, refIdx), kx->xDim, kx->yDim) == 0) + break; + } + if (refIdx == (ArrayList_Count(resolutions) - 1)) + refIdx = -1; + + Position* pos = bb == NULL ? NULL : + 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 = ArrayList_GetItem(msList, j); + int k; + for(k=0; k<ArrayList_Count(ms->matches); k++) { + Match* m = 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); +} + + +int main (int argc, char* argv[]) +{ + WriteLine ("autopano-sift, Automatic panorama generation program\n"); + + if (argc+1 < 3) { + Usage (); + exit (1); + } + + // 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] == '-') + { + char* optionStr = argv[optionN]; + + if (optionStr[1] != '-') { + Usage (); + exit (1); + } + + 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; + + 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 = 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 (argv[optionCount] == "-") { + 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 (argv[optionCount] != "-") + fclose(pto); + return 0; +} + Copied: autopano-sift-C/trunk/AutoPanoSift.h (from rev 2027, autopano-sift-C/trunk/autopano-sift-C/AutoPanoSift.h) =================================================================== --- autopano-sift-C/trunk/AutoPanoSift.h (rev 0) +++ autopano-sift-C/trunk/AutoPanoSift.h 2007-05-29 23:51:18 UTC (rev 2029) @@ -0,0 +1,742 @@ +#ifdef __cplusplus +extern "C" { +#endif +#include "pano12/filter.h" +#include "pano12/panorama.h" +#include "math.h" +#ifdef __cplusplus +} +#endif + +#define bool int +#define true 1 +#define false 0 +#define min(x,y) ((x)<(y)?(x):(y)) +#define max(x,y) ((x)>(y)?(x):(y)) +#define abs(x) ((x)>0?(x):(-x)) +#define Double_PositiveInfinity (1e+308) +#define Double_NegativeInfinity (-1e+308) + +void Write(char* fmt, ...); +void WriteLine(char* fmt, ...); +void WriteError(char* fmt, ...); +void FatalError(char* fmt, ...); + +char* FileNameToFullPath(char* filename); +char* FullPathToFileName(char* fullpath); + + +int** IntMap_new(int xDim, int yDim); +void IntMap_delete(int** self); + +double** DoubleMap_new(int xDim, int yDim); +void DoubleMap_delete(double** self); + +void*** PtrMap_new(int xDim, int yDim); +void PtrMap_delete(void*** self, void* deletefn); + +typedef struct Random Random; +struct Random { + int seed; +}; +Random* Random_new0(); +void Random_delete(Random*); +int Random_Next(Random*, int min, int max); +double Random_NextDouble(Random*); + + +typedef struct SimpleMatrix SimpleMatrix; +struct SimpleMatrix { + int xDim; + int yDim; + double** values; +}; +SimpleMatrix* SimpleMatrix_new0(); +SimpleMatrix* SimpleMatrix_new(int,int); +void SimpleMatrix_init(SimpleMatrix* self, int,int); +void SimpleMatrix_delete(SimpleMatrix*); +SimpleMatrix* SimpleMatrix_clone (SimpleMatrix* self); +double SimpleMatrix_GetValue(SimpleMatrix* self, int y, int x); +void SimpleMatrix_SetValue(SimpleMatrix* self, int y, int x, double value ); +SimpleMatrix* SimpleMatrix_Mul(SimpleMatrix* m1, SimpleMatrix* m2); +double SimpleMatrix_Dot (SimpleMatrix* self, SimpleMatrix* m); +void SimpleMatrix_Negate (SimpleMatrix* self); +void SimpleMatrix_Inverse (SimpleMatrix* self); +void SimpleMatrix_SolveLinear (SimpleMatrix* self, SimpleMatrix* vec); +void SimpleMatrix_SwapRow (SimpleMatrix* self, int r1, int r2); +char* SimpleMatrix_ToString (SimpleMatrix* self); + + +typedef struct ArrayList ArrayList; +struct ArrayList { + int count; + int reserved; + void** items; + void* deletefn; + }; +ArrayList* ArrayList_new0(void* deletefn); +void ArrayList_init(ArrayList* self, void* deletefn); +ArrayList* ArrayList_new(int count, void* deletefn); +void ArrayList_delete(ArrayList* self); +ArrayList* ArrayList_clone(ArrayList* self); +int ArrayList_Count(ArrayList* self); +void* ArrayList_GetItem(ArrayList* self, int index); +void ArrayList_SetItem(ArrayList* self, int index, void* value); +void ArrayList_AddItem(ArrayList* self, void* value); +void ArrayList_AddRange(ArrayList* self, ArrayList* list); +void ArrayList_Copy(ArrayList* self, int start, ArrayList* dest, int offset, int len); +void ArrayList_RemoveAt(ArrayList* self, int index); +void ArrayList_RemoveItem(ArrayList* self, void* value); +void ArrayList_RemoveRange(ArrayList* self, int start, int count); +bool ArrayList_Contains(ArrayList* self, void* value); +int ArrayList_IndexOf(ArrayList* self, void* value); +typedef struct IComparator IComparator; +struct IComparator { + int (*compareTo)(IComparator* self, const void*, const void*); +}; +void ArrayList_Sort(ArrayList* self, IComparator*); + + +typedef struct HashTable HashTable; +struct HashTable { + ArrayList* keys; + ArrayList* values; +}; +HashTable* HashTable_new0(void* delete_key, void* delete_value); +void HashTable_delete(HashTable* self); +void HashTable_AddItem(HashTable* self, void* key, void* value); +void* HashTable_GetItem(HashTable* self, void* key); +void HashTable_SetItem(HashTable* self, void* key, void* value); +bool HashTable_Contains(HashTable* self, void* key); + +typedef struct SortedLimitedList SortedLimitedList; +struct SortedLimitedList { + ArrayList base; + int max; + IComparator comparator; + void (*deletefn)(void*); +}; +SortedLimitedList* SortedLimitedList_new0 (); +SortedLimitedList* SortedLimitedList_new (int, void* deletefn); +void SortedLimitedList_delete (SortedLimitedList* self); +void SortedLimitedList_SetItem (SortedLimitedList* self, int idx, void* value); +int SortedLimitedList_Count (SortedLimitedList* self); +void* SortedLimitedList_GetItem (SortedLimitedList* self, int i); +void SortedLimitedList_RemoveAt (SortedLimitedList* self, int i); +int SortedLimitedList_AddItem (SortedLimitedList* self, void* value); + + +typedef struct IKDTreeDomain IKDTreeDomain; +typedef struct KDTreeBestEntry KDTreeBestEntry; +typedef struct KDTreeHREntry KDTreeHREntry; +typedef struct HyperRectangle HyperRectangle; +typedef struct KDTree KDTree; + +// The interface to be implemented by all data elements within the +// kd-tree. As every element is represented in domain space by a +// multidimensional vector, the interface provides readonly methods to +// access into this vector and to get its dimension. + +struct IKDTreeDomain { + int (*getDimensionCount)(IKDTreeDomain* self); + int (*getDimensionElement)(IKDTreeDomain* self, int dim); +}; +int IKDTreeDomain_GetDimensionCount(IKDTreeDomain* self); +int IKDTreeDomain_GetDimensionElement(IKDTreeDomain* self, int dim); + + +struct KDTreeHREntry { + double dist; + HyperRectangle* rect; + IKDTreeDomain* pivot; + KDTree* tree; +}; + +struct HyperRectangle { + int* leftTop; + int* rightBottom; + int dim; + int ref; +}; + +HyperRectangle* HyperRectangle_new0(); +void HyperRectangle_delete(HyperRectangle* self); +HyperRectangle* HyperRectangle_new (int dim); +HyperRectangle* HyperRectangle_clone (HyperRectangle* self); +HyperRectangle* HyperRectangle_ref (HyperRectangle* self); +void HyperRectangle_unref (HyperRectangle* self); +HyperRectangle* HyperRectangle_CreateUniverseRectangle (int dim); +double HyperRectangle_Distance (HyperRectangle* self, IKDTreeDomain* target); +bool HyperRectangle_IsInReach (HyperRectangle* self, IKDTreeDomain* target, double distRad); + +struct KDTreeBestEntry { + // Distance between this neighbour point and the target point. + double distance; + int distanceSq; + bool squared; + // The neighbour. + IKDTreeDomain* neighbour; + +}; +IKDTreeDomain* KDTreeBestEntry_Neighbour(KDTreeBestEntry* self); +void KDTreeBestEntry_delete(KDTreeBestEntry* self); + +struct KDTree { + // The current element + IKDTreeDomain* dr; + + // The splitting dimension for subtrees. + int splitDim; + + // The left and the right kd-subtree. + KDTree* left; + KDTree* right; +}; +int KDTree_DistanceSq (IKDTreeDomain* t1, IKDTreeDomain* t2); +IKDTreeDomain* KDTree_GoodCandidate (ArrayList* exset, int* splitDim); +KDTree* KDTree_CreateKDTree (ArrayList* exset); +KDTree* KDTree_new0 (); +void KDTree_delete (KDTree* self); +IKDTreeDomain* KDTree_NearestNeighbour (KDTree* self, IKDTreeDomain* target, double* resDist); +IKDTreeDomain* KDTree_NearestNeighbourI (KDTree* self, IKDTreeDomain* target, HyperRectangle* hr, + double maxDistSq, double* resDistSq, ArrayList* hrl); +SortedLimitedList* KDTree_NearestNeighbourList (KDTree* self, IKDTreeDomain* target, + double* resDist, int q); +IKDTreeDomain* KDTree_NearestNeighbourListI (KDTree* self, SortedLimitedList* best, + int q, IKDTreeDomain* target, HyperRectangle* hr, double maxDistSq, + double* resDistSq, ArrayList* hrl); +SortedLimitedList* KDTree_NearestNeighbourListBBF (KDTree* self, IKDTreeDomain* target, + int q, int searchSteps); +IKDTreeDomain* KDTree_NearestNeighbourListBBFI (KDTree* self, SortedLimitedList* best, + int q, IKDTreeDomain* target, HyperRectangle* hr, int maxDistSq, + int* resDistSq, SortedLimitedList* searchHr, int* searchSteps, + ArrayList* hrl); + +typedef struct Image DisplayImage; + +struct ImageMap { + int xDim; + int yDim; + double** values; +}; +typedef struct ImageMap ImageMap; + +DisplayImage* DisplayImage_new0(); +DisplayImage* DisplayImage_new(char* filename); +void DisplayImage_delete(Image* self); +double DisplayImage_ScaleWithin(DisplayImage* self, int); +ImageMap* DisplayImage_ConvertToImageMap(DisplayImage*); +DisplayImage* DisplayImage_Carve(DisplayImage* self, int x, int y, int w, int h); + +ImageMap* ImageMap_new0(); +ImageMap* ImageMap_new(int width, int height); +void ImageMap_delete(ImageMap* self); +void ImageMap_Save(ImageMap* self, char* filename, char* comment); +double ImageMap_ScaleWithin(ImageMap* self, int); +ImageMap* ImageMap_ScaleDouble(ImageMap* self); +ImageMap* ImageMap_ScaleHalf(ImageMap* self); +ImageMap* ImageMap_GaussianConvolution(ImageMap* self, double); +void ImageMap_SetPixel(ImageMap* self, int x, int y, double val); +double ImageMap_GetPixel(ImageMap* self, int x, int y); +ImageMap* ImageMap_Add(ImageMap* f1, ImageMap* f2); +ImageMap* ImageMap_Sub(ImageMap* f1, ImageMap* f2); +ImageMap* ImageMap_Mul(ImageMap* f1, ImageMap* f2); +void ImageMap_Normalize(ImageMap* self); +ImageMap* ImageMap_GaussianConvolution(ImageMap* self, double sigma); + + +typedef struct ConvLinearMask ConvLinearMask; +struct ConvLinearMask { + int Dim; + int Middle; + double* items; + double MaskSum; +}; + +typedef struct GaussianConvolution GaussianConvolution; +struct GaussianConvolution { + ConvLinearMask* mask; +}; + +GaussianConvolution* GaussianConvolution_new0 (); +void GaussianConvolution_delete (GaussianConvolution* ); +GaussianConvolution* GaussianConvolution_new1 (double sigma); +GaussianConvolution* GaussianConvolution_new2 (double sigma, int dim); +ImageMap* GaussianConvolution_Convolve (GaussianConvolution*, ImageMap*); + +ImageMap* ConvolutionFilter_Convolve (ImageMap* img, ConvLinearMask* mask); +void ConvolutionFilter_Convolve1D (ImageMap* dest, ConvLinearMask* mask, + ImageMap* src, int dir); +double ConvolutionFilter_CalculateConvolutionValue1D (ImageMap* src, + ConvLinearMask* mask, int n, int p, int maxN, int maxP, int dir); + + +typedef struct IRANSACModel IRANSACModel; +struct IRANSACModel { + IRANSACModel* (*clone)(IRANSACModel*); + void (*deletefn)(IRANSACModel*); + + // Fit the model to the samples given. The number of samples is equal + // to or larger than the smallest number of points required for a fit + // ('n'). + // Return true if the fit can be done, false otherwise. + bool (*fitModel)(IRANSACModel*, ArrayList*); + + // Return the fitting error of a single point against the current + // model. + double (*fittingErrorSingle)(IRANSACModel*, void*); + + // Threshhold the given fit error of a point. + // Return true if the fitting error is small enough and the point is + // fitting. + // Return false if the point is not fitting. + bool (*threshholdPoint)(IRANSACModel*, double fitError); + + // The overall fitting error of all points in FittingGround. This + // value is calculated by averaging all individual fitting errors of + // the points in the FittingGround. + double (*getFittingErrorSum)(IRANSACModel*); + void (*setFittingErrorSum)(IRANSACModel*, double); + + // All the points used to fit. Has to be set explicitly. + ArrayList* (*getFittingGround)(IRANSACModel*); + void (*setFittingGround)(IRANSACModel*, ArrayList*); + int (*compareTo)(IComparator*, IRANSACModel*, IRANSACModel*); +}; + +bool IRANSACModel_FitModel(IRANSACModel*, ArrayList*); +IRANSACModel* IRANSACModel_clone(IRANSACModel*); +void IRANSACModel_delete(IRANSACModel* self); +double IRANSACModel_FittingErrorSingle(IRANSACModel*, void*); +bool IRANSACModel_ThreshholdPoint (IRANSACModel*, double); +void IRANSACModel_SetFittingErrorSum(IRANSACModel*, double); +double IRANSACModel_GetFittingErrorSum(IRANSACModel*); +void IRANSACModel_SetFittingGround(IRANSACModel*, ArrayList*); +void IRANSACModel_CompareTo(IComparator*, void*, void*); + + +typedef struct RANSAC RANSAC; +struct RANSAC { + // Smallest number of points to be able to fit the model. + int n; + + // The number of iterations required. + int k; +}; +RANSAC* RANSAC_new0(); +RANSAC* RANSAC_new(int n, int k); +void RANSAC_delete(RANSAC* ); +ArrayList* RANSAC_FindModels(RANSAC*, IRANSACModel*, ArrayList*, int); +ArrayList* RANSAC_Sort(RANSAC*); +int RANSAC_GetKFromGoodfraction (int n, double goodFraction, int sdM); + +typedef struct OctavePyramid OctavePyramid; +typedef struct DScaleSpace DScaleSpace; + +struct OctavePyramid { + bool Verbose; + // Holds DScaleSpace objects, ordered by descending image size. + ArrayList* octaves; +}; +OctavePyramid* OctavePyramid_new0(); +void OctavePyramid_delete(OctavePyramid* self); +int OctavePyramid_Count(OctavePyramid* self); +int OctavePyramid_BuildOctaves(OctavePyramid* self, ImageMap*, double, int, double, int); +DScaleSpace* OctavePyramid_GetScaleSpace(OctavePyramid* self, int); + + +struct PointLocalInformation { + // Sub-pixel offset relative from this point. In the range of [-0.5 ; 0.5] + double fineX, fineY; + // Relative scale adjustment to the base image scale + double scaleAdjust; + double dValue; +}; +typedef struct PointLocalInformation PointLocalInformation; +PointLocalInformation* PointLocalInformation_new0 (); +void PointLocalInformation_delete (PointLocalInformation* self); +PointLocalInformation* PointLocalInformation_new3 (double fineS, double fineX, double fineY); + + +// A single point in scale space, used in keypoint creation to describe an +// exact position in scalespace and additional information about that point. +// Should not be used outside. +typedef struct ScalePoint ScalePoint; +struct ScalePoint { + int x; + int y; + int level; + // Sub-pixel level information from the Localization step are put here + PointLocalInformation* local; +}; +ScalePoint* ScalePoint_new0 (); +void ScalePoint_delete (ScalePoint* self); +ScalePoint* ScalePoint_new3 (int x, int y, int level); + + +// A single keypoint, the final result of keypoint creation. Contains the +// keypoint descriptor and position. +typedef struct Keypoint Keypoint; +struct Keypoint { + ImageMap* image; + double y, x; + double imgScale; // The scale of the image the keypoint was found in + // The absolute keypoint scale, where 1.0 is the original input image + double scale; + double orientation; + // The actual keypoint descriptor. + bool hasFV; + double* featureVector; + int featureVectorLength; + int xDim, yDim, oDim; +}; + +Keypoint* Keypoint_new0(); +void Keypoint_delete(Keypoint* self); +Keypoint* Keypoint_new (ImageMap* image, double x, double y, double imgScale, + double kpScale, double orientation); +double Keypoint_FVGet (Keypoint* self, int xI, int yI, int oI); +void Keypoint_FVSet (Keypoint* self, int xI, int yI, int oI, double value); +int Keypoint_FVLinearDim(Keypoint* self); +double Keypoint_FVLinearGet (Keypoint* self, int idx); +void Keypoint_FVLinearSet (Keypoint* self, int idx, double value); +void Keypoint_CreateLinearVector (Keypoint* self, int dim); +void Keypoint_CreateVector (Keypoint* self, int xDim, int yDim, int oDim); + + +// A single normalized and natural number keypoint. Contains the descriptor, +// position and orientation. +typedef struct KeypointN KeypointN; +struct KeypointN { + IKDTreeDomain domain; + double x, y; + double scale; + double orientation; + + int dim; + int* descriptor; +}; +KeypointN* KeypointN_new0(); +void KeypointN_delete(KeypointN* self); +KeypointN* KeypointN_new(Keypoint* kp); +KeypointN* KeypointN_clone(KeypointN* self); +void KeypointN_CreateDescriptor(KeypointN* self); +int KeypointN_GetDimensionCount(KeypointN* self); +int KeypointN_GetDimensionElement(KeypointN* self, int n); + + +struct DScaleSpace { + bool Verbose; + DScaleSpace* Down; + DScaleSpace* Up; + + // The original gaussian blurred source image this level was started with. + // Needed for keypoint generation. + + ImageMap* baseImg; + double basePixScale; + + // The smoothed gaussian images, all the base (lower scale) relative to + // the DoG spaces below. + ArrayList* imgScaled; + + ArrayList* magnitudes; + ArrayList* directions; + + // The DoG spaces. + ArrayList* spaces; +}; +DScaleSpace* DScaleSpace_new0(); +void DScaleSpace_delete(DScaleSpace* self); +ImageMap* DScaleSpace_GetGaussianMap (DScaleSpace* self, int idx); +ImageMap* DScaleSpace_LastGaussianMap(DScaleSpace* self); +int DScaleSpace_Count(DScaleSpace* self); +ImageMap* DScaleSpace_Map(DScaleSpace* self, int idx); +ArrayList* DScaleSpace_GenerateKeypoints(DScaleSpace* self, ArrayList*, int, double); +ArrayList* DScaleSpace_GenerateKeypointSingle (DScaleSpace* self, + double imgScale, ScalePoint* point, + int binCount, double peakRelThresh, int scaleCount, + double octaveSigma); +bool DScaleSpace_InterpolateOrientation (DScaleSpace* self, + double left, double middle, + double right, double* degreeCorrection, double* peakValue); +int DScaleSpace_FindClosestRotationBin (DScaleSpace* self, int binCount, double angle); +void DScaleSpace_AverageWeakBins (DScaleSpace* self, double* bins, int binCount); +ArrayList* DScaleSpace_CreateDescriptors (DScaleSpace* self, + ArrayList* keypoints, + ImageMap* magnitude, ImageMap* direction, + double considerScaleFactor, int descDim, int directionCount, + double fvGradHicap); +void DScaleSpace_CapAndNormalizeFV (DScaleSpace* self, Keypoint* kp, double fvGradHicap); +bool DScaleSpace_IsInCircle (int rX, int rY, int radiusSq); +ArrayList* DScaleSpace_FilterAndLocalizePeaks (DScaleSpace* self, ArrayList* peaks, double edgeRatio, + double dValueLoThresh, double scaleAdjustThresh, int relocationMaximum); +bool DScaleSpace_LocalizeIsWeak (DScaleSpace* self, ScalePoint* point, int steps, int** processed); +bool DScaleSpace_IsTooEdgelike (DScaleSpace* self, ImageMap* space, int x, int y, double r); +SimpleMatrix* DScaleSpace_GetAdjustment (DScaleSpace* self, ScalePoint* point, + int level, int x, int y, double* dp); +ArrayList* DScaleSpace_FindPeaks(DScaleSpace* self, double); +ArrayList* DScaleSpace_FindPeaksThreeLevel (DScaleSpace* self, ImageMap* below, ImageMap* current, + ImageMap* above, int curLev, double dogThresh); +void DScaleSpace_GenerateMagnitudeAndDirectionMaps(DScaleSpace* self); +void DScaleSpace_ClearMagnitudeAndDirectionMaps(DScaleSpace* self); +void DScaleSpace_BuildDiffMaps (DScaleSpace* self); +void DScaleSpace_BuildGaussianMaps (DScaleSpace* self, ImageMap* first, double firstScale, + int scales, double sigma); +void DScaleSpace_CheckMinMax (DScaleSpace* self, ImageMap* layer, double c, int x, int y, + bool* IsMin, bool* IsMax, bool cLayer); + + +typedef struct LoweFeatureDetector LoweFeatureDetector; +struct LoweFeatureDetector { + ArrayList* globalKeypoints; + ArrayList* globalNaturalKeypoints; + OctavePyramid* pyr; +}; + +void LoweFeatureDetector_SetPrintWarning(bool value); +void LoweFeatureDetector_SetVerbose(bool value); + +LoweFeatureDetector* LoweFeatureDetector_new0(); +void LoweFeatureDetector_delete(LoweFeatureDetector* ); +ArrayList* LoweFeatureDetector_GlobalKeypoints(LoweFeatureDetector* ); +ArrayList* LoweFeatureDetector_GlobalNaturalKeypoints(LoweFeatureDetector* ); +int LoweFeatureDetector_DetectFeaturesDownscaled(LoweFeatureDetector* self, ImageMap*, int, double); +int LoweFeatureDetector_DetectFeatures(LoweFeatureDetector* self, ImageMap*); + + +typedef struct KeypointXMLList KeypointXMLList; +struct KeypointXMLList { + char* imageFile; + ArrayList* array; + int xDim; + int yDim; +}; +KeypointXMLList* KeypointXMLList_new0(); +void KeypointXMLList_delete(KeypointXMLList* self); +KeypointXMLList* KeypointXMLList_new (char* imageFile, int xDim, int yDim, ArrayList* list); +KeypointXMLList* KeypointXMLList_new2 (ArrayList* list, int xDim, int yDim); +void KeypointXMLList_Add(KeypointXMLList* self, KeypointN* kp); +KeypointXMLList* KeypointXMLReader_ReadComplete(char*); +KeypointXMLList* KeypointXMLReader_ReadComplete2(char*, bool); +void KeypointXMLWriter_WriteComplete(char*, int, int, char*, ArrayList*); +void KeypointXMLWriter_WriteComplete2(char*, int, int, char*, ArrayList*, bool); + + +typedef struct Match Match; +struct Match { + KeypointN* kp1; + KeypointN* kp2; + double dist1; + double dist2; +}; +Match* Match_new0(); +Match* Match_new(KeypointN* kp1, KeypointN* kp2, double dist1, double dist2); +Match* Match_clone (Match* self); +void Match_delete(Match* self); + +typedef struct MatchWeighter MatchWeighter; +struct MatchWeighter { + IComparator comparator; + double distExp; + double quotExp; +}; +MatchWeighter* MatchWeighter_new0(); +MatchWeighter* MatchWeighter_new(double distExp, double quotExp); +void MatchWeighter_delete(MatchWeighter* self); +int MatchWeighter_CompareTo (MatchWeighter* self, Match* m1, Match* m2); +double MatchWeighter_OverallFitness(MatchWeighter* self, Match* m); + +typedef struct Position Position; +struct Position { + double yaw; + double pitch; + double rotation; +}; +Position* Position_new0(); +void Position_delete(Position* self); +Position* Position_new(double yaw, double pitch, double rotation); +char* Position_ToString (Position* self); + +typedef struct AffineTransform2D AffineTransform2D; +struct AffineTransform2D { + struct SimpleMatrix base; + // The relative angle of both images in the affine transformation. + double rotationAngle; + // The angle between the horizon and the line through the centers of both + // images. + double centerAngle; + double shiftWidth; +}; +AffineTransform2D* AffineTransform2D_new0(); +void AffineTransform2D_delete(AffineTransform2D* self); +AffineTransform2D* AffineTransform2D_clone(AffineTransform2D* self); +int AffineTransform2D_GetShiftWidth(AffineTransform2D* self); +AffineTransform2D* AffineTransform2D_BuildTransformFromTwoPairs(double,double,double,double,double,double,double,double,int,int); + +typedef struct ImageMatchModel ImageMatchModel; +struct ImageMatchModel { + struct IRANSACModel base; + + // The two original matches we build the model on. + Match* m1; + Match* m2; + + double fitThresh; + + // The distance-gratifying factor in the distance relaxing formula. + double distanceFactor; + // The image resolution to calculate the maximum possible distance. + int width, height; + + double fittingErrorSum; + ArrayList* fittingGround; + AffineTransform2D* trans; +}; + +ImageMatchModel* ImageMatchModel_clone(ImageMatchModel* self); +void ImageMatchModel_delete(ImageMatchModel* self); +ImageMatchModel* ImageMatchModel_new (double fitThresh, double distanceFactor, + int width, int height); +char* ImageMatchModel_ToString (ImageMatchModel* self); +ImageMatchModel* MatchDriver_FilterMatchSet (ArrayList* matches, + double distanceFactor, int width, int height); + +typedef struct FilterPoint FilterPoint; +struct FilterPoint +{ + double x, y; + void* user; +}; + +FilterPoint* FilterPoint_new0(); +void FilterPoint_delete(FilterPoint* self); + +typedef struct AreaFilter AreaFilter; +struct AreaFilter { + +}; +AreaFilter* AreaFilter_new0(); +void AreaFilter_delete(AreaFilter* self); +double AreaFilter_PolygonArea(AreaFilter* self, ArrayList* orderedPoints); +ArrayList* AreaFilter_CreateConvexHull (AreaFilter* self, ArrayList* points); +int AreaFilter_CreateHull (AreaFilter* self, ArrayList* points); +int AreaFilter_CompareLow (const FilterPoint* p1, const FilterPoint* p2); +int AreaFilter_CompareHigh (const FilterPoint* p1, const FilterPoint* p2); +bool AreaFilter_ccw (ArrayList* points, int i, int j, int k); +int AreaFilter_MakeChain (AreaFilter* self, ArrayList* points, int (*comp)(const FilterPoint*, const FilterPoint*)); + +typedef struct MultiMatch MultiMatch; +struct MultiMatch { + ArrayList* keySets; // KeypointXMLList[] + // Global k-d tree, containing all keys. + KDTree* globalKeyKD; + // Global key list, containing Keypoint elements + ArrayList* globalKeys; + + // Global match list containing Match objects + ArrayList* globalMatches; + + // Partitioned matches + void*** matchSets; + int imageCount; + + ArrayList* filteredMatchSets; + + bool verbose; +}; + +MultiMatch* MultiMatch_new0(); +void MultiMatch_delete(MultiMatch* self); +void MultiMatch_LoadKeysetsFromMemory (MultiMatch* self, ArrayList* memlist); +void MultiMatch_LoadKeysets(MultiMatch* self, ArrayList* filenames); + + + +// a set of files from the same panorama +typedef struct Component Component; +struct Component { + ArrayList* files; +}; +Component* Component_new0(); +void Component_delete(Component* self); +void Component_AddComponent(Component* self, Component* comp); +bool Component_IsIncluded (Component* self, char* filename); +void Component_AddFile (Component* self, char* filename); +char* Component_ToString (Component* self); + +// Matches between two images +typedef struct MatchSet MatchSet; +struct MatchSet { + MultiMatch* parent; // inner class + char* file1;... [truncated message content] |