233 lines
8.6 KiB
C++
233 lines
8.6 KiB
C++
///////////////////////////////////////////////////////////////////////////////////
|
|
// File : FFT.cpp
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// LumosQuad - A Lightning Generator
|
|
// Copyright 2007
|
|
// The University of North Carolina at Chapel Hill
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This program 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 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program 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 this program; if not, write to the Free Software
|
|
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
//
|
|
// The University of North Carolina at Chapel Hill makes no representations
|
|
// about the suitability of this software for any purpose. It is provided
|
|
// "as is" without express or implied warranty.
|
|
//
|
|
// Permission to use, copy, modify and distribute this software and its
|
|
// documentation for educational, research and non-profit purposes, without
|
|
// fee, and without a written agreement is hereby granted, provided that the
|
|
// above copyright notice and the following three paragraphs appear in all
|
|
// copies.
|
|
//
|
|
// THE UNIVERSITY OF NORTH CAROLINA SPECIFICALLY DISCLAIM ANY WARRANTIES,
|
|
// INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
|
// FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN
|
|
// "AS IS" BASIS, AND THE UNIVERSITY OF NORTH CAROLINA HAS NO OBLIGATION TO
|
|
// PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
|
|
//
|
|
// Please send questions and comments about LumosQuad to kim@cs.unc.edu.
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This program uses OpenEXR, which has the following restrictions:
|
|
//
|
|
// Copyright (c) 2002, Industrial Light & Magic, a division of Lucas
|
|
// Digital Ltd. LLC
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Industrial Light & Magic nor the names of
|
|
// its contributors may be used to endorse or promote products derived
|
|
// from this software without specific prior written permission.
|
|
//
|
|
|
|
#include "FFT.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Construction/Destruction
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
FFT::FFT()
|
|
{
|
|
|
|
}
|
|
|
|
FFT::~FFT()
|
|
{
|
|
|
|
}
|
|
|
|
bool FFT::convolve(float* source, float* kernel, int xSource, int ySource, int xKernel, int yKernel)
|
|
{
|
|
int x, y, index;
|
|
|
|
// get normalization params
|
|
float maxCurrent = 0.0f;
|
|
for (x = 0; x < xSource * ySource; x++)
|
|
maxCurrent = (maxCurrent < source[x]) ? source[x] : maxCurrent;
|
|
float maxKernel = 0.0f;
|
|
for (x = 0; x < xKernel * yKernel; x++)
|
|
maxKernel = (maxKernel < kernel[x]) ? kernel[x] : maxKernel;
|
|
float maxProduct = maxCurrent * maxKernel;
|
|
|
|
// retrieve dimensions
|
|
int xHalf = xKernel / 2;
|
|
int yHalf = yKernel / 2;
|
|
int xResPadded = xSource + xKernel;
|
|
int yResPadded = ySource + yKernel;
|
|
|
|
if (xResPadded != yResPadded)
|
|
(xResPadded > yResPadded) ? yResPadded = xResPadded : xResPadded = yResPadded;
|
|
|
|
// create padded field
|
|
fftw_complex* padded = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
|
if (!padded)
|
|
{
|
|
cout << " IMAGE: Not enough memory! Try a smaller final image size." << endl;
|
|
return false;
|
|
}
|
|
|
|
// init padded field
|
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
|
padded[index][0] = padded[index][1] = 0.0f;
|
|
index = 0;
|
|
for (y = 0; y < ySource; y++)
|
|
for (x = 0; x < xSource; x++, index++)
|
|
{
|
|
int paddedIndex = (x + xKernel / 2) + (y + yKernel / 2) * xResPadded;
|
|
padded[paddedIndex][0] = source[index];
|
|
}
|
|
|
|
// create padded filter
|
|
fftw_complex* filter = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
|
if (!filter)
|
|
{
|
|
cout << " FILTER: Not enough memory! Try a smaller final image size." << endl;
|
|
return false;
|
|
}
|
|
|
|
// init padded filter
|
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
|
filter[index][0] = filter[index][1] = 0.0f;
|
|
|
|
// quadrant IV
|
|
for (y = 0; y < (yHalf + 1); y++)
|
|
for (x = 0; x < (xHalf + 1); x++)
|
|
{
|
|
int filterIndex = x + xHalf + y * xKernel;
|
|
int fieldIndex = x + (y + yResPadded - (yHalf + 1)) * xResPadded;
|
|
filter[fieldIndex][0] = kernel[filterIndex];
|
|
}
|
|
|
|
// quadrant I
|
|
for (y = 0; y < yHalf; y++)
|
|
for (x = 0; x < (xHalf + 1); x++)
|
|
{
|
|
int filterIndex = (x + xHalf) + (y + yHalf + 1) * xKernel;
|
|
int fieldIndex = x + y * xResPadded;
|
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
|
}
|
|
|
|
// quadrant III
|
|
for (y = 0; y < (yHalf + 1); y++)
|
|
for (x = 0; x < xHalf; x++)
|
|
{
|
|
int filterIndex = x + y * xKernel;
|
|
int fieldIndex = (x + xResPadded - xHalf) + (y + yResPadded - (yHalf + 1)) * xResPadded;
|
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
|
}
|
|
|
|
// quadrant II
|
|
for (y = 0; y < yHalf; y++)
|
|
for (x = 0; x < xHalf; x++)
|
|
{
|
|
int filterIndex = x + (y + yHalf + 1) * xKernel;
|
|
int fieldIndex = (x + xResPadded - xHalf) + y * xResPadded;
|
|
filter[fieldIndex][0] = filter[fieldIndex][1] = kernel[filterIndex];
|
|
}
|
|
|
|
// perform forward FFT on field
|
|
fftw_complex* paddedTransformed = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
|
if (!paddedTransformed)
|
|
{
|
|
cout << " T-IMAGE: Not enough memory! Try a smaller final image size." << endl;
|
|
return false;
|
|
}
|
|
fftw_plan forwardField = fftw_plan_dft_2d(xResPadded, yResPadded, padded, paddedTransformed, FFTW_FORWARD, FFTW_ESTIMATE);
|
|
fftw_execute(forwardField);
|
|
|
|
// perform forward FFT on filter
|
|
fftw_complex* filterTransformed = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * xResPadded * yResPadded);
|
|
if (!filterTransformed)
|
|
{
|
|
cout << " T-FILTER: Not enough memory! Try a smaller final image size." << endl;
|
|
return false;
|
|
}
|
|
fftw_plan forwardFilter = fftw_plan_dft_2d(xResPadded, yResPadded, filter, filterTransformed, FFTW_FORWARD, FFTW_ESTIMATE);
|
|
fftw_execute(forwardFilter);
|
|
|
|
// apply frequency space filter
|
|
for (index = 0; index < xResPadded * yResPadded; index++)
|
|
{
|
|
float newReal = paddedTransformed[index][0] * filterTransformed[index][0] -
|
|
paddedTransformed[index][1] * filterTransformed[index][1];
|
|
float newIm = paddedTransformed[index][0] * filterTransformed[index][1] +
|
|
paddedTransformed[index][1] * filterTransformed[index][0];
|
|
paddedTransformed[index][0] = newReal;
|
|
paddedTransformed[index][1] = newIm;
|
|
}
|
|
|
|
// transform back
|
|
fftw_plan backwardField = fftw_plan_dft_2d(xResPadded, yResPadded, paddedTransformed, padded, FFTW_BACKWARD, FFTW_ESTIMATE);
|
|
fftw_execute(backwardField);
|
|
|
|
// copy back into padded
|
|
index = 0;
|
|
for (y = 0; y < ySource; y++)
|
|
for (x = 0; x < xSource; x++, index++)
|
|
{
|
|
int paddedIndex = (x + xKernel / 2) + (y + yKernel / 2) * xResPadded;
|
|
source[index] = padded[paddedIndex][0];
|
|
}
|
|
|
|
// clean up
|
|
fftw_free(padded);
|
|
fftw_free(paddedTransformed);
|
|
fftw_free(filter);
|
|
fftw_free(filterTransformed);
|
|
|
|
// if normalization is exceeded, renormalize
|
|
float newMax = 0.0f;
|
|
for (x = 0; x < xSource * ySource; x++)
|
|
newMax = (newMax < source[x]) ? source[x] : newMax;
|
|
if (newMax > maxProduct)
|
|
{
|
|
float scale = maxProduct / newMax;
|
|
for (x = 0; x < xSource * ySource; x++)
|
|
source[x] *= scale;
|
|
}
|
|
|
|
return true;
|
|
}
|