/* ** Integrated holistic system. ** ** Performs everything to do with my project. ** ** I am not kidding. ** ** Actually, my project had consisted of a set of separate ** programs up until the end of the first semester. I then ** integrated them into one pseudo-library of routines. ** This means that I can access the functions individually ** for testing, or use high-level routines to generate ** example data. ** ** All access is via the command line. Syntax is given ** below for the different modes together with a brief ** description of what they do. ** ** Example command line: ** integrate ** ** calculate ** Calculates affine coordinates from the input correspondence data ** using the method specified. For more details about the methods ** currently supported, type "integrate calculate". ** ** calc-analyse ** Calculates affine coordinates from the input correspondence data ** using the method specified. The coordinates are then compared to the ** given reference coordinates and the average error is then output. ** ** analyse ** Compares the affine coordinates with the reference coordinates given ** and outputs the average error. ** ** project <3D model file> ** Projects the given 3D model onto the pair of virtual cameras ** specified in the model. The second camera is mis-aligned by the given ** yaw, pitch and roll angles in degrees before the projection is done. ** This generates an output simulated correspondence file. ** ** noise ** This adds noise to a correspondence file. The magnitude of this ** random offset varies from 0 to (noise% * the distance from p0 to p3), ** for each correspondence point. ** ** errortest <3D model file> ** This takes the 3D model and distorts it according to each distortion ** found in the distortion file. The affine coordinates are then calculated ** using each method from 0 - 7 and the average error produced by each ** method is output to the summary file. This is no longer useful, given ** the alternative of generating triangle files and viewing them in 3D, ** which is much better than just obtaining a number describing the ** `average error`. ** ** rotationmap <3D model file> ** ** This distorts the given 3D model file by misaligning the cameras by ** values from -yaw to +yaw and -pitch to +pitch. The error produced by ** the calculation method is displayed in an image format, with yaw and ** pitch along the two axes. The main image is prefix_afErr.pgm; other ** pictures that convey subsidiary iteration are output as prefix_chi2.pgm, ** prefix_d1.pgm, prefix_d2.pgm, prefix_dMag.pgm, prefix_numIt.pgm, and ** prefix_ochi2.pgm. ** ** deltamap <3D model file> ** ** This distorts the 3D model file by misaligning the cameras by the given ** yaw and pitch angles. An image is then made of how well the delta- ** correction makes the correspondence lines converge for different values ** of delta. This is useful for verifying that it is possible to find ** a correct value of delta, and also for validating the theory behind ** delta-correction. ** ** warpmap <3D model file> ** ** This distorts the 3D model file by misaligning the cameras by the given ** yaw and pitch angles. An image is then made of how well warp- ** correction makes the correspondence lines converge for different values ** of the warp. This is useful for verifying that it is possible to find ** a correct warp, and also for validating the theory behind ** warp-correction. ** ** eTtest ** This finds the best point where the correspondence lines converge ** and how well they converge at that point. Used to verify that the ** chi^2 estimate is working correctly (you have to keep your eye on it, ** or else it might do evil things). ** ** chrisdebug ** Do not use this mode. You have been warned. ** ** gentriangles <3D model file> ** ** ** This takes the 3D model file, misaligns the cameras by (yaw, pitch, ** roll), and calculates affine coordinates using the specified method. ** These are used with the connectivity file to generate a set of ** triangles from the affine point coordinates, which is written as ** the output distorted triangle file. ** ** pixelize ** The input correspondences are snapped to the closest point on a ** simulated pixel grid constructed on the viewplane. They are then ** output to a different file. ** ** triconvert ** This makes a triangle file from an affine file plus the connections ** between points. ** ** Chris Butcher, 29 May 1996 ** */ #define DEBUGINFO 0 #define IMPORTANTDEBUGINFO 0 #define MAX_NUMPOINTS 20000 #include #include /* getclock() is used to seed the random number generator */ extern int getclock(int clock_type, struct timespec *tp); /* mathdefs.h contains vector, matrix definitions and math codes */ #include "mathdefs.h" /****** FUNCTION PROTOTYPES ******/ /*** Command line analysis ***/ void print_usage(int argc, char **argv); void calculate(int argc, char **argv); void calc_analyse(int argc, char **argv); void analyse(int argc, char **argv); void project(int argc, char **argv); void doNoise(int argc, char **argv); void errortest(int argc, char **argv); void rotationmap(int argc, char **argv); void deltamap(int argc, char **argv); void warpmap(int argc, char **argv); void eTtest(int argc, char **argv); void gentriangles(int argc, char **argv); void chrisdebug(int argc, char **argv); void pixelize(int argc, char **argv); void triconvert(int argc, char **argv); /*** Calculation functions ***/ /* === Affine Calculation functions - both with/without delta and warp correction === */ vector *AffineCalc(vector *p_ref1, vector *p_ref2, vector *points1, vector *points2, int approx_method, int do_axes); void getAffine(vector p, vector p_prime, vector p_affine); void calculate_constant_points(void); /* normal delta-correction functions */ double get_best_eT(double deltaX, double deltaY); void adjust_delta(void); double steepest_delta(double initial_x, double initial_y); void apply_delta(void); /* new warp-correction functions */ double get_best_warped_eT(double yaw, double pitch); void prepare_warp_matrix(double yaw, double pitch); void warp_point(vector thePoint, vector *warped_point); void adjust_warp(void); double steepest_warp(double initial_x, double initial_y); void apply_warp(void); /* functions to perform simplex minimization */ void adjust_simplex_delta(double initialx, double initialy, double (*somefunc_vec)(double *vec)); /* 2D minimization, 3 points comprise the simplex */ #define N_DIM 2 #define N_PTS N_DIM+1 double simplex_try_point(double point[N_PTS][N_DIM], double func[N_PTS], double pointsum[N_DIM], double (*evalfunc)(double *vec), int hi_point, int *n_evals, double factor); double simplex_eT_interface(double *vec); void fix(vector toFix); void adjust_axes(void); void readjust_axes(void); /* === Analysis functions - operates on reference and actual affine coordinates === */ float average_error(vector *refpoints, vector *affpoints); /* === Projection functions - project 3D points onto simulated cameras to give perfect correspondences === */ void rotate_project3d(double yaw, double pitch, double rollc, vector vf1, vector vf2, vector vt1, vector vt2, vector *points_3d, vector **p1_ref, vector **p1_corr, vector **p2_ref, vector **p2_corr); void project3d(vector vf1, vector vf2, vector vt1, vector vt2, double roll, vector *points_3d, vector **p1_ref, vector **p1_corr, vector **p2_ref, vector **p2_corr); void project_point(vector origin, vector X_vec, vector Y_vec, vector Z_vec, vector thePoint, vector projection); /* === Noise functions - adds random error to the input correspondences === */ void add_noise(double noisepercent, vector *p_ref1, vector *p_corr1, vector *p_ref2, vector *p_corr2); void apply_noise(double noisedist, vector toNoisify); /* === Rotation functions - alters the 3D view so it's no longer a translation === */ void getRotationMatrix(matrix R, int theAxis, double sintheta, double costheta); /* === Rotation-map functions - get the error for a given rotation to be used in the errormap */ double getRotationError(double yaw, double pitch); /* === Pixelization - maps all correspondence points to a grid on the view plane */ void map_pixels(double pixel_spacing, vector *p1_ref, vector *p1_correspond, vector *p2_ref, vector *p2_correspond); void map_to_pixel(double pixel_spacing, vector aPoint); /*** Support functions ***/ /* === Memory management === */ void veccpy(vector *array1, vector *array2, int numvec); vector *alloc_vector(int numVectors); gray **alloc_pgm(int width, int height); /* === File I/O - reads/writes .3D .2D and .affine === */ void read_3d_file(char *filename, vector vf1, vector vf2, vector vt1, vector vt2, double *rollc, vector **points_3d); void write_3d_file(char *filename, vector vf1, vector vf2, vector vt1, vector vt2, double rollc, vector *points_3d); void read_correspondence_file(char *filename, vector **p1ref, vector **p2ref, vector **p1, vector **p2); void write_correspondence_file(char *filename, vector *p1ref, vector *p2ref, vector *p1, vector *p2); vector *read_affine_file(char *filename); void write_affine_file(char *filename, vector *pointArray); void get_2d_point(FILE *infile, vector xypoint); void get_3d_point(FILE *infile, vector xyzpoint); void get_nextline(FILE *infile, char *buffer); void output_vector(FILE *outfile, vector theVector); void write_pgm_file(char *filename, gray **image, int width, int height); /* This shouldn't be necessary. Unfortunately the ppm libraries have been less than stable. */ void pgm_writepgm( FILE *fp, gray **image, int height, int width, int max, int rawbits); int *read_connectivity_file(char *filename, int *num_tri); void write_triangle_file(char *filename, vector *pAffine, int *connects, int num_tri); /* === Debugging output === */ void fatalError(char *errormessage); void text_print(char *message); void scalar_print(char *message, double d); void vec_print(char *message, vector v); void vec3_print(char *message, vector v); void matrix_print(char *message, matrix m); /* === Math functions - NB distinction between 2D (x,y,w) and 3D (4x4) algebra === */ /* use this to avoid divide by zero */ double check0(double checkthis, char *message); double vec_angle(vector v1, vector v2, int axis); double matrixDet_2d(matrix m); void matrixMult_2d(matrix a, matrix b, matrix result); void matrixMult_3d(matrix a, matrix b, matrix result); void matrixCopy_2d(matrix src, matrix dest); void matrixCopy_3d(matrix src, matrix dest); void preApply_2d(vector v, matrix m, vector result); void postApply_2d(matrix m, vector v, vector result); void apply_3d(matrix m, vector v, vector result); /* Global variables */ /* holds the number of points currently under consideration */ int numpoints; /* Affine Calculation global variables */ /* the reference correspondences */ vector p0, p0_prime, p1, p1_prime, p2, p2_prime, p3, p3_prime; /* used in the delta-convergence process */ vector e03, eT, delta; /* matrices to map the 3D representation to the image planes i and i' */ matrix A, A_prime; /* the arrays containing the points */ vector *pArray, *p_primeArray, *affineArray, *uArray; /* which axis is the virtual [remapped] P3? */ int rotateaxes; /* holds the warp matrix */ matrix warpM; /* holds the warp parameters */ vector warp_vec; /* should the warped points be printed to stderr? */ int print_warps; /* Rotation-map global variables */ vector vf1, vf2, vt1, orig_vt2, new_vt2, *points_3d; vector *p1_ref, *p2_ref, *p1_corr, *p2_corr, *pAffine, *referenceAffine; double rollCoeff; int methodnum, orignumpoints; /* Delta-correction global variables */ int n_evals, iterations; double trial0chiSquared, trialDchiSquared; /* Support globals */ char buf0[100]; /* miscellaneous buffer for general use */ /*************** COMMAND-LINE ANALYSIS ***************/ /* main() merely selects which function the program is performing. */ int main(int argc, char **argv) { numpoints = 0; if (argc < 2) { /* Enlighten the user as to proper protocol for homage. */ print_usage(argc, argv); } else if (strcmp(argv[1], "calculate") == 0) calculate(argc, argv); else if (strcmp(argv[1], "calc-analyse") == 0) calc_analyse(argc, argv); else if (strcmp(argv[1], "analyse") == 0) analyse(argc, argv); else if (strcmp(argv[1], "project") == 0) project(argc, argv); else if (strcmp(argv[1], "noise") == 0) doNoise(argc, argv); else if (strcmp(argv[1], "errortest") == 0) errortest(argc, argv); else if (strcmp(argv[1], "rotationmap") == 0) rotationmap(argc, argv); else if (strcmp(argv[1], "deltamap") == 0) deltamap(argc, argv); else if (strcmp(argv[1], "warpmap") == 0) warpmap(argc, argv); else if (strcmp(argv[1], "eTtest") == 0) eTtest(argc, argv); else if (strcmp(argv[1], "gentriangles") == 0) gentriangles(argc, argv); else if (strcmp(argv[1], "chrisdebug") == 0) chrisdebug(argc, argv); else if (strcmp(argv[1], "pixelize") == 0) pixelize(argc, argv); else if (strcmp(argv[1], "triconvert") == 0) triconvert(argc, argv); else print_usage(argc, argv); return 0; } /* Prints the proper command-line usage of the program */ void print_usage(int argc, char **argv) { fprintf(stderr, "Usage: %s \n", argv[0]); fprintf(stderr, "where is one of:\n"); fprintf(stderr, " calculate\n"); fprintf(stderr, " calc-analyse\n"); fprintf(stderr, " analyse\n"); fprintf(stderr, " project\n"); fprintf(stderr, " noise\n"); fprintf(stderr, " errortest\n"); fprintf(stderr, " rotationmap\n"); fprintf(stderr, " deltamap\n"); fprintf(stderr, " warpmap\n"); fprintf(stderr, " eTtest\n"); fprintf(stderr, " gentriangles\n"); fprintf(stderr, " pixelize\n"); fprintf(stderr, " triconvert\n"); } /* "calculate" mode inputs a correspondence file and outputs an affine file */ void calculate(int argc, char **argv) { vector *p1ref, *p2ref, *p1, *p2; int calcmode, approx_method, do_axes; if ((argc < 5) || (sscanf(argv[2], "%d", &calcmode) != 1)) { fprintf(stderr, "Usage: %s calculate \n", argv[0]); fprintf(stderr, "Mode: 0 = standard calculation\n"); fprintf(stderr, " 2 = delta adjustment\n"); fprintf(stderr, " 4 = simplex delta adjustment\n"); fprintf(stderr, " 6 = warp adjustment\n"); fprintf(stderr, " (add 1 for axis rotation)\n"); exit(-1); } if (calcmode < 0 || calcmode > 7) { fprintf(stderr, "Error: mode must be from 0 to 7 (entered %s)\n", argv[2]); fprintf(stderr, "Mode: 0 = standard calculation\n"); fprintf(stderr, " 2 = delta adjustment\n"); fprintf(stderr, " 4 = simplex delta adjustment\n"); fprintf(stderr, " 6 = warp adjustment\n"); fprintf(stderr, " (add 1 for axis rotation)\n"); exit(-1); } do_axes = calcmode % 2; approx_method = calcmode / 2; read_correspondence_file(argv[3], &p1ref, &p2ref, &p1, &p2); write_affine_file(argv[4], AffineCalc(p1ref, p2ref, p1, p2, approx_method, do_axes)); } /* "calc-analyse" mode inputs a correspondence file, analyses and outputs error to stderr */ void calc_analyse(int argc, char **argv) { vector *p1ref, *p2ref, *p1, *p2, *ref_affine; int calcmode, approx_method, do_axes; float aferror; if ((argc < 5) || (sscanf(argv[2], "%d", &calcmode) != 1)) { fprintf(stderr, "Usage: %s calc-analyse \n", argv[0]); fprintf(stderr, "Mode: 0 = standard calculation\n"); fprintf(stderr, " 2 = delta adjustment\n"); fprintf(stderr, " 4 = simplex delta adjustment\n"); fprintf(stderr, " 6 = warp adjustment\n"); fprintf(stderr, " (add 1 for axis rotation)\n"); exit(-1); } if (calcmode < 0 || calcmode > 7) { fprintf(stderr, "Error: mode must be from 0 to 7 (entered %s)\n", argv[2]); fprintf(stderr, "Mode: 0 = standard calculation\n"); fprintf(stderr, " 2 = delta adjustment\n"); fprintf(stderr, " 4 = simplex delta adjustment\n"); fprintf(stderr, " 6 = warp adjustment\n"); fprintf(stderr, " (add 1 for axis rotation)\n"); exit(-1); } do_axes = calcmode % 2; approx_method = calcmode / 2; read_correspondence_file(argv[3], &p1ref, &p2ref, &p1, &p2); ref_affine = read_affine_file(argv[4]); aferror = average_error(ref_affine, AffineCalc(p1ref, p2ref, p1, p2, approx_method, do_axes)); fprintf(stderr, "Calc-analyse : Average error is %9.5f%%\n", aferror); } /* "analyse" mode inputs two .affine files (one real, one perfect) and outputs error to stderr */ void analyse(int argc, char **argv) { vector *p_affine, *ref_affine; float aferror; if (argc < 4) { fprintf(stderr, "Usage: %s analyse \n", argv[0]); exit(-1); } p_affine = read_affine_file(argv[2]); ref_affine = read_affine_file(argv[3]); aferror = average_error(ref_affine, p_affine); fprintf(stderr, "Analyse : Average error is %9.5f%%\n", aferror); } /* "project" mode inputs a .3D file and projects it onto the virtual camera view planes */ void project(int argc, char **argv) { vector vf1, vf2, vt1, vt2, *points_3d; vector *p1_ref, *p2_ref, *p1_correspond, *p2_correspond; double yaw, pitch, roll, dummy; if ((argc < 7) || (sscanf(argv[3], "%lf", &yaw) != 1) || (sscanf(argv[4], "%lf", &pitch) != 1) || (sscanf(argv[5], "%lf", &roll) != 1)){ fprintf(stderr, "Usage: %s project <3D model file> \n", argv[0]); exit(-1); } read_3d_file(argv[2], vf1, vf2, vt1, vt2, &dummy, &points_3d); rotate_project3d(yaw, pitch, roll, vf1, vf2, vt1, vt2, points_3d, &p1_ref, &p1_correspond, &p2_ref, &p2_correspond); write_correspondence_file(argv[6], p1_ref, p1_correspond, p2_ref, p2_correspond); } /* "noise" mode adds noise to the given correspondence file and outputs it again. */ void doNoise(int argc, char **argv) { vector *p1_ref, *p2_ref, *p1_correspond, *p2_correspond; double noisepercent; if ((argc < 5) || (sscanf(argv[3], "%lf", &noisepercent) != 1)) { fprintf(stderr, "Usage: %s noise \n", argv[0]); exit(-1); } read_correspondence_file(argv[2], &p1_ref, &p2_ref, &p1_correspond, &p2_correspond); add_noise(noisepercent, p1_ref, p1_correspond, p2_ref, p2_correspond); write_correspondence_file(argv[4], p1_ref, p1_correspond, p2_ref, p2_correspond); } /* "errortest" mode distorts a 3D file according to a distortion file and writes the errors found to an output file. */ void errortest(int argc, char **argv) { FILE *distortionfile, *errorfile; vector vf1, vf2, vt1, orig_vt2, new_vt2, *points_3d; vector *p1_ref, *p2_ref, *p1_corr, *p2_corr, *referenceAffine; vector *temp_p1ref, *temp_p2ref, *temp_p1corr, *temp_p2corr; double orig_rollCoeff, new_rollCoeff, yaw, pitch, roll, noisepercent; double affineError[4]; int i, orignumpoints; if (argc != 5) { fprintf(stderr, "Usage: %s errortest \n", argv[0]); exit(-1); } if ((distortionfile = fopen(argv[3], "r")) == NULL) { fprintf(stderr, "Can't open distortion file \"%s\"", argv[3]); exit(-1); } if ((errorfile = fopen(argv[4], "w")) == NULL) { fprintf(stderr, "Can't open error file \"%s\"", argv[3]); exit(-1); } read_3d_file(argv[2], vf1, vf2, vt1, orig_vt2, &orig_rollCoeff, &points_3d); orignumpoints = numpoints; project3d(vf1, vf2, vt1, orig_vt2, orig_rollCoeff, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); referenceAffine = AffineCalc(p1_ref, p2_ref, p1_corr, p2_corr, 0, 0); temp_p1ref = alloc_vector(4); temp_p2ref = alloc_vector(4); temp_p1corr = alloc_vector(numpoints); temp_p2corr = alloc_vector(numpoints); while (1) { get_nextline(distortionfile, buf0); if (sscanf(buf0, "%lf %lf %lf %lf", &yaw, &pitch, &roll, &noisepercent) != 4) { if (buf0[0] == '~') break; else fprintf(errorfile, "%s", buf0); } else { printf("Performing analysis (%4.1g %4.1g %4.1g %4.1g) ... ", yaw, pitch, roll, noisepercent); fflush(stdout); /* Set current view back to original 3D view */ VSET(new_vt2, orig_vt2); new_rollCoeff = orig_rollCoeff; numpoints = orignumpoints; rotate_project3d(yaw, pitch, roll, vf1, vf2, vt1, new_vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); if (noisepercent != 0) { /* Add noise to correspondences */ add_noise(noisepercent, p1_ref, p1_corr, p2_ref, p2_corr); } for (i = 0; i < 4; i++) { /* before each test we must restore the original points as delta-correction will change them. */ veccpy(p1_ref, temp_p1ref, 4); veccpy(p2_ref, temp_p2ref, 4); veccpy(p1_corr, temp_p1corr, numpoints); veccpy(p2_corr, temp_p2corr, numpoints); affineError[i] = average_error(referenceAffine, AffineCalc(temp_p1ref, temp_p2ref, temp_p1corr, temp_p2corr, i, 0)); } fprintf(errorfile, "Y(%4.1f) P(%4.1f) R(%4.1f) N(%4.1f): Errors %12.3f,%12.3f,%12.3f,%12.3f%%\n", yaw, pitch, roll, noisepercent, affineError[0], affineError[1], affineError[2], affineError[3]); printf("Errors %7.3f,%7.3f,%7.3f,%7.3f%%\n", affineError[0], affineError[1], affineError[2], affineError[3]); } } fclose(errorfile); } /* "rotationmap" mode distorts a 3D file multiple times and writes the errors found (as a 2D errormap in PGM format) to a file. */ void rotationmap(int argc, char **argv) { #define AF_ERR_MAX 10.0 #define ITER_MAX 100 #define CHI2_MAX 0.01 #define DELTA_MAG_MAX 1.0 double yaw, pitch, max_yaw, max_pitch; double affineError, numIterations, deltaMagnitude, difference; gray **aferr_image, **iter_image, **chi2_image, **ochi2_image, **dmag_image, **diff1_image, **diff2_image; char filename[100]; int y, x, im_width, im_height, approx_method; if (argc != 9) { fprintf(stderr, "Usage: %s rotationmap \n", argv[0]); exit(-1); } if (sscanf(argv[3], "%d", &methodnum) != 1) { sprintf(buf0, "rotationmap: can't read method number (%s)", argv[3]); fatalError(buf0); } if (methodnum < 0 || methodnum > 7) fatalError("rotationmap: method must be from 0-7"); if (sscanf(argv[4], "%lf", &max_yaw) != 1) { sprintf(buf0, "rotationmap: can't read max yaw (%s)", argv[4]); fatalError(buf0); } approx_method = (methodnum / 2); if (sscanf(argv[5], "%lf", &max_pitch) != 1) { sprintf(buf0, "rotationmap: can't read max pitch (%s)", argv[5]); fatalError(buf0); } if (sscanf(argv[6], "%d", &im_width) != 1) { sprintf(buf0, "rotationmap: can't read image width (%s)", argv[6]); fatalError(buf0); } if (sscanf(argv[7], "%d", &im_height) != 1) { sprintf(buf0, "rotationmap: can't read image height (%s)", argv[7]); fatalError(buf0); } aferr_image = alloc_pgm(im_width, im_height); iter_image = alloc_pgm(im_width, im_height); chi2_image = alloc_pgm(im_width, im_height); ochi2_image = alloc_pgm(im_width, im_height); dmag_image = alloc_pgm(im_width, im_height); diff1_image = alloc_pgm(im_width, im_height); diff2_image = alloc_pgm(im_width, im_height); read_3d_file(argv[2], vf1, vf2, vt1, orig_vt2, &rollCoeff, &points_3d); orignumpoints = numpoints; project3d(vf1, vf2, vt1, orig_vt2, rollCoeff, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); referenceAffine = AffineCalc(p1_ref, p2_ref, p1_corr, p2_corr, 0, 0); for (y = 0; y < im_height; y++) { printf("Analysing ... %3d%%\r", (100*y)/im_height); fflush(stdout); for (x = 0; x < im_width; x++) { yaw = max_yaw * (2 * (((float) y) / im_height) - 1.0); pitch = max_pitch * (2 * (((float) x) / im_width) - 1.0); affineError = getRotationError(yaw, pitch)/AF_ERR_MAX; switch(approx_method) { case 0: numIterations = 0; deltaMagnitude = 0; break; case 1: numIterations = ((float) iterations)/ITER_MAX; deltaMagnitude = sqrt(SQR(delta[X]) + SQR(delta[Y]))/DELTA_MAG_MAX; break; case 2: numIterations = ((float) n_evals)/ITER_MAX; deltaMagnitude = sqrt(SQR(delta[X]) + SQR(delta[Y]))/DELTA_MAG_MAX; break; case 3: numIterations = ((float) iterations)/ITER_MAX; deltaMagnitude = sqrt(SQR(warp_vec[X]) + SQR(warp_vec[Y]))/DELTA_MAG_MAX; break; default: sprintf(buf0, "rotationmap: approx_method outside range (%d)?!\n", approx_method); fatalError(buf0); exit(-1); } difference = trial0chiSquared - trialDchiSquared; if (affineError > 1.0) aferr_image[y][x] = 255; else aferr_image[y][x] = (int) (255*affineError); if (numIterations > 1.0) iter_image[y][x] = 255; else iter_image[y][x] = (int) (255*numIterations); if (trialDchiSquared > CHI2_MAX) chi2_image[y][x] = 255; else chi2_image[y][x] = (int) (255*(trialDchiSquared/CHI2_MAX)); if (trial0chiSquared > CHI2_MAX) ochi2_image[y][x] = 255; else ochi2_image[y][x] = (int) (255*(trial0chiSquared/CHI2_MAX)); if (deltaMagnitude > 1.0) dmag_image[y][x] = 255; else dmag_image[y][x] = (int) (255*deltaMagnitude); if (difference < 0.0) { diff1_image[y][x] = 0; diff2_image[y][x] = (int) (255*(-difference)); } else { diff1_image[y][x] = (int) (255*difference); diff2_image[y][x] = 0; } } } printf("Analysing ... complete!\n"); sprintf(filename, "%s_afErr.pgm", argv[8]); write_pgm_file(filename, aferr_image, im_width, im_height); sprintf(filename, "%s_numIt.pgm", argv[8]); write_pgm_file(filename, iter_image, im_width, im_height); sprintf(filename, "%s_chi2.pgm", argv[8]); write_pgm_file(filename, chi2_image, im_width, im_height); sprintf(filename, "%s_ochi2.pgm", argv[8]); write_pgm_file(filename, ochi2_image, im_width, im_height); sprintf(filename, "%s_dMag.pgm", argv[8]); write_pgm_file(filename, dmag_image, im_width, im_height); sprintf(filename, "%s_d1.pgm", argv[8]); write_pgm_file(filename, diff1_image, im_width, im_height); sprintf(filename, "%s_d2.pgm", argv[8]); write_pgm_file(filename, diff2_image, im_width, im_height); } /* "deltamap" mode distorts a 3D file one time and writes the chi-squared versus delta (as a 2D errormap in PGM format) to a file. */ void deltamap(int argc, char **argv) { #define CHI2_MAX 0.01 #define DELTA_MAG_MAX 1.0 double yaw, pitch; double chiSquared; gray **delta_image; int y, x, im_width, im_height; if (argc != 8) { fprintf(stderr, "Usage: %s deltamap \n", argv[0]); exit(-1); } if (sscanf(argv[3], "%lf", &yaw) != 1) { sprintf(buf0, "deltamap: can't read yaw (%s)", argv[3]); fatalError(buf0); } if (sscanf(argv[4], "%lf", &pitch) != 1) { sprintf(buf0, "deltamap: can't read pitch (%s)", argv[4]); fatalError(buf0); } if (sscanf(argv[5], "%d", &im_width) != 1) { sprintf(buf0, "deltamap: can't read image width (%s)", argv[5]); fatalError(buf0); } if (sscanf(argv[6], "%d", &im_height) != 1) { sprintf(buf0, "deltamap: can't read image height (%s)", argv[6]); fatalError(buf0); } delta_image = alloc_pgm(im_width, im_height); read_3d_file(argv[2], vf1, vf2, vt1, new_vt2, &rollCoeff, &points_3d); rotate_project3d(yaw, pitch, 0.0, vf1, vf2, vt1, new_vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); VSET(p0, p1_ref[0]); VSET(p1, p1_ref[1]); VSET(p2, p1_ref[2]); VSET(p3, p1_ref[3]); pArray = p1_corr; VSET(p0_prime, p2_ref[0]); VSET(p1_prime, p2_ref[1]); VSET(p2_prime, p2_ref[2]); VSET(p3_prime, p2_ref[3]); p_primeArray = p2_corr; uArray = alloc_vector(numpoints); for (y = 0; y < im_height; y++) { printf("Deltamapping ... %3d%%\r", (100*y)/im_height); fflush(stdout); for (x = 0; x < im_width; x++) { delta[X] = DELTA_MAG_MAX * (2 * (((float) y) / im_height) - 1.0); delta[Y] = DELTA_MAG_MAX * (2 * (((float) x) / im_width ) - 1.0); chiSquared = get_best_eT(delta[X], delta[Y]); if (chiSquared > CHI2_MAX) delta_image[y][x] = 255; else delta_image[y][x] = (int) (255*(chiSquared/CHI2_MAX)); } } printf("Deltamapping ... complete!\n"); write_pgm_file(argv[7], delta_image, im_width, im_height); } /* "warpmap" mode distorts a 3D file one time and writes the chi-squared versus reverse warp (as a 2D errormap in PGM format) to a file. */ void warpmap(int argc, char **argv) { double warp_chi2_max = 0.15; double yaw, pitch; double chiSquared, warp_mag; gray **warp_image; int y, x, im_width, im_height; if (argc != 9) { fprintf(stderr, "Usage: %s warpmap \n", argv[0]); exit(-1); } if (sscanf(argv[3], "%lf", &yaw) != 1) { sprintf(buf0, "warpmap: can't read yaw (%s)", argv[3]); fatalError(buf0); } if (sscanf(argv[4], "%lf", &pitch) != 1) { sprintf(buf0, "warpmap: can't read pitch (%s)", argv[4]); fatalError(buf0); } if (sscanf(argv[5], "%lf", &warp_mag) != 1) { sprintf(buf0, "warpmap: can't read warp magnitude (%s)", argv[5]); fatalError(buf0); } if (sscanf(argv[6], "%d", &im_width) != 1) { sprintf(buf0, "warpmap: can't read image width (%s)", argv[6]); fatalError(buf0); } if (sscanf(argv[7], "%d", &im_height) != 1) { sprintf(buf0, "warpmap: can't read image height (%s)", argv[7]); fatalError(buf0); } warp_image = alloc_pgm(im_width, im_height); read_3d_file(argv[2], vf1, vf2, vt1, new_vt2, &rollCoeff, &points_3d); rotate_project3d(yaw, pitch, 0.0, vf1, vf2, vt1, new_vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); VSET(p0, p1_ref[0]); VSET(p1, p1_ref[1]); VSET(p2, p1_ref[2]); VSET(p3, p1_ref[3]); pArray = p1_corr; VSET(p0_prime, p2_ref[0]); VSET(p1_prime, p2_ref[1]); VSET(p2_prime, p2_ref[2]); VSET(p3_prime, p2_ref[3]); p_primeArray = p2_corr; uArray = alloc_vector(numpoints); for (y = 0; y < im_height; y++) { printf("Warpmapping ... %3d%%\r", (100*y)/im_height); fflush(stdout); for (x = 0; x < im_width; x++) { warp_vec[X] = warp_mag * (2 * (((float) y) / im_height) - 1.0); warp_vec[Y] = warp_mag * (2 * (((float) x) / im_width ) - 1.0); chiSquared = get_best_warped_eT(warp_vec[X], warp_vec[Y]); if (chiSquared > warp_chi2_max) warp_image[y][x] = 255; else warp_image[y][x] = (int) (255*(chiSquared/warp_chi2_max)); } } printf("Warpmapping ... complete!\n"); write_pgm_file(argv[8], warp_image, im_width, im_height); } /* "eTtest" mode checks how get_best_eT is working. It's a tricky little devil, it knows when your back is turned. */ void eTtest(int argc, char **argv) { double chiSquared; if (argc != 3) { fprintf(stderr, "Usage: %s eTtest \n", argv[0]); exit(-1); } read_correspondence_file(argv[2], &p1_ref, &p2_ref, &p1_corr, &p2_corr); VSET(p0, p1_ref[0]); VSET(p1, p1_ref[1]); VSET(p2, p1_ref[2]); VSET(p3, p1_ref[3]); pArray = p1_corr; VSET(p0_prime, p2_ref[0]); VSET(p1_prime, p2_ref[1]); VSET(p2_prime, p2_ref[2]); VSET(p3_prime, p2_ref[3]); p_primeArray = p2_corr; uArray = alloc_vector(numpoints); /* Print out what it says the results are. */ chiSquared = get_best_eT(0, 0); printf("chiSq = %f\n", chiSquared); printf("eT = %f, %f\n", eT[X], eT[Y]); } /* "gentriangles" mode distorts the given .3D view file with a specified pitch, yaw and roll, calculates and outputs a triangle file for fun. */ void gentriangles(int argc, char **argv) { vector vf1, vf2, vt1, vt2, *points_3d; vector *p1_ref, *p2_ref, *p1_corr, *p2_corr, *pAffine; double rollCoeff, yaw, pitch, roll, noisepercent; int methodnum, approx_method, do_axes; int *connections, num_triangles; if ((argc < 10) || (sscanf(argv[3], "%lf", &yaw) != 1) || (sscanf(argv[4], "%lf", &pitch) != 1) || (sscanf(argv[5], "%lf", &roll) != 1) || (sscanf(argv[6], "%lf", &noisepercent) != 1) || (sscanf(argv[7], "%d", &methodnum) != 1)) { fprintf(stderr, "Usage: %s gentriangles \n", argv[0]); exit(-1); } connections = read_connectivity_file(argv[8], &num_triangles); read_3d_file(argv[2], vf1, vf2, vt1, vt2, &rollCoeff, &points_3d); rotate_project3d(yaw, pitch, roll, vf1, vf2, vt1, vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); /* Add noise to correspondences */ add_noise(noisepercent, p1_ref, p1_corr, p2_ref, p2_corr); approx_method = methodnum / 2; do_axes = methodnum % 2; pAffine = AffineCalc(p1_ref, p2_ref, p1_corr, p2_corr, approx_method, do_axes); write_triangle_file(argv[9], pAffine, connections, num_triangles); if (approx_method == 1 || approx_method == 2) { printf("Delta was (%f, %f)\n", delta[X], delta[Y]); printf("By comparison, p0 = (%f, %f), p3 = (%f, %f)\n", p1_ref[0][X], p1_ref[0][Y], p1_ref[3][X], p1_ref[3][Y]); } else if (approx_method == 3) { printf("Reverse warp found to be (%f, %f)\n", warp_vec[X], warp_vec[Y]); } } /* Guess what "chrisdebug" mode does. */ void chrisdebug(int argc, char **argv) { double yaw, pitch, x_coeff, y_coeff; double chiSquared; int mode, orignumpoints; vector start_p1, end_p1, warped_p1; print_warps = 0; sscanf(argv[3], "%d", &mode); sscanf(argv[4], "%lf", &yaw); sscanf(argv[5], "%lf", &pitch); sscanf(argv[6], "%lf", &x_coeff); sscanf(argv[7], "%lf", &y_coeff); fprintf(stderr, "%s %d %f %f %f %f\n", argv[2], mode, yaw, pitch, x_coeff, y_coeff); read_3d_file(argv[2], vf1, vf2, vt1, new_vt2, &rollCoeff, &points_3d); orignumpoints = numpoints; project3d(vf1, vf2, vt1, new_vt2, rollCoeff, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); VSET(start_p1, p2_corr[0]); start_p1[Z] = 1.0; VSET(p0, p1_ref[0]); VSET(p1, p1_ref[1]); VSET(p2, p1_ref[2]); VSET(p3, p1_ref[3]); pArray = p1_corr; VSET(p0_prime, p2_ref[0]); VSET(p1_prime, p2_ref[1]); VSET(p2_prime, p2_ref[2]); VSET(p3_prime, p2_ref[3]); p_primeArray = p2_corr; uArray = alloc_vector(numpoints); chiSquared = get_best_warped_eT(0.0, 0.0); numpoints = orignumpoints; rotate_project3d(yaw, pitch, rollCoeff, vf1, vf2, vt1, new_vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); VSET(end_p1, p2_corr[0]); end_p1[Z] = 1.0; VSET(p0, p1_ref[0]); VSET(p1, p1_ref[1]); VSET(p2, p1_ref[2]); VSET(p3, p1_ref[3]); pArray = p1_corr; VSET(p0_prime, p2_ref[0]); VSET(p1_prime, p2_ref[1]); VSET(p2_prime, p2_ref[2]); VSET(p3_prime, p2_ref[3]); p_primeArray = p2_corr; uArray = alloc_vector(numpoints); chiSquared = get_best_warped_eT(0.0, 0.0); if (mode == 0) chiSquared = get_best_eT(x_coeff, y_coeff); else { warp_vec[X] = x_coeff; warp_vec[Y] = y_coeff; print_warps = 1; apply_warp(); print_warps = 0; printf("Warped by %f, %f\n", warp_vec[X], warp_vec[Y]); chiSquared = get_best_warped_eT(0.0, 0.0); VSET(warped_p1, p2_corr[0]); #if DEBUGINFO vec_print("start_p1: ", start_p1); vec_print("end_p1: ", end_p1); vec_print("warped_p1: ", warped_p1); #endif } } /* "pixelize" mode takes a correspondence file and maps all the points to a grid on the viewplane. */ void pixelize(int argc, char **argv) { vector *p1_ref, *p2_ref, *p1_correspond, *p2_correspond; double pixel_spacing; if ((argc < 5) || (sscanf(argv[3], "%lf", &pixel_spacing) != 1)) { fprintf(stderr, "Usage: %s pixelize \n", argv[0]); exit(-1); } printf("Pixel spacing %f\n", pixel_spacing); read_correspondence_file(argv[2], &p1_ref, &p2_ref, &p1_correspond, &p2_correspond); map_pixels(pixel_spacing, p1_ref, p1_correspond, p2_ref, p2_correspond); write_correspondence_file(argv[4], p1_ref, p1_correspond, p2_ref, p2_correspond); } /* "triconvert" mode takes an affine file and a connection file and turns them into triangles. */ void triconvert(int argc, char **argv) { vector *affinePoints; int *connections, num_tri; if (argc < 5) { fprintf(stderr, "Usage: %s triconvert \n", argv[0]); exit(-1); } affinePoints = read_affine_file(argv[2]); connections = read_connectivity_file(argv[3], &num_tri); write_triangle_file(argv[4], affinePoints, connections, num_tri); } /************ CALCULATION FUNCTIONS *************/ /*============ AFFINE CALCULATION ==============*/ /* AffineCalc() is passed a set of correspondences and control parameters (approx_method = 0, 1, 2 or 3, do_axes = 0 or 1) and returns a set of affine co-ordinates */ vector *AffineCalc(vector *p_refArray, vector *p_prime_refArray, vector *points1, vector *points2, int approx_method, int do_axes) { /* Global variables associated with affineCalc: vector p0, p1, p2, p3, p0', p1', p2', p3'; vector e03, eT, delta; matrix A, A'; vector pArray, p_primeArray, affineArray; int rotateaxes; Uses: int numpoints; Passed: vector *p_refArray (holds p0, p1, p2, p3) vector *p_prime_refArray (holds p0', p1', p2', p3') vector *points1 (holds numpoints correspondences in image i) vector *points2 (holds numpoints correspondences in image i) Returns: vector *affineArray (holds the 3D co-ordinates of numpoints points) */ int i; VSET(p0, p_refArray[0]); VSET(p1, p_refArray[1]); VSET(p2, p_refArray[2]); VSET(p3, p_refArray[3]); pArray = points1; VSET(p0_prime, p_prime_refArray[0]); VSET(p1_prime, p_prime_refArray[1]); VSET(p2_prime, p_prime_refArray[2]); VSET(p3_prime, p_prime_refArray[3]); p_primeArray = points2; uArray = alloc_vector(numpoints); affineArray = alloc_vector(numpoints); trial0chiSquared = get_best_eT(0.0, 0.0); switch(approx_method) { case 1: adjust_delta(); trialDchiSquared = get_best_eT(delta[X], delta[Y]); apply_delta(); break; case 2: adjust_simplex_delta(0.0, 0.0, simplex_eT_interface); trialDchiSquared = get_best_eT(delta[X], delta[Y]); apply_delta(); break; case 3: adjust_warp(); trialDchiSquared = get_best_warped_eT(warp_vec[X], warp_vec[Y]); apply_warp(); break; default: trialDchiSquared = 0.0; break; } if (do_axes) adjust_axes(); calculate_constant_points(); for (i = 0; i < numpoints; i++) getAffine(pArray[i], p_primeArray[i], affineArray[i]); if (do_axes) readjust_axes(); free(uArray); return affineArray; } /* The heart of the affine calculation - given the global variables defining the transformation, this determines the affine co-ordinates of a particular correspondence p -> p'. */ void getAffine(vector p, vector p_prime, vector p_affine) { vector I, i, i_prime, vtemp, q; line M, M_star, N; matrix mtemp; double lambda, lambda1, lambda2; vector q_p0, p3_p0, p3_e03, q_e03; /* NB: The debugging info stays. Last time I took it out I regretted it. */ #if DEBUGINFO vec_print("Doing p = ", p); vec_print(" p' = ", p_prime); #endif /* Calculate the line M, the intersection of the plane passing through the eye O and the line l (from p parallel to p0p3) with the plane pi */ vtemp[X] = p[Y] - e03[Y]; vtemp[Y] = e03[X] - p[X]; vtemp[Z] = (p[X]*e03[Y]) - (e03[X]*p[Y]); #if DEBUGINFO vec_print("l: ", vtemp); #endif preApply_2d(vtemp, A, M); #if DEBUGINFO vec_print("M: ", M); #endif /* Calculate the line M*, the intersection of the plane passing through the eye O and the line l' (from p' parallel to p0'p3') with the plane pi */ vtemp[X] = p_prime[Y] - e03[Y]; vtemp[Y] = e03[X] - p_prime[X]; vtemp[Z] = (p_prime[X]*e03[Y]) - (e03[X]*p_prime[Y]); #if DEBUGINFO vec_print("l': ", vtemp);*/ #endif preApply_2d(vtemp, A_prime, M_star); #if DEBUGINFO vec_print("M*: ", M_star); #endif /* Calculate the intersection of M and M*, the projection I of P onto pi in the direction P0P3 */ VCROSS(M, M_star, I); #if DEBUGINFO vec_print("I: ", I); #endif /* Calculate X and Y affine co-ordinates (of I in the plane pi = P0P1P2) */ p_affine[X] = I[X]/check0(I[Z], "AFFINE-CALC, I[Z]"); #if DEBUGINFO scalar_print("X: ", p_affine[X]); #endif p_affine[Y] = I[Y]/check0(I[Z], "AFFINE-CALC, I[Z]"); #if DEBUGINFO scalar_print("Y: ", p_affine[Y]); #endif if ((fabs(p_affine[X]) < TINY) && (fabs(p_affine[Y]) < TINY)) { #if DEBUGINFO fprintf(stderr, "X and Y == 0, setting q = p.\n"); #endif q[X] = p[X]; q[Y] = p[Y]; q[Z] = p[Z]; } else { /* Calculate projections of I in both images (i and i') */ postApply_2d(A, I, i); #if DEBUGINFO vec_print("i: ", i); #endif postApply_2d(A_prime, I, i_prime); #if DEBUGINFO vec_print("i': ", i_prime); #endif /* Construct lambda = -det(m1)/det(m2) where m1 = ( xp yp 1 ) ( x0' y0' 1 ) ( xi' yi' zi' ) m2 = ( xp yp 1 ) ( x0 y0 1 ) ( xi yi zi ) */ mtemp[0][0] = p[X]; mtemp[1][0] = p[Y]; mtemp[2][0] = 1.0; mtemp[0][1] = p0_prime[X]; mtemp[1][1] = p0_prime[Y]; mtemp[2][1] = 1.0; mtemp[0][2] = i_prime[X]; mtemp[1][2] = i_prime[Y]; mtemp[2][2] = i_prime[Z]; #if DEBUGINFO matrix_print("mtemp: ", mtemp);*/ #endif lambda1 = matrixDet_2d(mtemp); #if DEBUGINFO scalar_print("lambda1: ", lambda1); #endif mtemp[0][1] = p0[X]; mtemp[1][1] = p0[Y]; mtemp[0][2] = i[X]; mtemp[1][2] = i[Y]; mtemp[2][2] = i[Z]; #if DEBUGINFO matrix_print("mtemp: ", mtemp); #endif lambda2 = matrixDet_2d(mtemp); #if DEBUGINFO scalar_print("lambda2: ", lambda2); #endif if (fabs(lambda1 - lambda2) < TINY) lambda = 1; else lambda = -lambda1/check0(lambda2, "AFFINE_CALC: lambda"); #if DEBUGINFO scalar_print("lambda: ", lambda); #endif /* Calculate homogeneous co-ordinates of N, the line through P parallel to P0I */ N[X] = lambda*(p0[Y]*i[Z] - i[Y]) + (p0_prime[Y]*i_prime[Z] - i_prime[Y]); N[Y] = lambda*(i[X] - p0[X]*i[Z]) + (i_prime[X] - p0_prime[X]*i_prime[Z]); N[Z] = lambda*(p0[X]*i[Y] - i[X]*p0[Y]) + (p0_prime[X]*i_prime[Y] - i_prime[X]*p0_prime[Y]); #if DEBUGINFO vec_print("N: ", N); #endif /* Calculate co-ordinates of q, the projection of the intersection of N and P0P3 */ q[X] = (p3[X] - p0[X])*N[Z] - (p0[X]*p3[Y] - p3[X]*p0[Y])*N[Y]; q[Y] = (p0[X]*p3[Y] - p3[X]*p0[Y])*N[X] - (p0[Y] - p3[Y])*N[Z]; q[Z] = (p0[Y] - p3[Y])*N[Y] - (p3[X] - p0[X])*N[X]; #if DEBUGINFO vec_print("q: ", q); #endif /* Homogenize co-ordinates of q */ q[X] /= check0(q[Z], "Q-CALC (x)"); q[Y] /= check0(q[Z], "Q-CALC (y)"); q[Z] = 1.0; #if DEBUGINFO vec_print("q(h): ", q); #endif } /* Calculate Z affine co-ordinate of P */ /* now we need to get the cross product of the points p0, p3, q and e03 ... Now, these points *should* be collinear -> we can take a cross product. In practice, if they are not collinear, bad things happen. However since we have intersected N with p0p3 to get q and e03 is auto- matically on p0p3, they should be collinear in every (ab)normal situation. ***NOTE*** The Z-axis weirdness happens here. It happens because we're getting bad values for q (that lie _beyond_ e03 for some reason). Also, the fact that we only use the Y-coordinates of q, p0, p3 and e03 won't help. I can't think of a way to use the X and Y coordinates unless it's by using a dot-product? Huzzah! Maybe this will work better ... (qp0 . p3p0)*(p3e03 . p3e03) / (p3p0 . p3p0)*(qe03 . p3e03) */ /* Memo: This didn't work ... spectacularly. q_p0 = sqrt(SQR( q[X] - p0[X]) + SQR( q[Y] - p0[Y])); p3_p0 = sqrt(SQR(p3[X] - p0[X]) + SQR(p3[Y] - p0[Y])); p3_e03 = sqrt(SQR(p3[X] - e03[X]) + SQR(p3[Y] - e03[Y])); q_e03 = sqrt(SQR( q[X] - e03[X]) + SQR( q[Y] - e03[Y]));*/ VSUB(q, p0, q_p0); VSUB(p3, p0, p3_p0); VSUB(p3, e03, p3_e03); VSUB(q, e03, q_e03); p_affine[Z] = DOT2D(q_p0, p3_p0)*DOT2D(p3_e03, p3_e03); p_affine[Z] /= check0(DOT2D(p3_p0, p3_p0)*DOT2D(q_e03, p3_e03), "AFFINE_CALC for p[Z]"); /* Leave this here in case the alternative formulation dies horribly. p_affine[Z] = (q[Y] - p0[Y])*(p3[Y] - e03[Y]); p_affine[Z] /= check0((p3[Y] - p0[Y])*(q[Y] - e03[Y]), "AFFINE_CALC for p[Z]");*/ /* NB: |Z| > 5.0 indicates bad things. if (fabs(p_affine[Z]) > 5.0) { vec_print("q: ", q); scalar_print("q[Z]: ", q_homog); vec_print("p0: ", p0); vec_print("p3: ", p3); vec_print("e03: ", e03); fprintf(stderr, "p[Z] %f == Cr(%f,%f,%f,%f)\n", p_affine[Z], q_p0, p3_e03, p3_p0, q_e03); }*/ #if DEBUGINFO scalar_print("Z: ", p_affine[Z]); if (VMAG(p_affine) > 1000) { vec_print("large", p_affine); SET_VECTOR(p_affine, 0.0, 0.0, 0.0, 1.0); } #endif } /* calculate_constant_points() determines the defining parameters of the transformation and stores them in the designated global variables. They are then used for each point to generate the affine co-ords */ void calculate_constant_points(void) { line l1, l2, vline; matrix mtemp; double lambda, mu, nu; double lambda_prime, mu_prime, nu_prime; /* This routine calculates all of the constants needed for further calculation. e.g. e03 and the matrices A, A' */ get_line(p0, p3, l1); get_line(p0_prime, p3_prime, l2); line_intersect(l1, l2, e03); #if DEBUGINFO vec_print("e03: ", e03); #endif /* Construct lambda = -det(m1)/det(m2) where : */ /* m1 = ( y0-y2 y0'-y2' y0'-y1' ) ( x2-x0 x2'-x0' x1'-x0' ) (x0y2-x2y0 x0'y2'-x2'y0' x0'y1'-x1'y0') */ /* m2 = ( y0-y2 y0'-y2' y0-y1 ) ( x2-x0 x2'-x0' x1-x0 ) (x0y2-x2y0 x0'y2'-x2'y0' x0y1-x1y0) */ mtemp[0][0] = p0[Y] - p2[Y]; mtemp[0][1] = p2[X] - p0[X]; mtemp[0][2] = p0[X]*p2[Y] - p2[X]*p0[Y]; mtemp[1][0] = p0_prime[Y] - p2_prime[Y]; mtemp[1][1] = p2_prime[X] - p0_prime[X]; mtemp[1][2] = p0_prime[X]*p2_prime[Y] - p2_prime[X]*p0_prime[Y]; mtemp[2][0] = p0_prime[Y] - p1_prime[Y]; mtemp[2][1] = p1_prime[X] - p0_prime[X]; mtemp[2][2] = p0_prime[X]*p1_prime[Y] - p1_prime[X]*p0_prime[Y]; #if DEBUGINFO matrix_print("mtemp: ", mtemp); #endif lambda = -matrixDet_2d(mtemp); /* |m1| */ #if DEBUGINFO scalar_print("lambda1: ", lambda); #endif mtemp[2][0] = p0[Y] - p1[Y]; mtemp[2][1] = p1[X] - p0[X]; mtemp[2][2] = p0[X]*p1[Y] - p1[X]*p0[Y]; #if DEBUGINFO matrix_print("mtemp: ", mtemp); #endif lambda /= check0(matrixDet_2d(mtemp), "CONSTANTS (lambda)"); /* / |m2| */ #if DEBUGINFO scalar_print("lambda: ", lambda); #endif /* Calculate the vanishing line of pi in the images i and i' (the same) */ vline[X] = lambda*(p0[Y] - p1[Y]) + (p0_prime[Y] - p1_prime[Y]); vline[Y] = lambda*(p1[X] - p0[X]) + (p1_prime[X] - p0_prime[X]); vline[Z] = lambda*(p0[X]*p1[Y] - p1[X]*p0[Y]) + (p0_prime[X]*p1_prime[Y] - p1_prime[X]*p0_prime[Y]); #if DEBUGINFO vec_print("vline: ", vline); #endif /* Calculate the co-efficients of A */ lambda = 1/check0((vline[X]*p0[X] + vline[Y]*p0[Y] + vline[Z]), "A-CALC (lambda)"); #if DEBUGINFO scalar_print("lambda: ", lambda); #endif mu = 1/check0((vline[X]*p1[X] + vline[Y]*p1[Y] + vline[Z]), "A-CALC (mu)"); #if DEBUGINFO scalar_print("mu: ", mu); #endif nu = 1/check0((vline[X]*p2[X] + vline[Y]*p2[Y] + vline[Z]), "A-CALC (nu)"); #if DEBUGINFO scalar_print("nu: ", nu); #endif /* Calculate A, the projective transformation from pi to image i */ A[0][0] = mu*p1[X] - lambda*p0[X]; A[0][1] = mu*p1[Y] - lambda*p0[Y]; A[0][2] = mu - lambda; A[1][0] = nu*p2[X] - lambda*p0[X]; A[1][1] = nu*p2[Y] - lambda*p0[Y]; A[1][2] = nu - lambda; A[2][0] = lambda*p0[X]; A[2][1] = lambda*p0[Y]; A[2][2] = lambda; #if DEBUGINFO matrix_print("A: ", A); #endif /* Calculate the co-efficients of A' */ lambda_prime = 1/check0((vline[X]*p0_prime[X] + vline[Y]*p0_prime[Y] + vline[Z]), "A'-CALC (lambda')"); #if DEBUGINFO scalar_print("lambda': ", lambda_prime); #endif mu_prime = 1/check0((vline[X]*p1_prime[X] + vline[Y]*p1_prime[Y] + vline[Z]), "A'-CALC (mu')"); #if DEBUGINFO scalar_print("mu': ", mu_prime); #endif nu_prime = 1/check0((vline[X]*p2_prime[X] + vline[Y]*p2_prime[Y] + vline[Z]), "A'-CALC (nu')"); #if DEBUGINFO scalar_print("nu': ", nu_prime); #endif /* Calculate A', the projective transformation from pi to image i' */ A_prime[0][0] = mu_prime*p1_prime[X] - lambda_prime*p0_prime[X]; A_prime[0][1] = mu_prime*p1_prime[Y] - lambda_prime*p0_prime[Y]; A_prime[0][2] = mu_prime - lambda_prime; A_prime[1][0] = nu_prime*p2_prime[X] - lambda_prime*p0_prime[X]; A_prime[1][1] = nu_prime*p2_prime[Y] - lambda_prime*p0_prime[Y]; A_prime[1][2] = nu_prime - lambda_prime; A_prime[2][0] = lambda_prime*p0_prime[X]; A_prime[2][1] = lambda_prime*p0_prime[Y]; A_prime[2][2] = lambda_prime; #if DEBUGINFO matrix_print("A': ", A_prime); #endif } /* get_best_eT utilises a least-sqares technique to find the best approximate to eT for the given adjustment delta. It returns the (inevitable) error associated with this eT. */ double get_best_eT(double deltax, double deltay) { /* This procedure gets the best eT for the given pArray, p_primeArray and delta. It returns the sum of d^2, otherwise known as chi squared. */ double chi_squared, mod_u, d; double a11, a12, a13, a21, a22, a23; double *p, *p_, *u; int i; a11 = 0; a12 = 0; a13 = 0; a21 = 0; a22 = 0; a23 = 0; for (i = 0; i < numpoints; i++) { u = uArray[i]; p = pArray[i]; p_ = p_primeArray[i]; u[X] = -(deltay + p_[Y] - p[Y]); u[Y] = (deltax + p_[X] - p[X]); mod_u = sqrt(u[X]*u[X] + u[Y]*u[Y]); u[X] /= check0(mod_u, "mod u"); u[Y] /= check0(mod_u, "mod u"); a11 += 2*u[X]*u[X]; a12 += 2*u[X]*u[Y]; a13 += 2*u[X]*(u[X]*p[X] + u[Y]*p[Y]); a21 += 2*u[Y]*u[X]; a22 += 2*u[Y]*u[Y]; a23 += 2*u[Y]*(u[X]*p[X] + u[Y]*p[Y]); } /* Okay, we now have a set of equations of the form a11eTx + a12eTy = a13 a21eTx + a22eTy = a23 These give eTy = (a13a21 - a11a23)/(a12a21 - a11a22) eTx = (a13 - a12eTy)/a11 */ #if DEBUGINFO fprintf(stderr, "Trying to solve : %16.11feTx + %16.11feTy = %16.11f\n", a11, a12, a13); fprintf(stderr, " %16.11feTx + %16.11feTy = %16.11f\n", a21, a22, a23); #endif if (a11 == 0) { eT[Y] = a13/check0(a12, "non-determinative equations for eT!"); eT[X] = (a23 - a22*eT[Y])/check0(a21, "non-determinative equations for eT!"); } else { eT[Y] = (a13*a21 - a11*a23)/check0(a12*a21 - a11*a22, "eT.y"); eT[X] = (a13 - a12*eT[Y])/check0(a11, "a11"); } chi_squared = 0.0; for (i = 0; i < numpoints; i++) { p = pArray[i]; u = uArray[i]; d = u[X]*(p[X] - eT[X]) + u[Y]*(p[Y] - eT[Y]); chi_squared += d*d; } return chi_squared; } /* get_best_warped_eT utilises the same least-sqares technique to find best approximation to eT for the warp yaw, pitch, (roll eventually?). It returns the error associated with this eT. */ double get_best_warped_eT(double yaw, double pitch) { /* This procedure gets the best eT for the given pArray, p_primeArray and rotation. It returns the sum of d^2, otherwise known as chi squared. */ double chi_squared, mod_u, d; double a11, a12, a13, a21, a22, a23; double *p, *p_, *u; vector warped_p_; int i; prepare_warp_matrix(yaw, pitch); /* precalculate the warp matrix */ a11 = 0; a12 = 0; a13 = 0; a21 = 0; a22 = 0; a23 = 0; for (i = 0; i < numpoints; i++) { u = uArray[i]; p = pArray[i]; p_ = p_primeArray[i]; warp_point(p_, &warped_p_); /* warp the points in image 2 to match */ u[X] = -(warped_p_[Y] - p[Y]); u[Y] = (warped_p_[X] - p[X]); mod_u = sqrt(u[X]*u[X] + u[Y]*u[Y]); u[X] /= check0(mod_u, "mod u"); u[Y] /= check0(mod_u, "mod u"); a11 += 2*u[X]*u[X]; a12 += 2*u[X]*u[Y]; a13 += 2*u[X]*(u[X]*p[X] + u[Y]*p[Y]); a21 += 2*u[Y]*u[X]; a22 += 2*u[Y]*u[Y]; a23 += 2*u[Y]*(u[X]*p[X] + u[Y]*p[Y]); } /* Okay, we now have a set of equations of the form a11eTx + a12eTy = a13 a21eTx + a22eTy = a23 These give eTy = (a13a21 - a11a23)/(a12a21 - a11a22) eTx = (a13 - a12eTy)/a11 */ #if DEBUGINFO fprintf(stderr, "Trying to solve : %16.11feTx + %16.11feTy = %16.11f\n", a11, a12, a13); fprintf(stderr, " %16.11feTx + %16.11feTy = %16.11f\n", a21, a22, a23); #endif eT[Y] = (a13*a21 - a11*a23)/check0(a12*a21 - a11*a22, "eT.y"); eT[X] = (a13 - a12*eT[Y])/check0(a11, "a11"); chi_squared = 0.0; for (i = 0; i < numpoints; i++) { p = pArray[i]; u = uArray[i]; d = u[X]*(p[X] - eT[X]) + u[Y]*(p[Y] - eT[Y]); chi_squared += d*d; } return chi_squared; } void prepare_warp_matrix(double yaw, double pitch) { matrix tempR; getRotationMatrix(tempR,Y,sin(- yaw*M_PI/180.0),cos(- yaw*M_PI/180.0)); getRotationMatrix(warpM,X,sin(-pitch*M_PI/180.0),cos(pitch*M_PI/180.0)); matrixMult_3d(tempR, warpM, warpM); #if DEBUGINFO matrix_print("warpM: ", warpM); #endif } void warp_point(vector thePoint, vector *warped_point) { vector tempresult; tempresult[X] = warpM[0][0]*thePoint[X] + warpM[1][0]*thePoint[Y] + warpM[2][0]*thePoint[Z]; tempresult[Y] = warpM[0][1]*thePoint[X] + warpM[1][1]*thePoint[Y] + warpM[2][1]*thePoint[Z]; tempresult[Z] = warpM[0][2]*thePoint[X] + warpM[1][2]*thePoint[Y] + warpM[2][2]*thePoint[Z]; if (tempresult[Z] < 0) { /* NB: This is a Very Bad Thing but it will be silent unless DEBUGINFO is on. This may not be wise, but the warnings were bugging me. */ #if DEBUGINFO fprintf(stderr, "warp_point: point behind viewplane!!!\n"); vec_print("w_p: ", tempresult); #endif } (*warped_point)[X] = tempresult[X] / check0(tempresult[Z], "warp_point: w_p[Z]"); (*warped_point)[Y] = tempresult[Y] / check0(tempresult[Z], "warp_point: w_p[Z]"); (*warped_point)[Z] = 1.0; } /* adjust_delta tries five steepest_delta calls starting at different procedures and returns the answer from the one which gives the smallest chi^2. NB: this is a Cheap Hack(TM) but Kevin likes it. */ void adjust_delta(void) { #define DELTA_INITIAL_VARIATION 0.05 double founddelta[5][2]; double foundchi2[5]; double bestchi2; int i, bestdelta; bestchi2 = 999999.9; bestdelta = 0; for (i = 0; i < 5; i++) { switch(i) { case 0: foundchi2[i] = steepest_delta(0, 0); break; case 1: foundchi2[i] = steepest_delta(DELTA_INITIAL_VARIATION, 0); break; case 2: foundchi2[i] = steepest_delta(-DELTA_INITIAL_VARIATION, 0); break; case 3: foundchi2[i] = steepest_delta(0, DELTA_INITIAL_VARIATION); break; case 4: foundchi2[i] = steepest_delta(0, -DELTA_INITIAL_VARIATION); break; } founddelta[i][X] = delta[X]; founddelta[i][Y] = delta[Y]; if (foundchi2[i] < bestchi2) { bestchi2 = foundchi2[i]; bestdelta = i; } } delta[X] = founddelta[bestdelta][X]; delta[Y] = founddelta[bestdelta][Y]; } /* steepest_delta() performs an iterative technique upon delta in order to determine the best approximation so that the rotational motion of the camera may be accounted for. It uses a steepest-descent algorithm to do so, and the aim is to find the value of delta that minimizes the return value of get_best_eT(). (may be accomplished without any prior knowledge of what get_best_eT() actually does) */ double steepest_delta(double initial_x, double initial_y) { #define GRAD_EPSILON 1e-14 #define CHI2_TOLERANCE 0.0001 #define MAX_ITERATIONS 100 #define INITIAL_STEP 0.1 double oldchi_square, chi_square; double step_size, grad_size; vector chi_grad, lastdelta; lastdelta[X] = 0; lastdelta[Y] = 0; delta[X] = initial_x; delta[Y] = initial_y; grad_size = 1.0; step_size = INITIAL_STEP; iterations = 0; chi_square = 999999999999.9; /* large number */ #if DEBUGINFO text_print("Adjusting delta ..."); #endif do { iterations++; oldchi_square = chi_square; chi_square = get_best_eT(delta[X], delta[Y]); while (chi_square > oldchi_square) { /* if we take a bad step, reduce the step size and try again from the old delta. */ #if DEBUGINFO fprintf(stderr, " chi^2(%20.17g) step(%10.7g) delta(%10.7f,%10.7f) REJECTING\n", chi_square, step_size, delta[X], delta[Y]); #endif step_size /= 2; delta[X] = lastdelta[X] - step_size * (chi_grad[X] / grad_size); delta[Y] = lastdelta[Y] - step_size * (chi_grad[Y] / grad_size); chi_square = get_best_eT(delta[X], delta[Y]); } #if DEBUGINFO fprintf(stderr, "%4d: chi^2(%20.17g) step(%10.7g) delta(%10.7f,%10.7f) eT(%10.7f,%10.7f)", iterations, chi_square, step_size, delta[X], delta[Y], eT[X], eT[Y]); #endif if ((fabs(chi_square - oldchi_square)/chi_square < CHI2_TOLERANCE) && (step_size < INITIAL_STEP/1024)) { /* if not improving much, and quite close, stop! */ #if DEBUGINFO fprintf(stderr, "\n"); #endif break; } chi_grad[X] = (chi_square - get_best_eT((delta[X] - GRAD_EPSILON), delta[Y]))/GRAD_EPSILON; chi_grad[Y] = (chi_square - get_best_eT(delta[X], (delta[Y] - GRAD_EPSILON)))/GRAD_EPSILON; grad_size = sqrt(SQR(chi_grad[X]) + SQR(chi_grad[Y])); if (grad_size == 0) { /* we've found a minimum, stop. Shouldn't be any maximums, so safe. */ #if DEBUGINFO fprintf(stderr, "\n"); #endif break; } #if DEBUGINFO fprintf(stderr, " chi'(%10.7g,%10.7g)\n", chi_grad[X], chi_grad[Y]); #endif /* this code does delta -= step_size*chi_grad */ lastdelta[X] = delta[X]; lastdelta[Y] = delta[Y]; delta[X] -= step_size * (chi_grad[X]/grad_size); delta[Y] -= step_size * (chi_grad[Y]/grad_size); } while (iterations < MAX_ITERATIONS); #if DEBUGINFO fprintf(stderr, "%d iterations: delta (%20.17f,%20.17f)\n", i, delta[X], delta[Y]); #endif return chi_square; } /* adjust_warp tries five steepest_warp calls starting at different points and returns the answer from the one which gives the smallest chi^2. NB: this is a Cheap Hack(TM) but Kevin likes it. */ void adjust_warp(void) { #define WARP_INITIAL_VARIATION 1.0 double foundwarp[5][2]; double foundchi2[5]; double bestchi2; int i, bestwarp; bestchi2 = 99999999999.9; bestwarp = 0; for (i = 0; i < 5; i++) { switch(i) { case 0: foundchi2[i] = steepest_warp(0, 0); break; case 1: foundchi2[i] = steepest_warp(WARP_INITIAL_VARIATION, 0); break; case 2: foundchi2[i] = steepest_warp(-WARP_INITIAL_VARIATION, 0); break; case 3: foundchi2[i] = steepest_warp(0, WARP_INITIAL_VARIATION); break; case 4: foundchi2[i] = steepest_warp(0, -WARP_INITIAL_VARIATION); break; } foundwarp[i][X] = warp_vec[X]; foundwarp[i][Y] = warp_vec[Y]; if (foundchi2[i] < bestchi2) { bestchi2 = foundchi2[i]; bestwarp = i; } } warp_vec[X] = foundwarp[bestwarp][X]; warp_vec[Y] = foundwarp[bestwarp][Y]; } /* steepest_warp() performs an iterative technique upon the warp in order to determine the best approximation so that the rotational motion of the camera may be accounted for. It uses a steepest-descent algorithm to do so, and the aim is to find the rotational warp that minimizes the return value of get_best_warped_eT(). (may be accomplished without any prior knowledge of what get_best_warped_eT() actually does) */ double steepest_warp(double initial_x, double initial_y) { #define WARP_GRAD_EPSILON 1e-06 #define WARP_CHI2_TOLERANCE 0.0001 #define WARP_MAX_ITERATIONS 100 #define WARP_INITIAL_STEP 10.0 double oldchi_square, chi_square; double step_size, grad_size; vector chi_grad, last_warp; last_warp[X] = 0; last_warp[Y] = 0; warp_vec[X] = initial_x; /* yaw */ warp_vec[Y] = initial_y; /* pitch */ grad_size = 1.0; step_size = WARP_INITIAL_STEP; iterations = 0; chi_square = 99999999999.9; /* large number */ #if DEBUGINFO text_print("Adjusting the warp ..."); #endif do { iterations++; oldchi_square = chi_square; chi_square = get_best_warped_eT(warp_vec[X], warp_vec[Y]); while (chi_square > oldchi_square) { /* if we take a bad step, reduce the step size and try again from the old warp. */ #if DEBUGINFO fprintf(stderr, " chi^2(%20.17g) step(%10.7g) warp(%10.7f,%10.7f) REJECTING\n", chi_square, step_size, warp_vec[X], warp_vec[Y]); #endif step_size /= 2; warp_vec[X] = last_warp[X] - step_size * (chi_grad[X] / grad_size); warp_vec[Y] = last_warp[Y] - step_size * (chi_grad[Y] / grad_size); chi_square = get_best_warped_eT(warp_vec[X], warp_vec[Y]); } #if DEBUGINFO fprintf(stderr, "%4d: chi^2(%20.17g) step(%10.7g) warp(%10.7f,%10.7f) eT(%10.7f,%10.7f)", iterations, chi_square, step_size, warp_vec[X], warp_vec[Y], eT[X], eT[Y]); #endif if ((fabs(chi_square - oldchi_square)/chi_square < WARP_CHI2_TOLERANCE) && (step_size < WARP_INITIAL_STEP/1024)) { /* if not improving much, and quite close, stop! */ #if DEBUGINFO fprintf(stderr, "\n"); #endif break; } chi_grad[X] = (chi_square - get_best_warped_eT((warp_vec[X] - WARP_GRAD_EPSILON),warp_vec[Y]))/WARP_GRAD_EPSILON; chi_grad[Y] = (chi_square - get_best_warped_eT(warp_vec[X],(warp_vec[Y] - WARP_GRAD_EPSILON)))/WARP_GRAD_EPSILON; grad_size = sqrt(SQR(chi_grad[X]) + SQR(chi_grad[Y])); if (grad_size == 0) { /* we've found a minimum, stop. */ #if DEBUGINFO fprintf(stderr, "\n"); #endif break; } #if DEBUGINFO fprintf(stderr, " chi'(%10.7g,%10.7g)\n", chi_grad[X], chi_grad[Y]); #endif /* warp_vec -= step_size*chi_grad */ last_warp[X] = warp_vec[X]; last_warp[Y] = warp_vec[Y]; warp_vec[X] -= step_size * (chi_grad[X]/grad_size); warp_vec[Y] -= step_size * (chi_grad[Y]/grad_size); } while (iterations < WARP_MAX_ITERATIONS); #if IMPORTANTDEBUGINFO fprintf(stderr, "%d iterations: warp_vec (%20.17f,%20.17f)\n", iterations, warp_vec[X], warp_vec[Y]); #endif return chi_square; } double simplex_eT_interface(double *vec) { return get_best_eT(vec[X], vec[Y]); } /* adjust_simplex_delta uses the simplex method in two dimensions to minimize the value of chi^2 on delta in 2D. Initial value is given. */ void adjust_simplex_delta(double initialx, double initialy, double (*evalfunc)(double *vec)) { #define N_DIM 2 #define N_PTS N_DIM+1 #define EVALUATE_MAX 5000 #define ALPHA 1.0 #define BETA 0.5 #define GAMMA 2.0 #define DESIRED_TOLERANCE 0.001 #define INITIAL_DELTA 0.02 int i, j, lo_point, hi_point, nexthi_point; double pointsum[N_DIM]; double point[N_PTS][N_DIM]; double func[N_PTS]; double sum, current_tolerance, attempt_func, hi_value; /* initialise number of f evals to zero */ n_evals = 0; /* initial points form an equilateral triangle around initial_xy */ point[0][X] = initialx; point[0][Y] = initialy + INITIAL_DELTA; point[1][X] = initialx + INITIAL_DELTA*(sqrt(3.0)/2.0); point[1][Y] = initialy - INITIAL_DELTA*(0.5); point[2][X] = initialx - INITIAL_DELTA*(sqrt(3.0)/2.0); point[2][Y] = initialy - INITIAL_DELTA*(0.5); /* for each dimension (X, Y) */ for (j = 0; j < N_DIM; j++) { /* take the sum of the X or Y co-ords of all 3 points and store it in pointsum[X, Y] */ for (sum = 0.0, i = 0; i < N_PTS; i++) sum += point[i][j]; pointsum[j] = sum; } /* initialise the array func[i] to hold function values at the points */ for (i = 0; i < N_PTS; i++) { func[i] = (*evalfunc)(point[i]); } /* loop: begin a new iteration */ for (;;) { /* loop over points to determine the ones with the lowest and */ /* highest f values. Initialise lo_p to 0, hi_p to the bigger */ /* of p0 and p1, nexthi_p to the lower of p0 and p1. */ lo_point = 0; if (func[0] > func[1]) { hi_point = 0; nexthi_point = 1; } else { hi_point = 1; nexthi_point = 0; } /* actually perform the above loop. Update lo_p, hi_p, nexthi_p if */ /* necessary. Upon exit these three values are correct. */ for (i = 0; i < N_PTS; i++) { if (func[i] <= func[lo_point]) lo_point = i; if (func[i] > func[hi_point]) { nexthi_point = hi_point; hi_point = i; } else if (func[i] > func[nexthi_point]) nexthi_point = i; } /* compute the current fractional tolerance. Return the best value as */ /* delta[X,Y] if we're within the desired tolerance OR we've exceeded */ /* the maximum number of evaluations allowed. */ current_tolerance = 2.0 * fabs(func[hi_point] - func[lo_point]) / (fabs(func[hi_point]) + fabs(func[lo_point])); if ((current_tolerance < DESIRED_TOLERANCE) || (n_evals >= EVALUATE_MAX)) { delta[X] = point[lo_point][X]; delta[Y] = point[lo_point][Y]; break; } /* try a reflection through the highest point */ attempt_func = simplex_try_point(point, func, pointsum, evalfunc, hi_point, &n_evals, -ALPHA); /* if the new point is better than the best point, try to extrapolate more in that direction */ if (attempt_func <= func[lo_point]){ attempt_func = simplex_try_point(point, func, pointsum, evalfunc, hi_point, &n_evals, GAMMA); } else if (attempt_func >= func[nexthi_point]) { /* if it's worse than the second worst point, save the worst value */ hi_value = func[hi_point]; /* look for a lower point between the two tried */ attempt_func = simplex_try_point(point, func, pointsum, evalfunc, hi_point, &n_evals, BETA); /* if unsuccessful, contract around the lowest point */ if (attempt_func >= hi_value) { /* for every point not the lowest, contract */ for (i = 0; i < N_PTS; i++) { if (i != lo_point) { /* calculate new point */ for (j = 0; j < N_DIM; j++) { pointsum[j] = 0.5*(point[i][j] + point[lo_point][j]); point[i][j] = pointsum[j]; } /* calculate function value at new point */ func[i] = (*evalfunc)(pointsum); } } n_evals += N_PTS - 1; /* recompute pointsum[] as at start */ for (j = 0; j < N_DIM; j++) { for (sum = 0.0, i = 0; i < N_PTS; i++) sum += point[i][j]; pointsum[j] = sum; } } } else { /* If we've found a smaller point between the two tried, update function evals and go back to next iteration */ --n_evals; } } /* the main for loop never terminates normally, only through break; */ } /* simplex_try_point extrapolates by a factor (factor) through the face of the simplex across from the high point, and if it finds a better point replaces the high point with the new (extrapolated) point. */ double simplex_try_point(double point[N_PTS][N_DIM], double func[N_PTS], double pointsum[N_DIM], double (*evalfunc)(double *vec), int hi_point, int *n_evals, double factor) { int j; double trypoint[N_DIM]; double factor1, factor2, tryvalue; /* these factors used to construct the extrapolated point */ factor1 = (1.0 - factor)/N_DIM; factor2 = factor1 - factor; /* construct the extrapolated point */ for (j = 0; j < N_DIM; j++) trypoint[j] = pointsum[j]*factor1 - point[hi_point][j]*factor2; /* evaluate the function at the extrapolated point */ tryvalue = (*evalfunc)(trypoint); (*n_evals)++; /* if it's better than the current worst point */ if (tryvalue < func[hi_point]) { func[hi_point] = tryvalue; for (j = 0; j < N_DIM; j++) { pointsum[j] += trypoint[j] - point[hi_point][j]; point[hi_point][j] = trypoint[j]; } } return tryvalue; } /* apply_delta() takes the delta that has been found by the successive approximation technique and pre-processes the correspondences accordingly. It uses the procedure fix(v) to apply the delta correction to the vector v. */ void apply_delta(void) { int i; fix(p0_prime); fix(p1_prime); fix(p2_prime); fix(p3_prime); for (i = 0; i < numpoints; i++) fix(p_primeArray[i]); } /* apply_warp() takes the warp that has been found by the successive approximation technique and adjusts the correspondences accordingly. */ void apply_warp(void) { int i; prepare_warp_matrix(warp_vec[X], warp_vec[Y]); warp_point(p0_prime, &p0_prime); warp_point(p1_prime, &p1_prime); warp_point(p2_prime, &p2_prime); warp_point(p3_prime, &p3_prime); for (i = 0; i < numpoints; i++) warp_point(p_primeArray[i], &p_primeArray[i]); } /* fix() adjusts the vector v to account for the rotational camera motion by using delta. */ void fix(vector toFix) { toFix[X] += delta[X]; toFix[Y] += delta[Y]; } /* adjust_axes() determines the axis which has the most parallax on it between the two images, and rotates the reference points p0-3 and p'0-3 to make this axis p0-p3 while preserving the handedness of the co-ordinate system */ void adjust_axes(void) { int bestAxis; line l1, l2; vector e1, e2, e3, tempvect; double d1, d2, d3; get_line(p0, p1, l1); get_line(p0_prime, p1_prime, l2); line_intersect(l1, l2, e1); get_line(p0, p2, l1); get_line(p0_prime, p2_prime, l2); line_intersect(l1, l2, e2); get_line(p0, p3, l1); get_line(p0_prime, p3_prime, l2); line_intersect(l1, l2, e3); d1 = SQR(e1[X]) + SQR(e1[Y]); d2 = SQR(e2[X]) + SQR(e2[Y]); d3 = SQR(e3[X]) + SQR(e3[Y]); /* We look for the **closest** vanishing point, which is the one with the **most** parallax on it. */ if (d1 < d3) if (d1 < d2) bestAxis = 1; else bestAxis = 2; else if (d2 < d3) bestAxis = 2; else bestAxis = 3; switch (bestAxis) { case 0:{ text_print("ERROR! BestAxis == 0!"); rotateaxes = 0; break; } case 1:{ /* rotate p3 -> p2 -> p1 -> p3 so that old p1 is now in p3 position */ VSET(tempvect, p3); VSET(p3, p1); VSET(p1, p2); VSET(p2, tempvect); VSET(tempvect, p3_prime); VSET(p3_prime, p1_prime); VSET(p1_prime, p2_prime); VSET(p2_prime, tempvect); rotateaxes = 1; break; } case 2: { /* rotate p3 -> p1 -> p2 -> p3 so that old p2 is now in p3 position */ VSET(tempvect, p3); VSET(p3, p2); VSET(p2, p1); VSET(p1, tempvect); VSET(tempvect, p3_prime); VSET(p3_prime, p2_prime); VSET(p2_prime, p1_prime); VSET(p1_prime, tempvect); rotateaxes = 2; break; } case 3: { /* no rotation necessary, p3 already has good parallax */ rotateaxes = 3; break; } } } /* readjust_axes() restores the affine coordinates back to the original axes that were presented to the program. (undoes the effects of adjust_axes) */ void readjust_axes(void) { int i; vector tempvect; for (i = 0; i < numpoints; i++) { VSET(tempvect, affineArray[i]); switch(rotateaxes){ case 0:{ text_print("ERROR! rotateaxes == 0"); break; } case 1:{ affineArray[i][X] = tempvect[Z]; affineArray[i][Y] = tempvect[X]; affineArray[i][Z] = tempvect[Y]; break; } case 2:{ affineArray[i][X] = tempvect[Y]; affineArray[i][Y] = tempvect[Z]; affineArray[i][Z] = tempvect[X]; break; } case 3:{ /* no adjustment necessary */ break; } } } } /*======== ANALYSIS FUNCTIONS =========*/ /* average_error() returns the average error in % between the specified reference and actual affine co-ordinates. */ float average_error(vector *refpoints, vector *affpoints) { float totError, maxValue; int i, index; maxValue = 0.0; for (i = 0; i < numpoints; i++) for (index = X; index <= Z; index++) if (maxValue < fabs(refpoints[i][index])) maxValue = fabs(refpoints[i][index]); if (maxValue == 0) fatalError("average_error: maxvalue of 0.0"); totError = 0.0; for(i = 0; i < numpoints; i++) for (index = X; index <= Z; index++) totError += fabs(affpoints[i][X] - refpoints[i][X]); return (100.0*totError/maxValue) / (numpoints*3); } /*====== PROJECTION FUNCTIONS ======*/ /* rotate_project3d() takes a specified 3D view and a set of 3D points which are then projected onto the virtual image plane using a 3D->2D projection matrix for each view. */ void rotate_project3d(double yaw, double pitch, double rollc, vector vf1, vector vf2, vector vt1, vector vt2, vector *points_3d, vector **p1_ref, vector **p1_corr, vector **p2_ref, vector **p2_corr) { vector view_dir, up_dir, right_dir; vector new_viewdir, new_updir, new_rightdir; double cos_pitch, cos_yaw, sin_pitch, sin_yaw; int i; numpoints -= 4; /* adjust for the reference points */ *p1_ref = alloc_vector(4); *p2_ref = alloc_vector(4); *p1_corr = alloc_vector(numpoints); *p2_corr = alloc_vector(numpoints); /* I had enormous problems with trying to rotate the points around so that the view vector ended up along the Z axis. So now, I'm creating a new basis system with Z along the view vector, Y along its upvector and X along its rightvector. Maybe it will work better. Then again ... */ /* First for the first camera ... */ VSUB(vt1, vf1, view_dir); VTIMES(1.0/check0(VMAG(view_dir), "view_dir in project3d"), view_dir, view_dir); SET_VECTOR(up_dir, 0.0, 0.0, 1.0, 1.0); VCROSS(view_dir, up_dir, right_dir); VTIMES(1.0/check0(VMAG(right_dir), "right_dir in project3d"), right_dir, right_dir); VCROSS(right_dir, view_dir, up_dir); VTIMES(1.0/check0(VMAG(up_dir), "up_dir in project3d"), up_dir, up_dir); #if DEBUGINFO vec_print("view_dir:", view_dir); vec_print("right_dir:", right_dir); vec_print("up_dir:", up_dir); scalar_print("v_d . r_d:", VDOT(view_dir, right_dir)); scalar_print("v_d . u_d:", VDOT(view_dir, up_dir)); scalar_print("r_d . u_d:", VDOT(right_dir, up_dir)); #endif /* Now that we have the basis system for the first camera worked out, use it to determine the projections of the 3D points (in the first camera). */ for (i = 0; i < 4; i++) project_point(vf1, right_dir, up_dir, view_dir, points_3d[i], (*p1_ref)[i]); for (i = 0; i < numpoints; i++) project_point(vf1, right_dir, up_dir, view_dir, points_3d[i + 4], (*p1_corr)[i]); /* Now for the second camera ... */ VSUB(vt2, vf2, view_dir); VTIMES(1.0/check0(VMAG(view_dir), "view_dir in project3d"), view_dir, view_dir); SET_VECTOR(up_dir, 0.0, 0.0, 1.0, 1.0); VCROSS(view_dir, up_dir, right_dir); VTIMES(1.0/check0(VMAG(right_dir), "right_dir in project3d"), right_dir, right_dir); VCROSS(right_dir, view_dir, up_dir); VTIMES(1.0/check0(VMAG(up_dir), "up_dir in project3d"), up_dir, up_dir); #if DEBUGINFO vec_print("view_dir:", view_dir); vec_print("right_dir:", right_dir); vec_print("up_dir:", up_dir); scalar_print("v_d . r_d:", VDOT(view_dir, right_dir)); scalar_print("v_d . u_d:", VDOT(view_dir, up_dir)); scalar_print("r_d . u_d:", VDOT(right_dir, up_dir)); #endif /* we have the basis system for the unrotated camera, now rotate it as a rigid body */ cos_pitch = cos(pitch*M_PI/180.0); cos_yaw = cos(yaw*M_PI/180.0); sin_pitch = sin(pitch*M_PI/180.0); sin_yaw = sin(yaw*M_PI/180.0); /* new view = cos(yaw)cos(pitch)view + cos(pitch)sin(yaw)right + sin(pitch)up */ VTIMES(cos_yaw*cos_pitch, view_dir, new_viewdir); VFRACADD(new_viewdir, cos_pitch*sin_yaw, right_dir); VFRACADD(new_viewdir, sin_pitch, up_dir); /* new up = cos(pitch)up - sin(pitch)cos(yaw)view - sin(pitch)sin(yaw)right */ VTIMES(cos_pitch, up_dir, new_updir); VFRACADD(new_updir, -sin_pitch*cos_yaw, view_dir); VFRACADD(new_updir, -sin_pitch*sin_yaw, right_dir); /* new right = cos(yaw)right - sin(yaw)view */ VTIMES(cos_yaw, right_dir, new_rightdir); VFRACADD(new_rightdir, -sin_yaw, view_dir); /* renormalise the modified view, up and right vectors */ VTIMES(1.0/check0(VMAG(new_viewdir), "new_viewdir in project3d"), new_viewdir, new_viewdir); VTIMES(1.0/check0(VMAG(new_updir), "new_updir in project3d"), new_updir, new_updir); VTIMES(1.0/check0(VMAG(new_rightdir), "new_rightdir in project3d"), new_rightdir, new_rightdir); #if DEBUGINFO vec_print("new view_dir:", new_viewdir); vec_print("new right_dir:", new_rightdir); vec_print("new up_dir:", new_updir); scalar_print("new v_d . r_d:", VDOT(new_viewdir, new_rightdir)); scalar_print("new v_d . u_d:", VDOT(new_viewdir, new_updir)); scalar_print("new r_d . u_d:", VDOT(new_rightdir, new_updir)); #endif /* Now that we have the (rotated) basis system for the second camera worked out, use it to determine the projections of the 3D points (in the second camera). */ for (i = 0; i < 4; i++) project_point(vf2, new_rightdir, new_updir, new_viewdir, points_3d[i], (*p2_ref)[i]); for (i = 0; i < numpoints; i++) project_point(vf2, new_rightdir, new_updir, new_viewdir, points_3d[i + 4], (*p2_corr)[i]); /* ********* NB *********** Must apply the roll coefficient here, don't forget */ } /* project3d() takes a specified 3D view and a set of 3D points which are then projected onto the virtual image plane using a 3D->2D projection matrix for each view. */ void project3d(vector vf1, vector vf2, vector vt1, vector vt2, double rollc, vector *points_3d, vector **p1_ref, vector **p1_corr, vector **p2_ref, vector **p2_corr) { vector view_dir, up_dir, right_dir; int i; numpoints -= 4; /* adjust for the reference points */ *p1_ref = alloc_vector(4); *p2_ref = alloc_vector(4); *p1_corr = alloc_vector(numpoints); *p2_corr = alloc_vector(numpoints); /* I had enormous problems with trying to rotate the points around so that the view vector ended up along the Z axis. So now, I'm creating a new basis system with Z along the view vector, Y along its upvector and X along its rightvector. This allows direct projection without any time-consuming rotation for every point. Maybe it will work better. */ /* First for the first camera ... */ VSUB(vt1, vf1, view_dir); VTIMES(1.0/check0(VMAG(view_dir), "view_dir in project3d"), view_dir, view_dir); SET_VECTOR(up_dir, 0.0, 0.0, 1.0, 1.0); VCROSS(view_dir, up_dir, right_dir); VTIMES(1.0/check0(VMAG(right_dir), "right_dir in project3d"), right_dir, right_dir); VCROSS(right_dir, view_dir, up_dir); VTIMES(1.0/check0(VMAG(up_dir), "up_dir in project3d"), up_dir, up_dir); #if DEBUGINFO vec_print("view_dir:", view_dir); vec_print("right_dir:", right_dir); vec_print("up_dir:", up_dir); scalar_print("v_d . r_d:", VDOT(view_dir, right_dir)); scalar_print("v_d . u_d:", VDOT(view_dir, up_dir)); scalar_print("r_d . u_d:", VDOT(right_dir, up_dir)); #endif /* Now that we have the basis system for the first camera worked out, use it to determine the projections of the 3D points (in the first camera). */ for (i = 0; i < 4; i++) project_point(vf1, right_dir, up_dir, view_dir, points_3d[i], (*p1_ref)[i]); for (i = 0; i < numpoints; i++) project_point(vf1, right_dir, up_dir, view_dir, points_3d[i + 4], (*p1_corr)[i]); /* Now for the second camera ... */ VSUB(vt2, vf2, view_dir); VTIMES(1.0/check0(VMAG(view_dir), "view_dir in project3d"), view_dir, view_dir); SET_VECTOR(up_dir, 0.0, 0.0, 1.0, 1.0); VCROSS(view_dir, up_dir, right_dir); VTIMES(1.0/check0(VMAG(right_dir), "right_dir in project3d"), right_dir, right_dir); VCROSS(right_dir, view_dir, up_dir); VTIMES(1.0/check0(VMAG(up_dir), "up_dir in project3d"), up_dir, up_dir); /* Now that we have the basis system for the second camera worked out, use it to determine the projections of the 3D points (in the second camera). */ for (i = 0; i < 4; i++) project_point(vf2, right_dir, up_dir, view_dir, points_3d[i],(*p2_ref)[i]); for (i = 0; i < numpoints; i++) project_point(vf2, right_dir, up_dir, view_dir, points_3d[i + 4], (*p2_corr)[i]); /* ********* NB *********** Must apply the roll coefficient here, don't forget */ } void project_point(vector origin, vector X_vec, vector Y_vec, vector Z_vec, vector thePoint, vector projection) { vector relPoint; VSUB(thePoint, origin, relPoint); projection[X] = VDOT(relPoint, X_vec); projection[Y] = VDOT(relPoint, Y_vec); projection[Z] = VDOT(relPoint, Z_vec); projection[X] /= check0(projection[Z], "projection[Z]"); projection[Y] /= check0(projection[Z], "projection[Z]"); projection[Z] = 1.0; } /*========== NOISE FUNCTIONS ========*/ /* add_noise() seeds the random number generator from the system timer and adds random noise to all of the input correspondences. This noise is measured in percent of the distance p0 <-> p3 ... e.g. 1.0 means the maximum variation in the X and Y axes is 1% of the p0-p3 distance */ void add_noise(double noisepercent, vector *p1_ref, vector *p1_corr, vector *p2_ref, vector *p2_corr) { double noisedist; struct timespec mytime; int i; /* compute noise distance that's necessary for noisepercent% */ noisedist = sqrt(SQR(p1_ref[3][X] - p1_ref[0][X]) + SQR(p1_ref[3][Y] - p1_ref[0][Y])); #if DEBUGINFO fprintf(stderr, "Initial noisedist %16.11f\n", noisedist); #endif noisedist *= noisepercent/100.0; #if DEBUGINFO fprintf(stderr, "Noise +/- term is %16.11f\n", noisedist); #endif /* initialise random number generator */ getclock(TIMEOFDAY, &mytime); srand48(mytime.tv_nsec); #if DEBUGINFO fprintf(stderr, "Random number generator seeded : first %13.11f\n", drand48()); #endif for (i = 0; i < 4; i++) { apply_noise(noisedist, p1_ref[i]); apply_noise(noisedist, p2_ref[i]); } for (i = 0; i < numpoints; i++) { apply_noise(noisedist, p1_corr[i]); apply_noise(noisedist, p2_corr[i]); } } /* apply_noise() applies noise of magnitude noisedist to the given correspondence vector. */ void apply_noise(double noisedist, vector toNoisify) { toNoisify[X] += noisedist*2*(drand48() - 0.5); toNoisify[Y] += noisedist*2*(drand48() - 0.5); } /*======== ROTATION FUNCTIONS =========*/ /* getRotationMatrix places a matrix in R that will rotate about the given axis by an amount specified by sin(theta) and cos(theta) [supplied to avoid unnecessary calculation] */ void getRotationMatrix(matrix R, int theAxis, double sintheta, double costheta) { /* This returns a matrix that should rotate about the given axis by the given amount (specified by sin(theta) and cos(theta)). */ int axis2, axis3; switch (theAxis) { case X: { axis2 = Y; axis3 = Z; break; } case Y: { axis2 = X; axis3 = Z; break; } case Z: { axis2 = X; axis3 = Y; break; } default: { fprintf(stderr, "Can't rotate about axis %d!\n", theAxis); exit(-1); } } R[axis2] [axis2] = costheta; R[axis3] [axis2] = -sintheta; R[theAxis][axis2] = 0.0; R[3] [axis2] = 0.0; R[axis2] [axis3] = sintheta; R[axis3] [axis3] = costheta; R[theAxis][axis3] = 0.0; R[3] [axis3] = 0.0; R[axis2] [theAxis] = 0.0; R[axis3] [theAxis] = 0.0; R[theAxis][theAxis] = 1.0; R[3] [theAxis] = 0.0; R[axis2] [3] = 0.0; R[axis3] [3] = 0.0; R[theAxis][3] = 0.0; R[3] [3] = 1.0; } /*======== ROTATION-MAP FUNCTIONS ========*/ /* Uses the global variables defined for rotationmap() to return the error for a certain picture. */ double getRotationError(double yaw, double pitch) { int do_axes, approx_method; /* Set current view back to original 3D view */ VSET(new_vt2, orig_vt2); numpoints = orignumpoints; free(p1_ref); free(p2_ref); free(p1_corr); free(p2_corr); rotate_project3d(yaw, pitch, 0.0, vf1, vf2, vt1, new_vt2, points_3d, &p1_ref, &p1_corr, &p2_ref, &p2_corr); free(pAffine); do_axes = methodnum % 2; approx_method = methodnum / 2; pAffine = AffineCalc(p1_ref, p2_ref, p1_corr, p2_corr, approx_method, do_axes); return average_error(referenceAffine, pAffine); } /*========= PROJECTION FUNCTIONS =========*/ /* Takes a correspondence file and maps each point in it to the nearest place in a grid placed on the view-plane. */ void map_pixels(double pixel_spacing, vector *p1_ref, vector *p1_correspond, vector *p2_ref, vector *p2_correspond) { int i; for (i = 0; i < 4; i++) { map_to_pixel(pixel_spacing, p1_ref[i]); map_to_pixel(pixel_spacing, p2_ref[i]); } for (i = 0; i < numpoints; i++) { if (i == 0) { vec_print("before 0:", p1_correspond[i]); map_to_pixel(pixel_spacing, p1_correspond[i]); vec_print("after 0:", p1_correspond[i]); } else { map_to_pixel(pixel_spacing, p1_correspond[i]); } map_to_pixel(pixel_spacing, p2_correspond[i]); } } /* Maps a point to the nearest place in a grid on the view-plane, spacing pixel_spacing. */ void map_to_pixel(double pixel_spacing, vector aPoint) { int pixel_x, pixel_y; pixel_x = (int) (fabs(aPoint[X] / pixel_spacing) + 0.5); if (aPoint[X] < 0) pixel_x = -pixel_x; pixel_y = (int) (fabs(aPoint[Y] / pixel_spacing) + 0.5); if (aPoint[Y] < 0) pixel_y = -pixel_y; aPoint[X] = pixel_x * pixel_spacing; aPoint[Y] = pixel_y * pixel_spacing; } /************ SUPPORT FUNCTIONS ***********/ /*======= MEMORY ALLOCATION =========*/ /* This copies numvec vectors from array 1 to array 2. */ void veccpy(vector *array1, vector *array2, int numvec) { int i; for (i = 0; i < numvec; i++) VSET(array2[i], array1[i]); } /* alloc_vector() returns a pointer to a block of memory big enough for n vectors, using calloc. */ vector *alloc_vector(int numVectors) { vector *tempalloc; if (numVectors > MAX_NUMPOINTS) fatalError("Error: exceeded MAX_NUMPOINTS!"); if ((tempalloc = (vector *)calloc(numVectors, sizeof(vector))) == NULL) { sprintf(buf0, "alloc_vector: can't allocate %d vectors", numVectors); fatalError(buf0); } return tempalloc; } /* alloc_pgm() returns a pointer to a list of row pointers to a pgm image of the given size */ gray **alloc_pgm(int width, int height) { gray *image, **rows; int i; /* Allocate space for image and row pointer arrays */ if ((image = (gray *)calloc(width*height, sizeof(gray))) == NULL) fatalError("alloc_pgm: cannot allocate image data"); if ((rows = (gray **)calloc(height, sizeof(gray *))) == NULL) fatalError("alloc_pgm: cannot allocate row pointers"); /* Set up image row pointers */ for (i = 0; i < height; i++) rows[i] = &image[i * width] ; return rows; } /*======== FILE INPUT/OUTPUT ========*/ /* Read in the given .3D file */ void read_3d_file(char *filename, vector vf1, vector vf2, vector vt1, vector vt2, double *rollc, vector **points_3d) { FILE *infile; int i; if ((infile = fopen(filename, "r")) == NULL) { sprintf(buf0, "read_3d_file: can't open %s", filename); fatalError(buf0); } get_3d_point(infile, vf1); get_3d_point(infile, vt1); get_3d_point(infile, vf2); get_3d_point(infile, vt2); get_nextline(infile, buf0); if (sscanf(buf0, "%lf", rollc) < 1) { sprintf(buf0, "read_3d_file: corrupt rollcoeff in %s", filename); fatalError(buf0); } get_nextline(infile, buf0); if (sscanf(buf0, "%d", &numpoints) < 1) { sprintf(buf0, "read_3d_file: corrupt numpoints in %s", filename); fatalError(buf0); } if (numpoints > MAX_NUMPOINTS) fatalError("Error: exceeded MAX_NUMPOINTS!"); *points_3d = alloc_vector(numpoints); for (i = 0; i < numpoints; i++) get_3d_point(infile, (*points_3d)[i]); fclose(infile); } /* Write out a .3D file */ void write_3d_file(char *filename, vector vf1, vector vf2, vector vt1, vector vt2, double rollc, vector *points_3d) { FILE *outfile; int i; if ((outfile = fopen(filename, "w")) == NULL) { sprintf(buf0, "write_3d_file: can't open file %s", filename); fatalError(buf0); } fprintf(outfile, "; This is a 3D model for generating sample test data that\n"); fprintf(outfile, "; has pure 2D co-ordinates.\n"); fprintf(outfile, "\n"); fprintf(outfile, "; View From 1 :\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", vf1[X], vf1[Y], vf1[Z]); fprintf(outfile, "\n"); fprintf(outfile, "; View To 1 :\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", vt1[X], vt1[Y], vt1[Z]); fprintf(outfile, "\n"); fprintf(outfile, "; View From 2 :\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", vf2[X], vf2[Y], vf2[Z]); fprintf(outfile, "\n"); fprintf(outfile, "; View To 2 :\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", vt2[X], vt2[Y], vt2[Z]); fprintf(outfile, "\n"); fprintf(outfile, "; Roll Clockwise (degrees)\n"); fprintf(outfile, "%16.11f\n", rollc); fprintf(outfile, "\n"); fprintf(outfile, "; Number of Points to Project\n"); fprintf(outfile, "%d\n", numpoints); fprintf(outfile, "\n"); fprintf(outfile, "; P0\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", points_3d[0][X], points_3d[0][Y], points_3d[0][Z]); fprintf(outfile, "\n"); fprintf(outfile, "; P1\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", points_3d[1][X], points_3d[1][Y], points_3d[1][Z]); fprintf(outfile, "\n"); fprintf(outfile, "; P2\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", points_3d[2][X], points_3d[2][Y], points_3d[2][Z]); fprintf(outfile, "\n"); fprintf(outfile, "; P3\n"); fprintf(outfile, "%16.11f %16.11f %16.11f\n", points_3d[3][X], points_3d[3][Y], points_3d[3][Z]); fprintf(outfile, "\n"); fprintf(outfile, "; Data points\n"); for (i = 4; i < numpoints; i++) fprintf(outfile, "%16.11f %16.11f %16.11f\n", points_3d[i][X], points_3d[i][Y], points_3d[i][Z]); fclose(outfile); } /* Read in the given .2D file */ void read_correspondence_file(char *filename, vector **p1ref, vector **p2ref, vector **p1, vector **p2) { FILE *infile; int i; if ((infile = fopen(filename, "r")) == NULL) { sprintf(buf0, "read_correspondence_file: can't open %s", filename); fatalError(buf0); } *p1ref = alloc_vector(4); *p2ref = alloc_vector(4); get_2d_point(infile, (*p1ref)[0]); get_2d_point(infile, (*p2ref)[0]); get_2d_point(infile, (*p1ref)[1]); get_2d_point(infile, (*p2ref)[1]); get_2d_point(infile, (*p1ref)[2]); get_2d_point(infile, (*p2ref)[2]); get_2d_point(infile, (*p1ref)[3]); get_2d_point(infile, (*p2ref)[3]); get_nextline(infile, buf0); if (sscanf(buf0, "%d", &numpoints) < 1) { sprintf(buf0, "read_correspondence_file: corrupt number of correspondences in %s", filename); fatalError(buf0); } if (numpoints > MAX_NUMPOINTS) fatalError("Error: exceeded MAX_NUMPOINTS!"); *p1 = alloc_vector(numpoints); *p2 = alloc_vector(numpoints); for (i = 0; i < numpoints; i++) { get_2d_point(infile, (*p1)[i]); get_2d_point(infile, (*p2)[i]); } fclose(infile); } /* Write out a .2D file */ void write_correspondence_file(char *filename, vector *p1_ref, vector *p1_cond, vector *p2_ref, vector *p2_cond) { FILE *outfile; int i; if ((outfile = fopen(filename, "w")) == NULL) { sprintf(buf0, "write_correspondence_file: can't open file %s", filename); fatalError(buf0); } fprintf(outfile, ";These are simulated 2D co-ordinates for a set\n"); fprintf(outfile, ";of 3D points.\n"); fprintf(outfile, "\n"); fprintf(outfile, "; P0 : the basis of the axes\n"); fprintf(outfile, "%16.11f %16.11f\n", p1_ref[0][X], p1_ref[0][Y]); fprintf(outfile, "%16.11f %16.11f\n", p2_ref[0][X], p2_ref[0][Y]); fprintf(outfile, "\n"); fprintf(outfile, "; P1 : the X affine axis\n"); fprintf(outfile, "%16.11f %16.11f\n", p1_ref[1][X], p1_ref[1][Y]); fprintf(outfile, "%16.11f %16.11f\n", p2_ref[1][X], p2_ref[1][Y]); fprintf(outfile, "\n"); fprintf(outfile, "; P2 : the Y affine axis\n"); fprintf(outfile, "%16.11f %16.11f\n", p1_ref[2][X], p1_ref[2][Y]); fprintf(outfile, "%16.11f %16.11f\n", p2_ref[2][X], p2_ref[2][Y]); fprintf(outfile, "\n"); fprintf(outfile, "; P3 : the Z affine axis\n"); fprintf(outfile, "%16.11f %16.11f\n", p1_ref[3][X], p1_ref[3][Y]); fprintf(outfile, "%16.11f %16.11f\n", p2_ref[3][X], p2_ref[3][Y]); fprintf(outfile, "\n"); fprintf(outfile, "; The number of correspondences found\n"); fprintf(outfile, "%d\n", numpoints); fprintf(outfile, "\n"); fprintf(outfile, "; The correspondences, each in the form\n"); fprintf(outfile, "; \n"); fprintf(outfile, "; \n"); fprintf(outfile, "\n"); for (i = 0; i < numpoints; i++) { fprintf(outfile, "%16.11f %16.11f\n", p1_cond[i][X], p1_cond[i][Y]); fprintf(outfile, "%16.11f %16.11f\n", p2_cond[i][X], p2_cond[i][Y]); fprintf(outfile, "\n"); } fclose(outfile); } /* Read in an affine co-ordinate (.affine) file */ vector *read_affine_file(char *filename) { FILE *infile; vector *thepoints; int i, newnumpoints; if ((infile = fopen(filename, "r")) == NULL) { sprintf(buf0, "read_affine_file: can't open %s", filename); fatalError(buf0); } get_nextline(infile, buf0); if (sscanf(buf0, "%d", &newnumpoints) != 1) fatalError("read_affine_file: corrupt numpoints"); if (newnumpoints != numpoints && numpoints != 0) fatalError("read_affine_file: differing number of points"); if (newnumpoints > MAX_NUMPOINTS) fatalError("Error: exceeded MAX_NUMPOINTS!"); numpoints = newnumpoints; thepoints = alloc_vector(numpoints); for (i = 0; i < numpoints; i++) get_3d_point(infile, thepoints[i]); fclose(infile); return thepoints; } /* Read in a connectivity (.con) file */ int *read_connectivity_file(char *filename, int *num_tri) { FILE *infile; int *connects; int i; if ((infile = fopen(filename, "r")) == NULL) { sprintf(buf0, "read_connectivity_file: can't open %s", filename); fatalError(buf0); } get_nextline(infile, buf0); if (sscanf(buf0, "%d", num_tri) != 1) fatalError("read_connectivity_file: corrupt num_tri"); if ((connects = (int *)calloc(3 * (*num_tri + 1), sizeof(int))) == NULL) { sprintf(buf0, "read_connectivity_file: can't allocate %d triangles", *num_tri); fatalError(buf0); } for (i = 0; i < *num_tri; i++) { get_nextline(infile, buf0); if (sscanf(buf0, "%d %d %d", &connects[3*i + 0], &connects[3*i + 1], &connects[3*i + 2]) < 3) fatalError("read_connectivity_file: can't read next triangle"); } fclose(infile); return connects; } /* Read in a connectivity (.con) file */ void write_triangle_file(char *filename, vector *pAffine, int *connects, int num_tri) { FILE *outfile; int i; if ((outfile = fopen(filename, "w")) == NULL) { sprintf(buf0, "write_triangle_file: can't write to %s", filename); fatalError(buf0); } fprintf(outfile, "%d\n", num_tri); for (i = 0; i < num_tri; i++) { output_vector(outfile, pAffine[connects[3*i + 0]]); output_vector(outfile, pAffine[connects[3*i + 1]]); output_vector(outfile, pAffine[connects[3*i + 2]]); } fclose(outfile); } /* Write out vector to a file */ void output_vector(FILE *outfile, vector theVector) { fprintf(outfile, "%16.11f %16.11f %16.11f\n", theVector[X], theVector[Y], theVector[Z]); } /* Write out a .affine file */ void write_affine_file(char *filename, vector *pointArray) { FILE *outfile; int i; if ((outfile = fopen(filename, "w")) == NULL) { sprintf(buf0, "write_affine_file: can't open file %s", filename); fatalError(buf0); } fprintf(outfile, ";****Affine co-ordinate output****\n"); fprintf(outfile, "\n; Number of points is :\n"); fprintf(outfile, "%d\n", numpoints); fprintf(outfile, "\n; Point data follows ...\n"); for (i = 0; i < numpoints; i++) fprintf(outfile, "%16.11f %16.11f %16.11f\n", pointArray[i][X], pointArray[i][Y], pointArray[i][Z]); fclose(outfile); } /* Read in an XY point from the given file */ void get_2d_point(FILE *infile, vector xypoint) { get_nextline(infile, buf0); if (sscanf(buf0, "%lf %lf", &xypoint[X], &xypoint[Y]) < 2) fatalError("get_2d_point: can't read next XYpoint"); xypoint[Z] = 1.0; xypoint[W] = 1.0; } /* Read in an XYZ point from the given file */ void get_3d_point(FILE *infile, vector xyzpoint) { get_nextline(infile, buf0); if (sscanf(buf0, "%lf %lf %lf", &xyzpoint[X], &xyzpoint[Y], &xyzpoint[Z]) < 2) fatalError("get_3d_point: can't read next XYZpoint"); xyzpoint[W] = 1.0; } /* Get the next non-comment, non-blank line from the file */ void get_nextline(FILE *infile, char *buffer) { do { if ((buffer = fgets(buffer, 95, infile)) == NULL) fatalError("get_nextline: unexpected file termination"); } while (buffer[0] == ';' || strlen(buffer) < 2); } /* Write to the given filename an image in raw PGM format */ void write_pgm_file(char *filename, gray **image, int width, int height) { FILE *outfile; if ((outfile = fopen(filename, "w")) == NULL) { sprintf(buf0, "write_pgm_file: can't open file %s", filename); fatalError(buf0); } pgm_writepgm(outfile, image, height, width, 255, 1); fclose(outfile); } /* pgm_writepgm() NB: This is Kevin's PGM reader. You can tell because I would never call my code cheap. Mine is ... inexpensive. This is a cheap implementation of the pgm library's writepgm call. For documentation on parameters, see libpgm(3). Note: Setting the "max" value to anything other than 255 can cause weird behaviour in programs that use the output image. */ void pgm_writepgm( FILE *fp, gray **image, int height, int width, int max, int rawbits) { /* Write the header */ if ( fprintf( fp, "P5\n") < 0 ) { fprintf(stderr,"Write Magic Number error.\n"); exit( -1 ); } if ( fprintf( fp, "%d %d\n", width, height ) < 0 ) { fprintf(stderr,"Write image size error.\n"); exit( -1 ); } if ( fprintf( fp, "%d\n", max ) < 0 ) { fprintf(stderr,"Write max gray level error.\n"); exit( -1 ); } /* Write the data */ if (fwrite( image[0], sizeof(gray) * width, height, fp ) != height ) { fprintf(stderr,"Write data error.\n"); exit(-1); } } /*========= DEBUGGING OUTPUT =======*/ /* Print out the error message and exit the program */ void fatalError(char *errormessage) { fprintf(stderr, "\n\nFatal error encountered :\n%s\n", errormessage); fprintf(stderr, "Note: this is generally bad.\n"); exit(-1); } /* Print the text as debugging information */ void text_print(char *message) { fprintf(stderr, "%s\n", message); } /* Print the value as debugging information */ void scalar_print(char *message, double d) { fprintf(stderr, "%10s %16.11f\n", message, d); } /* Print the vector as debugging information */ void vec_print(char *message, vector v) { fprintf(stderr, "%10s (%16.11f %16.11f %16.11f %16.11f)\n", message, v[X], v[Y], v[Z], v[W]); } /* Print the vector as debugging information */ void vec3_print(char *message, vector v) { fprintf(stderr, "%10s (%16.11f %16.11f %16.11f)\n", message, v[X], v[Y], v[Z]); } /* Print the matrix as debugging information */ void matrix_print(char *message, matrix m) { fprintf(stderr, "%10s (%16.11f %16.11f %16.11f %16.11f)\n", message, m[0][0], m[1][0], m[2][0], m[3][0]); fprintf(stderr, " (%16.11f %16.11f %16.11f %16.11f)\n", m[0][1], m[1][1], m[2][1], m[3][1]); fprintf(stderr, " (%16.11f %16.11f %16.11f %16.11f)\n", m[0][2], m[1][2], m[2][2], m[3][2]); fprintf(stderr, " (%16.11f %16.11f %16.11f %16.11f)\n", m[0][3], m[1][3], m[2][3], m[3][3]); } /*========= MATH FUNCTIONS =========*/ /* Gets the angle between two vectors, relative to rotation about an axis */ double vec_angle(vector v1, vector v2, int axis) { int other1, other2; double result, magaccum; other1 = (axis + 1) % 3; other2 = (axis + 2) % 3; printf("axes %d, %d\n", other1, other2); result = v1[other1]*v2[other1] + v1[other2]*v2[other2]; magaccum = sqrt(v1[other1]*v1[other1] + v1[other2]*v1[other2]) * sqrt(v2[other1]*v2[other1] + v2[other2]*v2[other2]); result /= check0(magaccum, "vec_angle: along axis!"); printf("val %f\n", result); result = acos(result)*180.0/M_PI; printf("ang %f\n", result); return result; } /* These mathematics functions are all reasonably straightforward. */ double matrixDet_2d(matrix m) { double sum; sum = m[0][0]*(m[1][1]*m[2][2] - m[1][2]*m[2][1]); sum -= m[1][0]*(m[0][1]*m[2][2] - m[0][2]*m[2][1]); sum += m[2][0]*(m[0][1]*m[1][2] - m[0][2]*m[1][1]); return sum; } void matrixMult_2d(matrix a, matrix b, matrix result) { int i, j, k; double sum; matrix temp; for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) { sum = 0.0; for (k = 0; k < 3; k++) sum += a[i][k] * b[k][j]; temp[i][j] = sum; } matrixCopy_2d(temp, result); } void matrixMult_3d(matrix a, matrix b, matrix result) { int i, j, k; double sum; matrix temp; for (i = 0; i < 4; i++) for (j = 0; j < 4; j++) { sum = 0.0; for (k = 0; k < 4; k++) sum += a[k][j] * b[i][k]; temp[i][j] = sum; } matrixCopy_3d(temp, result); } void matrixCopy_2d(matrix src, matrix dest) { double *sptr = (double *)src; double *dptr = (double *)dest; int i; for (i = 0; i < 9; i++) dptr[i] = sptr[i]; } void matrixCopy_3d(matrix src, matrix dest) { double *sptr = (double *)src; double *dptr = (double *)dest; int i; for (i = 0; i < 16; i++) dptr[i] = sptr[i]; } void preApply_2d(vector v, matrix m, vector result) { int i, j; vector temp; for (i = 0; i < 3; i++) { temp[i] = 0.0; for (j = 0; j < 3; j++) temp[i] += v[j]*m[i][j]; } for (i = 0; i < 3; i++) result[i] = temp[i]; } void postApply_2d(matrix m, vector v, vector result) { int i, j; vector temp; for (j = 0; j < 3; j++) { temp[j] = 0.0; for (i = 0; i < 3; i++) temp[j] += v[i]*m[i][j]; } for (i = 0; i < 3; i++) result[i] = temp[i]; } void apply_3d(matrix m, vector v, vector result) { int i; vector temp; temp[0] = v[0]*m[0][0] + v[1]*m[1][0] + v[2]*m[2][0] + v[3]*m[3][0]; temp[1] = v[0]*m[0][1] + v[1]*m[1][1] + v[2]*m[2][1] + v[3]*m[3][1]; temp[2] = v[0]*m[0][2] + v[1]*m[1][2] + v[2]*m[2][2] + v[3]*m[3][2]; temp[3] = v[0]*m[0][3] + v[1]*m[1][3] + v[2]*m[2][3] + v[3]*m[3][3]; for (i = 0; i < 4; i++) result[i] = temp[i]; } double check0(double checkthis, char *message) { if (checkthis == 0) { #if DEBUGINFO fprintf(stderr, "Zero divisor replaced : %s\n", message); #else if (strcmp(message, "AFFINE_CALC: lambda") != 0) fprintf(stderr, "Zero divisor replaced : %s\n", message); #endif return 1/10000000000.0; } else return checkthis; }