// Copyright 2003 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 */ #ifndef __backprojector_h__ #define __backprojector_h__ #include "../../point.h" #include "rasterizer.h" #include "raster.h" /* * Backprojector for rasterized PSFs. * * This class converts a rasterized PSF into a rasterized backprojection array. */ class backprojector : public raster { raster *input; public: unsigned int varieties() const { return input->varieties(); } unsigned int select(unsigned int i, unsigned int j) const { return input->select(i, j); } private: /* * Backprojection for the Irani-Peleg renderer * * Applying a special case of theorem 4.1 of the source paper by Irani * and Peleg, convergence can be assured for a single image, with * uniform PSF, no change in sampling rate, and taking the normalizing * divisor c == 1, if H[psf](f)H[aux](f) is real and within the open * interval (0, 2), where H[psf] is the frequency-domain representation * of the point-spread function and H[aux] is the frequency-domain * representation of the backprojection kernel. We can guarantee that * H[psf](f)H[aux](f) is real by making H[aux](f) == k(f)H[psf](f)*, * where k is a real function and '*' indicates the complex conjugate. * If k(f) is equal to 1 for all f, then this is equivalent to the * condition h[aux](x) == h[psf](-x), where h[] are the time domain * representations of the respective functions. Since this negation * of position is implicitly performed in ipc.h, we don't perform it * here. * * However, to ensure that the range (0, 2) is satisfied, it may be * necessary for k(f) to assume a value other than 1. We choose a * constant function k, in accordance with the source paper's * normalizing divisor c, but this is not required. We use FFTW * when available, but it is likely that common cases will not observe * any speed improvement. */ void initialize_response_array(ale_real *response_array) { int cols = _filter_dim_j; int rows = _filter_dim_i; #ifdef USE_FFTW fftw_complex *inout; fftw_plan p_forward; fftw_plan p_backward; inout = (fftw_complex *)fftw_malloc(sizeof(fftw_complex) * cols * rows); p_forward = fftw_plan_dft_2d(rows, cols, inout, inout, FFTW_FORWARD, FFTW_ESTIMATE); p_backward = fftw_plan_dft_2d(rows, cols, inout, inout, FFTW_BACKWARD, FFTW_ESTIMATE); for (int k = 0; k < 3; k++) { for (int i = 0; i < rows * cols; i++) { /* * Write the values to the FFTW input array, * shifting by (rows * cols - 1) / 2 in order * to accommodate the implicit translation. */ inout[i][0] = response_array[((i + (rows * cols - 1)/2) * 3 + k) % (rows * cols * 3)]; inout[i][1] = 0; } fftw_execute(p_forward); /* * Find the frequency with maximum magnitude, then * adjust this according to the sampling rate * (filter resolution). */ ale_real max_magnitude = 0; for (int i = 0; i < rows * cols; i++) { ale_real input_magnitude; input_magnitude = sqrt(pow(inout[i][0], 2) + pow(inout[i][1], 2)); if (input_magnitude > max_magnitude) max_magnitude = input_magnitude; } max_magnitude *= (4 * _height * _width) / (rows * cols); /* * Scale the magnitude of all of the frequencies and perform * conjugation. */ for (int i = 0; i < rows * cols; i++) { /* * Adjust the magnitude * * Note: since we're currently dividing all frequencies * by the same value, there's no need to divide in the * frequency domain. However, we might want to do * something else in the future, so it might be * good to leave the code like this for now. */ inout[i][0] = inout[i][0] * pow(0.9 / max_magnitude, 2); inout[i][1] = inout[i][1] * pow(0.9 / max_magnitude, 2); /* * Perform conjugation * * Note: conjugation is implicit in ipc.h, so we omit the * step here. */ /* inout[i][1] = -inout[i][1]; */ } fftw_execute(p_backward); for (int i = 0; i < rows * cols; i++) { /* * Read the values from the FFTW output array, * shifting by (rows * cols - 1) / 2 in order * to accommodate the implicit translation. */ response_array[((i + (rows * cols - 1)/2) * 3 + k) % (rows * cols * 3)] = inout[i][0] / (rows * cols); } } fftw_destroy_plan(p_forward); fftw_destroy_plan(p_backward); fftw_free(inout); #else for (int k = 0; k < 3; k++) { ale_real *real1 = (ale_real *) calloc(rows * cols, sizeof(ale_real)); ale_real *imag1 = (ale_real *) calloc(rows * cols, sizeof(ale_real)); ale_real *real2 = (ale_real *) calloc(rows * cols, sizeof(ale_real)); ale_real *imag2 = (ale_real *) calloc(rows * cols, sizeof(ale_real)); assert (real1 && imag1 && real2 && imag2); if (!(real1 && imag1 && real2 && imag2)) { fprintf(stderr, "Unable to allocate memory in backprojector.\n"); exit(1); } /* * Calculate frequencies. We implement the equations indicated by * the FFTW3 info page (section "What FFTW Really Computes"). */ for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) for (int jj = 0; jj < cols; jj++) { real1[i * cols + j] += response_array[((i * cols + jj + (rows * cols - 1)/2) * 3 + k) % (rows * cols * 3)] * (ale_real) cos((-2 * M_PI * j * jj) / cols); imag1[i * cols + j] += response_array[((i * cols + jj + (rows * cols - 1)/2) * 3 + k) % (rows * cols * 3)] * (ale_real) sin((-2 * M_PI * j * jj) / cols); } for (int i = 0; i < rows; i++) for (int j = 0; j < cols; j++) for (int ii = 0; ii < rows; ii++) { real2[i * cols + j] += real1[ii * cols + j] * (ale_real) cos((-2 * M_PI * i * ii) / rows) - imag1[ii * cols + j] * (ale_real) sin((-2 * M_PI * i * ii) / rows); imag2[i * cols + j] += real1[ii * cols + j] * (ale_real) sin((-2 * M_PI * i * ii) / rows) + imag1[ii * cols + j] * (ale_real) cos((-2 * M_PI * i * ii) / rows); } /* * Find the frequency with maximum magnitude, then * adjust this according to the sampling rate * (filter resolution). */ ale_real max_magnitude = 0; for (int i = 0; i < rows * cols; i++) { ale_real input_magnitude; input_magnitude = sqrt(pow(real2[i], 2) + pow(imag2[i], 2)); if (input_magnitude > max_magnitude) max_magnitude = input_magnitude; } max_magnitude *= (4 * _height * _width) / (rows * cols); for (int i = 0; i < rows * cols; i++) response_array[i * 3 + k] *= pow(0.9 / max_magnitude, 2); free(real1); free(imag1); free(real2); free(imag2); } #endif } public: backprojector (raster *input) { this->input = input; _height = -input->min_i(); assert (input->max_i() == _height); _width = -input->min_j(); assert (input->max_j() == _width); /* * The element structure matches that of the input. */ _filter_dim_i = input->max_elem_i(); _filter_dim_j = input->max_elem_j(); /* * Ensure that the array has an odd number of elements in each * direction. This allows us to move the center to the right * place when using a discrete FT. */ assert (_filter_dim_i % 2 == 1); assert (_filter_dim_j % 2 == 1); /* * Determine the number of arrays to create. */ num_arrays = input->varieties(); /* * Create arrays */ response_arrays = (ale_real **)malloc(num_arrays * sizeof(ale_real *)); if (!response_arrays) { fprintf(stderr, "Could not allocate in backprojector.\n"); exit(1); } for (unsigned int n = 0; n < num_arrays; n++) { response_arrays[n] = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3 * sizeof(ale_real)); if (!response_arrays[n]) { fprintf(stderr, "Could not allocate in backprojector.\n"); exit(1); } for (unsigned int i = 0; i < _filter_dim_i; i++) for (unsigned int j = 0; j < _filter_dim_j; j++) for (unsigned int k = 0; k < 3; k++) { response_arrays[n][i * _filter_dim_j * 3 + j * 3 + k] = input->element(n, i, j, k); } initialize_response_array(response_arrays[n]); } #if 0 avg_response = (ale_real *)malloc(_filter_dim_i * _filter_dim_j * 3 * sizeof(ale_real)); if (!avg_response) { fprintf(stderr, "Could not allocate in backprojector.\n"); exit(1); } for (unsigned int i = 0; i < _filter_dim_i; i++) for (unsigned int j = 0; j < _filter_dim_j; j++) for (unsigned int k = 0; k < 3; k++) { avg_response[i * _filter_dim_j * 3 + j * 3 + k] = input->element(i, j, k); } initialize_response_array(avg_response); #endif compute_integrals(); } }; #endif