// Copyright 2002, 2003, 2007 David Hilvert , // /* This file is part of the Anti-Lamenessing Engine. The Anti-Lamenessing Engine is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. The Anti-Lamenessing Engine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with the Anti-Lamenessing Engine; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * tfile.h: Read and write transformation data files. */ /* * This version of ALE reads transformation data file versions 0, 1, 2, and 3, * and writes version 2 and 3 transformation data files. Data file versions 1 * and higher are identified by a version command "V x", where x is the version * number, prior to any transformation command. Data file version 0 is * identified by having no version command. */ #ifndef __tfile_h__ #define __tfile_h__ #include "transformation.h" #define TFILE_VERSION 3 #define TFILE_VERSION_MAX 3 extern int tfile_input_version; extern int tfile_output_version; /* * Structure to describe a transformation data file to load data from. */ struct tload_t { const char *filename; FILE *file; }; /* * Structure to describe a transformation data file to write data to. */ struct tsave_t { const char *filename; const char *target; const char *orig; pixel orig_apm; FILE *file; }; /* * Create a new tload_t transformation data file structure, used for * reading data from transformation data files. */ static inline struct tload_t *tload_new(const char *filename) { FILE *file = fopen (filename, "r"); struct tload_t *result = NULL; if (!file) { fprintf(stderr, "tload: Error: could not open transformation data file '%s'.", filename); exit(1); } result = (struct tload_t *) malloc(sizeof(struct tload_t)); result->filename = filename; result->file = file; return result; } /* * Load the first transformation from a transformation data file associated with * transformation data file structure T, or return the default transformation * if no transformation is available. * * T is a pointer to the tload_t transformation data file structure. * * IS_P is nonzero if a projective transformation is expected. * * DEFAULT_TRANSFORM is the default transformation result. * * IS_DEFAULT is used to signal a non-default transformation result. */ static inline transformation tload_first(struct tload_t *t, int is_p, transformation default_transform, int *is_default) { transformation result = default_transform; *is_default = 1; /* * If there is no file, return the default. */ if (t == NULL) return result; /* * Search through the initial part of the file to determine * its version. */ /* * Skip comments */ int first_character; first_character = fgetc(t->file); while (first_character == ' ' || first_character == 0xa || first_character == 0xd || first_character == '\t' || first_character == '#') { ungetc(first_character, t->file); char line[1024]; fgets(line, 1024, t->file); if (strlen(line) >= 1023) { fprintf(stderr, "\ntrans-load: Error: line too long in input file\n"); exit(1); } first_character = fgetc(t->file); } if (first_character != EOF) ungetc(first_character, t->file); /* * Check for version 0 */ if (first_character != 'V') /* * Must be version 0. */ return result; /* * Obtain version from version command string. */ char line[1024]; fgets(line, 1024, t->file); if (strlen(line) >= 1023) { fprintf(stderr, "\ntrans-load: Error: line too long in input file\n"); exit(1); } int count = sscanf(line, "V %d", &tfile_input_version); if (count < 1) { fprintf(stderr, "Error in transformation " "file version command.\n"); exit(1); } else if (tfile_input_version > TFILE_VERSION_MAX) { fprintf(stderr, "Unsupported transformation " "file version %d\n", tfile_input_version); exit(1); } /* * Handle versions lower than 3. */ if (tfile_input_version < 3) /* * Versions lower than 3 use the default transformation * for the original frame. */ return result; /* * Read each line of the file until we find a transformation * or EOF. */ while (!feof(t->file)) { char line[1024]; fgets(line, 1024, t->file); if (feof(t->file)) return result; if (strlen(line) >= 1023) { fprintf(stderr, "\ntrans-load: Error: line too long in input file\n"); exit(1); } switch (line[0]) { case ' ': case 0xa: case 0xd: case '\t': case '#': /* Comment or whitespace */ break; case 'D': case 'd': /* Default transformation */ return result; case 'B': case 'b': if (tfile_input_version < 3) { fprintf(stderr, "\ntrans-load: Error: " "Barrel distortion not supported " "for version %d input files.\n" "trans-load: Hint: Use version 3 " "file syntax.\n", tfile_input_version); exit(1); } else { unsigned int count; unsigned int pos = 0, chars; unsigned int bdc; double dparameters[BARREL_DEGREE]; ale_pos parameters[BARREL_DEGREE]; count = sscanf(line, "B %u%n", &bdc, &chars); pos += chars; if (count < 1) { fprintf(stderr, "\ntrans-load: Error: " "Malformed 'B' command.\n"); exit(1); } if (bdc > result.bd_max()) { fprintf(stderr, "\ntrans-load: Error: " "Barrel distortion degree %d " "is too large. (Maximum is %d.)\n" "trans-load: Hint: " "Reduce degree or re-compile " "with BD_DEGREE=%d\n", bdc, BARREL_DEGREE, bdc); exit(1); } for (unsigned int d = 0; d < bdc; d++) { count = sscanf(line + pos, "%lf%n", &dparameters[d], &chars); pos += chars; if (count < 1) { fprintf(stderr, "\ntrans-load: Error: " "Malformed 'B' command.\n"); exit(1); } parameters[d] = dparameters[d]; } result.bd_set(bdc, parameters); } break; case 'P': case 'p': /* Projective transformation data */ *is_default = 0; if (is_p == 0) { fprintf(stderr, "\ntrans-load: Error: " "Projective data for euclidean " "transformation.\n" "trans-load: Hint: " "Use command-line option --projective.\n"); exit(1); } else { double width, height, values[8]; int count, i; point x[4]; count = sscanf(line + 1, " %lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", &width, &height, &values[0], &values[1], &values[2], &values[3], &values[4], &values[5], &values[6], &values[7]); int index = 0; for (int i = 0; i < 4; i++) for (int j = 1; j >= 0; j--) x[i][j] = values[index++]; if (count < 10) fprintf(stderr, "\ntrans-load: warning:" "Missing args for 'P'\n"); for (i = 0; i < count - 2; i++) { ale_pos factor = (i % 2) ? ((double) result.scaled_width() / width) : ((double) result.scaled_height() / height); x[i / 2][i % 2] *= factor; } result.gpt_set(x); return result; } break; case 'E': case 'e': /* Euclidean transformation data */ *is_default = 0; { double width, height; double values[3] = {0, 0, 0}; int count, i; ale_pos eu[3]; count = sscanf(line + 1, " %lf%lf%lf%lf%lf", &width, &height, &values[0], &values[1], &values[2]); eu[1] = values[0]; eu[0] = values[1]; eu[2] = values[2]; if (count < 5) fprintf(stderr, "\ntrans-load: warning:" "Missing args for 'E'\n"); for (i = 0; (i < count - 2) && (i < 2); i++) { ale_pos factor = (i % 2) ? ((double) result.scaled_width() / width) : ((double) result.scaled_height() / height); eu[i] *= factor; } result.eu_set(eu); return result; } break; default: fprintf(stderr, "\ntrans-load: Error in tload_first: unrecognized command '%s'\n", line); exit(1); } } /* * EOF reached: return default transformation. */ return result; } /* * Load the next transformation from a transformation data file associated with * transformation data file structure T, or return the default transformation * if no transformation is available. * * T is a pointer to the tload_t transformation data file structure. * * IS_P is nonzero if a projective transformation is expected. * * DEFAULT_TRANSFORM is the default transformation result. * * IS_DEFAULT is used to signal a non-default transformation result. * * IS_PRIMARY is used to differentiate primary and non-primary * transformations */ static inline transformation tload_next(struct tload_t *t, int is_p, transformation default_transform, int *is_default, int is_primary) { transformation result = default_transform; *is_default = 1; /* * Read each line of the file until we find a transformation. */ while (t && !feof(t->file)) { char c = fgetc(t->file); if (!feof(t->file) && c != EOF) ungetc(c, t->file); if (feof(t->file) || (!is_primary && c != EOF && (c == 'E' || c == 'e' || c == 'P' || c == 'p' || c == 'D' || c == 'd' || c == 'B' || c == 'b'))) { return result; } char line[1024]; fgets(line, 1024, t->file); if (feof(t->file)) return result; if (strlen(line) >= 1023) { fprintf(stderr, "\ntrans-load: warning: line too long in input file\n"); } switch (line[0]) { case ' ': case 0xa: case 0xd: case '\t': case '#': /* Comment or whitespace */ break; case 'D': case 'd': /* Default transformation */ return result; case 'B': case 'b': if (tfile_input_version < 3) { fprintf(stderr, "\ntrans-load: Error: " "Barrel distortion not supported " "for version %d input files.\n" "trans-load: Hint: Use version 3 " "file syntax.\n", tfile_input_version); exit(1); } else { unsigned int count; unsigned int pos = 0, chars; unsigned int bdc; ale_pos parameters[BARREL_DEGREE]; double dparameters[BARREL_DEGREE]; count = sscanf(line, "B %u%n", &bdc, &chars); pos += chars; if (count < 1) { fprintf(stderr, "\ntrans-load: Error: " "Malformed 'B' command.\n"); exit(1); } if (bdc > result.bd_max()) { fprintf(stderr, "\ntrans-load: Error: " "Barrel distortion degree %d " "is too large. (Maximum is %d.)\n" "trans-load: Hint: " "Reduce degree or re-compile " "with BD_DEGREE=%d\n", bdc, BARREL_DEGREE, bdc); exit(1); } for (unsigned int d = 0; d < bdc; d++) { count = sscanf(line + pos, "%lf%n", &dparameters[d], &chars); pos += chars; if (count < 1) { fprintf(stderr, "\ntrans-load: Error: " "Malformed 'B' command.\n"); exit(1); } parameters[d] = dparameters[d]; } result.bd_set(bdc, parameters); } break; case 'Q': case 'q': if (is_primary) break; case 'P': case 'p': /* Projective transformation data */ if (is_p == 0) { fprintf(stderr, "\ntrans-load: Error: " "Projective data for euclidean " "transformation.\n" "trans-load: Hint: " "Use command-line option --projective.\n"); exit(1); } else { double width, height, values[8]; int count, i; point x[4]; transformation::multi_coordinate mc1, mc2; count = sscanf(line + 1, " %lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d%d%d", &width, &height, &values[0], &values[1], &values[2], &values[3], &values[4], &values[5], &values[6], &values[7], &mc1.degree, &mc1.x, &mc1.y); if (count == 13) { mc2 = default_transform.get_current_coordinate(); if (mc1.degree < mc2.degree || mc1.degree == mc2.degree && mc1.y < mc2.y || mc1.degree == mc2.degree && mc1.y == mc2.y && mc1.x < mc2.x) break; if (mc1.degree != mc2.degree || mc1.x != mc2.x || mc1.y != mc2.y) { if (!result.exists(mc1)) break; result.set_current_index(result.get_index(mc1)); } } int index = 0; for (int i = 0; i < 4; i++) for (int j = 1; j >= 0; j--) x[i][j] = values[index++]; if (count < 10) fprintf(stderr, "\ntrans-load: warning:" "Missing args for 'P'\n"); for (i = 0; i < count - 2; i++) { ale_pos factor = (i % 2) ? ((double) result.scaled_width() / width) : ((double) result.scaled_height() / height); x[i / 2][i % 2] *= factor; } if (tfile_input_version < 1) { /* * Accommodate older versions * of tfile. */ for (i = 0; i < 4; i++) { ale_pos y = x[i][0]; x[i][0] = x[i][1]; x[i][1] = y; } result.gpt_v0_set(x); } else { result.gpt_set(x); } *is_default = 0; return result; } break; case 'F': case 'f': if (is_primary) break; case 'E': case 'e': /* Euclidean transformation data */ { double width, height; double values[3] = {0, 0, 0}; int count, i; ale_pos eu[3]; transformation::multi_coordinate mc1, mc2; count = sscanf(line + 1, " %lf%lf%lf%lf%lf%d%d%d", &width, &height, &values[0], &values[1], &values[2], &mc1.degree, &mc1.x, &mc1.y); if (count == 8) { mc2 = default_transform.get_current_coordinate(); if (mc1.degree < mc2.degree || mc1.degree == mc2.degree && mc1.y < mc2.y || mc1.degree == mc2.degree && mc1.y == mc2.y && mc1.x < mc2.x) break; if (mc1.degree != mc2.degree || mc1.x != mc2.x || mc1.y != mc2.y) { if (!result.exists(mc1)) break; result.set_current_index(result.get_index(mc1)); } } eu[1] = values[0]; eu[0] = values[1]; eu[2] = values[2]; if (tfile_input_version < 2) { ale_pos t = eu[0]; eu[0] = eu[1]; eu[1] = t; } if (count < 5) fprintf(stderr, "\ntrans-load: warning:" "Missing args for 'E'\n"); for (i = 0; (i < count - 2) && (i < 2); i++) { ale_pos factor = (i % 2) ? ((double) result.scaled_width() / width) : ((double) result.scaled_height() / height); eu[i] *= factor; } if (tfile_input_version < 1) { result.eu_v0_set(eu); } else { result.eu_set(eu); } *is_default = 0; return result; } break; default: fprintf(stderr, "\ntrans-load: Error in tload_next: unrecognized command '%s'\n", line); exit(1); } } return result; } /* * Create a new tsave_t transformation data file structure, used for * writing data to transformation data files. */ static inline struct tsave_t *tsave_new(const char *filename) { FILE *file = fopen (filename, "w"); struct tsave_t *result = NULL; if (!file) { fprintf(stderr, "tsave: Error: could not open transformation data file '%s'.", filename); exit(1); } result = (struct tsave_t *) malloc(sizeof(struct tsave_t)); result->filename = filename; result->file = file; result->orig = "unknown"; result->target = "unknown"; fprintf(file, "# created by ALE transformation file handler version %d\n", TFILE_VERSION); fclose(file); return result; } /* * Save the first transformation to a transformation data file associated with * transformation data file structure T, or do nothing if T is NULL. This * function also establishes the output file version. * * OFFSET is the transformation to be saved. * * IS_PROJECTIVE indicates whether to write a projective transformation. * */ static inline void tsave_first(struct tsave_t *t, transformation offset, int is_projective) { if (t == NULL) return; t->file = fopen(t->filename, "a"); /* * Determine the output version to use. We use version 3 output syntax only when * necessary. This comprises two cases: * * (i) an input file is used, and this file uses version 3 syntax. * (ii) non-degenerate barrel distortion correction is selected. * * (i) can be directly examined. When (i) does not hold, (ii) can be * inferred from offset.bd_count(), since this value should be constant * when (i) does not hold. XXX: This logic should be reviewed. */ if (tfile_input_version == 3 || offset.bd_count() > 0) tfile_output_version = 3; else tfile_output_version = 2; fprintf(t->file, "# producing transformation file syntax version %d\n", tfile_output_version); fprintf(t->file, "V %d\n", tfile_output_version); fprintf(t->file, "# Comment: Target output file is %s\n", t->target); fprintf(t->file, "# Comment: Original frame is %s\n", t->orig); fprintf(t->file, "# Comment: Avg magnitude [r=%f g=%f b=%f]\n", (double) t->orig_apm[0], (double) t->orig_apm[1], (double) t->orig_apm[2]); if (tfile_output_version < 3) { fclose(t->file); return; } if (offset.bd_count() > 0) { assert (tfile_output_version >= 3); unsigned int i; fprintf(t->file, "B "); fprintf(t->file, "%u ", offset.bd_count()); for (i = 0; i < offset.bd_count(); i++) fprintf(t->file, "%f ", (double) offset.bd_get(i)); fprintf(t->file, "\n"); } if (is_projective) { int i, j; fprintf(t->file, "P "); fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height()); for (i = 0; i < 4; i++) for (j = 1; j >= 0; j--) fprintf(t->file, "%f ", (double) offset.gpt_get(i, j)); } else { fprintf(t->file, "E "); fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height()); fprintf(t->file, "%f ", (double) offset.eu_get(1)); fprintf(t->file, "%f ", (double) offset.eu_get(0)); fprintf(t->file, "%f ", (double) offset.eu_get(2)); } fprintf(t->file, "\n"); fclose(t->file); } /* * Save the next transformation to a transformation data file associated with * transformation data file structure T, or do nothing if T is NULL. * * OFFSET is the transformation to be saved. * * IS_PROJECTIVE indicates whether to write a projective transformation. * * IS_PRIMARY indicates whether to write a primary transformation * */ static inline void tsave_next(struct tsave_t *t, transformation offset, int is_projective, int is_primary) { if (t == NULL) return; t->file = fopen(t->filename, "a"); if (is_primary && offset.bd_count() > 0) { assert (tfile_output_version >= 3); unsigned int i; fprintf(t->file, "B "); fprintf(t->file, "%u ", offset.bd_count()); for (i = 0; i < offset.bd_count(); i++) fprintf(t->file, "%f ", (double) offset.bd_get(i)); fprintf(t->file, "\n"); } if (is_projective) { int i, j; fprintf(t->file, is_primary ? "P " : "Q "); fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height()); for (i = 0; i < 4; i++) for (j = 1; j >= 0; j--) fprintf(t->file, "%f ", (double) offset.gpt_get(i, j)); } else { fprintf(t->file, is_primary ? "E " : "F "); fprintf(t->file, "%f %f ", (double) offset.scaled_width(), (double) offset.scaled_height()); fprintf(t->file, "%f ", (double) offset.eu_get(1)); fprintf(t->file, "%f ", (double) offset.eu_get(0)); fprintf(t->file, "%f ", (double) offset.eu_get(2)); } if (!is_primary) { transformation::multi_coordinate mc = offset.get_current_coordinate(); fprintf(t->file, "%d %d %d ", mc.degree, mc.x, mc.y); } fprintf(t->file, "\n"); fclose(t->file); } /* * Write information to a transformation file indicating the target output * file. */ static inline void tsave_target(struct tsave_t *t, const char *filename) { if (t == NULL) return; t->target = filename; if (t != NULL) { t->file = fopen(t->filename, "a"); fclose(t->file); } } /* * Write information to a transformation data file indicating the filename * of the original frame (i.e. the first frame in the sequence of input * frames). */ static inline void tsave_orig(struct tsave_t *t, const char *filename, pixel apm) { if (t == NULL) return; t->orig = filename; t->orig_apm = apm; } /* * Write information to a transformation data file indicating the filename * of a supplemental frame (i.e. a frame in the sequence of input frames * that is not the first frame). */ static inline void tsave_info(struct tsave_t *t, const char *filename) { if (t != NULL) { t->file = fopen(t->filename, "a"); fprintf(t->file, "# Comment: Supplemental frame %s\n", filename); fclose(t->file); } } /* * Write information to a transformation data file indicating the tonal * registration multiplier. */ static inline void tsave_trm(struct tsave_t *t, ale_real r, ale_real g, ale_real b) { if (t != NULL) { t->file = fopen(t->filename, "a"); fprintf(t->file, "# Comment: Exposure [r=%f g=%f b=%f]\n", (double) r, (double) g, (double) b); fclose(t->file); } } /* * Write information to a transformation data file indicating the average * pixel magnitude. */ static inline void tsave_apm(struct tsave_t *t, ale_real r, ale_real g, ale_real b) { if (t != NULL) { t->file = fopen(t->filename, "a"); fprintf(t->file, "# Comment: Avg magnitude [r=%f g=%f b=%f]\n", (double) r, (double) g, (double) b); fclose(t->file); } } /* * Destroy a tload_t transformation data file structure. */ static inline void tload_delete(struct tload_t *victim) { if (victim) fclose(victim->file); free(victim); } /* * Destroy a tsave_t transformation data file structure. */ static inline void tsave_delete(struct tsave_t *victim) { free(victim); } #endif