Changeset 11703
- Timestamp:
- Feb 7, 2007, 6:17:58 PM (19 years ago)
- Location:
- trunk/psLib
- Files:
-
- 1 added
- 9 edited
-
src/fft/psImageFFT.c (modified) (2 diffs)
-
src/fft/psImageFFT.h (modified) (2 diffs)
-
src/fft/psVectorFFT.c (modified) (3 diffs)
-
src/fft/psVectorFFT.h (modified) (2 diffs)
-
src/imageops/psImageConvolve.c (modified) (5 diffs)
-
src/imageops/psImageConvolve.h (modified) (4 diffs)
-
test/imageops (modified) (1 prop)
-
test/imageops/.cvsignore (modified) (1 diff)
-
test/imageops/Makefile.am (modified) (1 diff)
-
test/imageops/tap_psImageConvolve2.c (added)
Legend:
- Unmodified
- Added
- Removed
-
trunk/psLib/src/fft/psImageFFT.c
r11668 r11703 1 /** @file psImageFFT.c 2 * 3 * @brief Contains FFT transform related functions for psImage. 4 * 5 * @author Robert DeSonia, MHPCC 6 * 7 * @version $Revision: 1.20 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2007-02-06 21:36:09 $ 9 * 10 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 11 */ 1 /// @file psImageFFT.c 2 /// 3 /// @brief Contains FFT transform related functions for psImage. 4 /// 5 /// @author Paul Price, IfA 6 /// @author Robert DeSonia, MHPCC 7 /// 8 /// @version $Revision: 1.21 $ $Name: not supported by cvs2svn $ 9 /// @date $Date: 2007-02-08 04:17:58 $ 10 /// 11 /// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 12 /// 12 13 13 14 #ifdef HAVE_CONFIG_H … … 20 21 #include <fftw3.h> 21 22 22 #include "ps ImageFFT.h"23 #include "psAssert.h" 23 24 #include "psError.h" 24 25 #include "psMemory.h" 25 26 #include "psLogMsg.h" 27 #include "psConstants.h" 26 28 #include "psImageStructManip.h" 27 28 29 30 #define PS_FFTW_PLAN_RIGOR FFTW_ESTIMATE 31 32 static bool p_fftwWisdomImported = false; 33 34 psImage* psImageFFT(psImage* out, const psImage* image, psFFTFlags direction) 35 { 36 psU32 numCols; 37 psU32 numRows; 38 psElemType type; 39 fftwf_plan plan; 40 41 /* got good image data? */ 42 if (image == NULL) { 43 psFree(out); 29 #include "psImageFFT.h" 30 31 32 #define FFTW_PLAN_RIGOR FFTW_ESTIMATE // How rigorous the FFTW planning is 33 34 static psBool fftwWisdomImported = false; // Has the FFTW wisdom been imported yet? 35 36 bool psImageForwardFFT(psImage **real, psImage **imag, const psImage *in) 37 { 38 PS_ASSERT_IMAGE_NON_NULL(in, false); 39 PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, false); 40 PS_ASSERT_PTR_NON_NULL(real, false); 41 PS_ASSERT_PTR_NON_NULL(imag, false); 42 43 // Make sure the system-level wisdom information is imported. 44 if (!fftwWisdomImported) { 45 fftwf_import_system_wisdom(); 46 fftwWisdomImported = true; 47 } 48 49 int numCols = in->numCols; // Number of columns 50 int numRows = in->numRows; // Number of rows 51 52 psF32 *input; // Input data, for FFTW 53 if (!in->parent) { 54 // No parent --- can just use the data buffer 55 input = psMemIncrRefCounter(in->p_rawDataBuffer); 56 } else { 57 // Need to copy the data 58 input = psAlloc(numCols * numRows); 59 for (int y = 0; y < numRows; y++) { 60 memcpy(&input[y * numRows], in->data.F32[y], numCols); 61 } 62 } 63 64 // Do the FFT 65 fftwf_complex *out = fftwf_malloc((numCols/2 + 1) * numRows * sizeof(fftwf_complex)); // Output data 66 fftwf_plan plan = fftwf_plan_dft_r2c_2d(numRows, numCols, input, out, FFTW_PLAN_RIGOR); 67 fftwf_execute(plan); 68 fftwf_destroy_plan(plan); 69 psFree(input); 70 71 // Pull the real and imaginary parts out 72 numCols = numCols/2 + 1; // x dimension is halved by FFTW 73 *real = psImageRecycle(*real, numCols, numRows, PS_TYPE_F32); 74 *imag = psImageRecycle(*imag, numCols, numRows, PS_TYPE_F32); 75 for (int y = 0, index = 0; y < numRows; y++) { 76 for (int x = 0; x < numCols; index++, x++) { 77 #if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) 78 // C99 complex support 79 (*real)->data.F32[y][x] = creal(out[index]); 80 (*imag)->data.F32[y][x] = cimag(out[index]); 81 #else 82 // FFTW's backup complex support 83 (*real)->data.F32[y][x] = out[index][0]; 84 (*imag)->data.F32[y][x] = out[index][1]; 85 #endif 86 } 87 } 88 fftwf_free(out); 89 90 return true; 91 } 92 93 bool psImageBackwardFFT(psImage **out, const psImage *real, const psImage *imag, int origCols) 94 { 95 PS_ASSERT_IMAGE_NON_NULL(real, false); 96 PS_ASSERT_IMAGE_TYPE(real, PS_TYPE_F32, false); 97 PS_ASSERT_IMAGE_NON_NULL(imag, false); 98 PS_ASSERT_IMAGE_TYPE(imag, PS_TYPE_F32, false); 99 PS_ASSERT_IMAGES_SIZE_EQUAL(real, imag, false); 100 PS_ASSERT_PTR_NON_NULL(out, false); 101 102 int numCols = real->numCols; // Number of columns 103 int numRows = real->numRows; // Number of rows 104 105 // Because of the way FFT r2c and c2r work, need the number of columns in the target to be: 106 // 2 * numCols = 2*(numCols/2 + 1) = origCols % 2 ? origCols + 1 : origCols + 2 107 if (2 * numCols != ((origCols % 2) ? origCols + 1 : origCols + 2)) { 108 psError(PS_ERR_BAD_PARAMETER_VALUE, false, 109 "Number of columns in FFT-ed images (%d) should be origCols (%d) / 2 + 1 = %d", 110 numCols, origCols, origCols/2 + 1); 111 return false; 112 } 113 114 if (*out && (*out)->parent) { 115 // It has a parent, so we can't write directly into the buffer. 116 // It had better be the correct size and type, because we don't want to resize a child image 117 PS_ASSERT_IMAGE_SIZE(*out, origCols, numRows, false); 118 PS_ASSERT_IMAGE_TYPE(*out, PS_TYPE_F32, false); 119 } 120 121 122 // Make sure the system-level wisdom information is imported. 123 if (!fftwWisdomImported) { 124 fftwf_import_system_wisdom(); 125 fftwWisdomImported = true; 126 } 127 128 // Stuff the real and imaginary parts in 129 psF32 *target = psAlloc(2 * numCols * numRows * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); // Target for FFTW 130 fftwf_complex *in = fftwf_malloc(numCols * numRows * sizeof(fftwf_complex)); // Input data 131 for (int y = 0, index = 0; y < numRows; y++) { 132 for (int x = 0; x < numCols; index++, x++) { 133 #if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) 134 // C99 complex support 135 in[index] = real->data.F32[y][x] + imag->data.F32[y][x] * I; 136 #else 137 // FFTW's backup complex support 138 in[index][0] = real->data.F32[y][x]; 139 in[index][1] = imag->data.F32[y][x]; 140 #endif 141 } 142 } 143 144 // Do the FFT 145 fftwf_plan plan = fftwf_plan_dft_c2r_2d(numRows, origCols, in, target, FFTW_PLAN_RIGOR); 146 fftwf_execute(plan); 147 fftwf_destroy_plan(plan); 148 fftwf_free(in); 149 150 if (!(*out) || !(*out)->parent) { 151 *out = psImageRecycle(*out, origCols, numRows, PS_TYPE_F32); 152 } 153 154 // Copy the target pixels into the output 155 // There's a slight offset --- target has the additional padding required by FFTW, 156 // while the output doesn't. 157 for (int y = 0, index = 0; y < numRows; y++, index += origCols) { 158 memcpy(&(*out)->data.F32[y][0], &target[index], numRows * PSELEMTYPE_SIZEOF(PS_TYPE_F32)); 159 } 160 psFree(target); 161 162 return true; 163 } 164 165 psImage* psImagePowerSpectrum(psImage *out, const psImage *in) 166 { 167 PS_ASSERT_IMAGE_NON_NULL(in, NULL); 168 PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL); 169 170 psImage *real = NULL; // Real component of FFT 171 psImage *imag = NULL; // Imaginary component of FFT 172 173 if (!psImageForwardFFT(&real, &imag, in)) { 174 psError(PS_ERR_UNKNOWN, false, "Unable to perform forward FFT."); 44 175 return NULL; 45 176 } 46 177 47 if ( ((direction & PS_FFT_FORWARD) != 0) ) { 48 if ((direction & PS_FFT_REVERSE) != 0) { 49 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 50 _("Can not specify both PS_FFT_FORWARD and PS_FFT_REVERSE options.")); 51 psFree(out); 52 return NULL; 53 } 54 if ((direction & PS_FFT_REAL_RESULT) != 0) { 55 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 56 _("The PS_FFT_FORWARD and PS_FFT_REAL_RESULT combinition is not supported.")); 57 psFree(out); 58 return NULL; 59 } 60 } else if ((direction & PS_FFT_REVERSE) == 0) { 178 int numCols = real->numCols; // Number of columns 179 int numRows = real->numRows; // Number of rows 180 181 float norm = 1.0 / PS_SQR(in->numCols) / PS_SQR(in->numRows); 182 for (int y = 0; y < numRows; y++) { 183 for (int x = 0; x < numCols; x++) { 184 // Power spectrum is the square of the complex modulus 185 real->data.F32[y][x] = norm * (PS_SQR(real->data.F32[y][x]) + PS_SQR(imag->data.F32[y][x])); 186 } 187 } 188 psFree(imag); 189 190 return real; 191 } 192 193 194 195 bool psImageComplexMultiply(psImage **outReal, psImage **outImag, 196 const psImage *in1Real, const psImage *in1Imag, 197 const psImage *in2Real, const psImage *in2Imag) 198 { 199 PS_ASSERT_IMAGE_NON_NULL(in1Real, false); 200 PS_ASSERT_IMAGE_NON_NULL(in1Imag, false); 201 PS_ASSERT_IMAGE_NON_NULL(in2Real, false); 202 PS_ASSERT_IMAGE_NON_NULL(in2Imag, false); 203 PS_ASSERT_IMAGE_TYPE(in1Real, PS_TYPE_F32, false); 204 PS_ASSERT_IMAGE_TYPE(in1Imag, PS_TYPE_F32, false); 205 PS_ASSERT_IMAGE_TYPE(in2Real, PS_TYPE_F32, false); 206 PS_ASSERT_IMAGE_TYPE(in2Imag, PS_TYPE_F32, false); 207 PS_ASSERT_IMAGES_SIZE_EQUAL(in1Imag, in1Real, false); 208 PS_ASSERT_IMAGES_SIZE_EQUAL(in2Real, in1Real, false); 209 PS_ASSERT_IMAGES_SIZE_EQUAL(in2Imag, in1Real, false); 210 PS_ASSERT_PTR_NON_NULL(outReal, false); 211 PS_ASSERT_PTR_NON_NULL(outImag, false); 212 if (*outReal) { 213 PS_ASSERT_IMAGE_NON_NULL(*outReal, false); 214 PS_ASSERT_IMAGE_NON_NULL(*outImag, false); 215 } else if (*outImag) { 61 216 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 62 _("Must specify either PS_FFT_FORWARD or PS_FFT_REVERSE option.")); 63 psFree(out); 64 return NULL; 65 } 66 67 type = image->type.type; 68 69 /* make sure the system-level wisdom information is imported. */ 70 if (!p_fftwWisdomImported) { 71 fftwf_import_system_wisdom(); 72 p_fftwWisdomImported = true; 73 } 74 75 numRows = image->numRows; 76 numCols = image->numCols; 77 78 // n.b. FFTW can perform a in-place transform at the same rate or faster than out-of-place. 79 psS32 sign = ((direction & PS_FFT_FORWARD) != 0) ? FFTW_FORWARD : FFTW_BACKWARD; 80 81 fftwf_complex* outBuffer = fftwf_malloc(numRows*numCols*sizeof(fftwf_complex)); 82 p_psImageCopyToRawBuffer(outBuffer, image, PS_TYPE_C32); 83 84 plan = fftwf_plan_dft_2d(numRows, numCols, 85 outBuffer, 86 outBuffer, 87 sign, 88 PS_FFTW_PLAN_RIGOR); 89 90 /* check if a plan exists now -- if not, it is a real problem at this point */ 91 if (plan == NULL) { 92 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 93 _("Could not create a valid FFT plan to perform the transform.")); 94 psFree(out); 95 return NULL; 96 } 97 98 /* finally, call FFTW with the plan made above */ 99 fftwf_execute(plan); 100 101 fftwf_destroy_plan(plan); 102 103 if (direction & PS_FFT_REAL_RESULT) { 104 // n.b., we do this instead of using fftwf_plan_dft_c2r because that 105 // plan requires a half-image, which would require a image reordering 106 // that is not as simple as performing a normal complex transform and 107 // then taking the real part of the result. If performance here 108 // becomes an issue, the use of fftwf_plan_dft_r2c should be considered 109 // as well as fftwf_plan_dft_c2r. 110 out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32); 111 int index = 0; 112 for (int row=0; row < numRows; row++) { 113 psF32* outRow = out->data.F32[row]; 114 for (int col=0; col < numCols; col++) { 115 outRow[col] = crealf(outBuffer[index++]); // take just the real part 116 } 117 } 217 "If the output real part is provided, the output imaginary part must also be provided."); 218 return false; 219 } 220 221 int numRows = in1Real->numRows; // Number of rows 222 int numCols = in1Real->numCols; // Number of columns 223 224 // Need to worry if the outputs are children 225 psImage *targetReal, *targetImag; // Target real and imaginary parts 226 227 if ((*outReal)->parent) { 228 // It had better be the correct size and type, because we don't want to resize a child image 229 PS_ASSERT_IMAGE_TYPE(*outReal, PS_TYPE_F32, false); 230 PS_ASSERT_IMAGE_SIZE(*outReal, numCols, numRows, false); 231 targetReal = psImageAlloc(numCols, numRows, PS_TYPE_F32); 118 232 } else { 119 out = psImageRecycle(out,numCols,numRows,PS_TYPE_C32); 120 int index = 0; 121 for (int row=0; row < numRows; row++) { 122 psC32* outRow = out->data.C32[row]; 123 for (int col=0; col < numCols; col++) { 124 outRow[col] = outBuffer[index++]; // take just the real part 125 } 126 } 127 // memcpy(out->rawDataBuffer, outBuffer, numRows*numCols*sizeof(fftwf_complex)); 128 } 129 130 fftwf_free(outBuffer); 131 132 return out; 133 134 } 135 136 psImage* psImageReal(psImage* out, const psImage* in) 137 { 138 psElemType type; 139 psU32 numCols; 140 psU32 numRows; 141 142 if (in == NULL) { 143 psFree(out); 144 return NULL; 145 } 146 147 type = in->type.type; 148 numCols = in->numCols; 149 numRows = in->numRows; 150 151 /* if not a complex number, this is logically just a copy then */ 152 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) { 153 return psImageCopy(out, in, type); 154 } 155 156 if (type == PS_TYPE_C32) { 157 psF32* outRow; 158 psC32* inRow; 159 160 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32); 161 for (psU32 row = 0; row < numRows; row++) { 162 outRow = out->data.F32[row]; 163 inRow = in->data.C32[row]; 164 165 for (psU32 col = 0; col < numCols; col++) { 166 outRow[col] = crealf(inRow[col]); 167 } 168 } 169 } else if (type == PS_TYPE_C64) { 170 psF64* outRow; 171 psC64* inRow; 172 173 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64); 174 for (psU32 row = 0; row < numRows; row++) { 175 outRow = out->data.F64[row]; 176 inRow = in->data.C64[row]; 177 178 for (psU32 col = 0; col < numCols; col++) { 179 outRow[col] = creal(inRow[col]); 180 } 181 } 233 *outReal = psImageRecycle(*outReal, numCols, numRows, PS_TYPE_F32); 234 targetReal = psMemIncrRefCounter(*outReal); 235 } 236 if ((*outImag)->parent) { 237 // It had better be the correct size and type, because we don't want to resize a child image 238 if ((*outReal)->numCols != numCols || (*outReal)->numRows != numRows || 239 (*outImag)->type.type != PS_TYPE_F32) { 240 // Plug potential memory leak --- need to free targetReal if there's a problem 241 psFree(targetReal); 242 } 243 PS_ASSERT_IMAGE_TYPE(*outReal, PS_TYPE_F32, false); 244 PS_ASSERT_IMAGE_SIZE(*outReal, numCols, numRows, false); 245 targetImag = psImageAlloc(numCols, numRows, PS_TYPE_F32); 182 246 } else { 183 char* typeStr; 184 PS_TYPE_NAME(typeStr,type); 185 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 186 _("Specified psImage type, %s, is not supported."), 187 typeStr); 188 189 psFree(out); 190 return NULL; 191 } 192 193 return out; 194 } 195 196 psImage* psImageImaginary(psImage* out, const psImage* in) 197 { 198 psElemType type; 199 psU32 numCols; 200 psU32 numRows; 201 202 if (in == NULL) { 203 psFree(out); 204 return NULL; 205 } 206 207 type = in->type.type; 208 numCols = in->numCols; 209 numRows = in->numRows; 210 211 /* if not a complex image type, this is logically just zeroed image of same size */ 212 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) { 213 out = psImageRecycle(out, numCols, numRows, type); 214 memset(out->data.V[0], 0, PSELEMTYPE_SIZEOF(type) * numCols * numRows); 215 return out; 216 } 217 218 if (type == PS_TYPE_C32) { 219 psF32* outRow; 220 psC32* inRow; 221 222 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32); 223 for (psU32 row = 0; row < numRows; row++) { 224 outRow = out->data.F32[row]; 225 inRow = in->data.C32[row]; 226 227 for (psU32 col = 0; col < numCols; col++) { 228 outRow[col] = cimagf(inRow[col]); 229 } 230 } 231 } else if (type == PS_TYPE_C64) { 232 psF64* outRow; 233 psC64* inRow; 234 235 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64); 236 for (psU32 row = 0; row < numRows; row++) { 237 outRow = out->data.F64[row]; 238 inRow = in->data.C64[row]; 239 240 for (psU32 col = 0; col < numCols; col++) { 241 outRow[col] = cimag(inRow[col]); 242 } 243 } 244 } else { 245 char* typeStr; 246 PS_TYPE_NAME(typeStr,type); 247 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 248 _("Specified psImage type, %s, is not supported."), 249 typeStr); 250 psFree(out); 251 return NULL; 252 } 253 254 return out; 255 } 256 257 psImage* psImageComplex(psImage* out, const psImage* real, const psImage* imag) 258 { 259 psElemType type; 260 psU32 numCols; 261 psU32 numRows; 262 263 if (real == NULL || imag == NULL) { 264 psFree(out); 265 return NULL; 266 } 267 268 type = real->type.type; 269 numCols = real->numCols; 270 numRows = real->numRows; 271 272 if (imag->type.type != type) { 273 char* typeStrReal; 274 char* typeStrImag; 275 PS_TYPE_NAME(typeStrReal,type); 276 PS_TYPE_NAME(typeStrImag,imag->type.type); 277 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 278 _("Real psImage type (%s) and imaginary psImage type (%s) must be the same."), 279 typeStrReal,typeStrImag); 280 psFree(out); 281 return NULL; 282 } 283 284 if (imag->numCols != numCols || imag->numRows != numRows) { 285 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 286 _("Real psImage size (%dx%d) and imaginary psImage size (%dx%d) must be the same."), 287 numCols, numRows, imag->numCols, imag->numRows); 288 psFree(out); 289 return NULL; 290 } 291 292 if (type == PS_TYPE_F32) { 293 psC32* outRow; 294 psF32* realRow; 295 psF32* imagRow; 296 297 out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32); 298 299 for (psU32 row = 0; row < numRows; row++) { 300 outRow = out->data.C32[row]; 301 realRow = real->data.F32[row]; 302 imagRow = imag->data.F32[row]; 303 304 for (psU32 col = 0; col < numCols; col++) { 305 outRow[col] = realRow[col] + I * imagRow[col]; 306 } 307 } 308 } else if (type == PS_TYPE_F64) { 309 psC64* outRow; 310 psF64* realRow; 311 psF64* imagRow; 312 313 out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64); 314 for (psU32 row = 0; row < numRows; row++) { 315 outRow = out->data.C64[row]; 316 realRow = real->data.F64[row]; 317 imagRow = imag->data.F64[row]; 318 319 for (psU32 col = 0; col < numCols; col++) { 320 outRow[col] = realRow[col] + I * imagRow[col]; 321 } 322 } 323 } else { 324 char* typeStr; 325 PS_TYPE_NAME(typeStr,type); 326 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 327 _("Input psImage type, %s, is required to be either psF32 or psF64."), 328 typeStr); 329 psFree(out); 330 return NULL; 331 } 332 333 334 return out; 335 } 336 337 psImage* psImageConjugate(psImage* out, const psImage* in) 338 { 339 psElemType type; 340 psU32 numCols; 341 psU32 numRows; 342 343 if (in == NULL) { 344 psFree(out); 345 return NULL; 346 } 347 348 type = in->type.type; 349 numCols = in->numCols; 350 numRows = in->numRows; 351 352 /* if not a complex image, this is logically just a image copy */ 353 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) { 354 return psImageCopy(out, in, type); 355 } 356 357 if (type == PS_TYPE_C32) { 358 psC32* outRow; 359 psC32* inRow; 360 361 out = psImageRecycle(out, numCols, numRows, PS_TYPE_C32); 362 for (psU32 row = 0; row < numRows; row++) { 363 outRow = out->data.C32[row]; 364 inRow = in->data.C32[row]; 365 366 for (psU32 col = 0; col < numCols; col++) { 367 outRow[col] = crealf(inRow[col]) - I * cimagf(inRow[col]); 368 } 369 } 370 } else if (type == PS_TYPE_C64) { 371 psC64* outRow; 372 psC64* inRow; 373 374 out = psImageRecycle(out, numCols, numRows, PS_TYPE_C64); 375 for (psU32 row = 0; row < numRows; row++) { 376 outRow = out->data.C64[row]; 377 inRow = in->data.C64[row]; 378 379 for (psU32 col = 0; col < numCols; col++) { 380 outRow[col] = creal(inRow[col]) - I * cimag(inRow[col]); 381 } 382 } 383 } else { 384 char* typeStr; 385 PS_TYPE_NAME(typeStr,type); 386 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 387 _("Input psImage type, %s, is required to be either psC32 or psC64."), 388 typeStr); 389 psFree(out); 390 return NULL; 391 } 392 393 return out; 394 } 395 396 psImage* psImagePowerSpectrum(psImage* out, const psImage* in) 397 { 398 psElemType type; 399 psU32 numCols; 400 psU32 numRows; 401 402 if (in == NULL) { 403 psFree(out); 404 return NULL; 405 } 406 407 type = in->type.type; 408 numCols = in->numCols; 409 numRows = in->numRows; 410 411 if (type == PS_TYPE_C32) { 412 psF32* outRow; 413 psC32* inRow; 414 psF32 real; 415 psF32 imag; 416 psF32 fNumCols = numCols; 417 psF32 fNumRows = numRows; 418 psF32 numElementsSquared = fNumCols * fNumCols * fNumRows * fNumRows; 419 420 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F32); 421 for (psU32 row = 0; row < numRows; row++) { 422 outRow = out->data.F32[row]; 423 inRow = in->data.C32[row]; 424 425 for (psU32 col = 0; col < numCols; col++) { 426 real = crealf(inRow[col]); 427 imag = cimagf(inRow[col]); 428 outRow[col] = (real * real + imag * imag) / numElementsSquared; 429 } 430 } 431 } else if (type == PS_TYPE_C64) { 432 psF64* outRow; 433 psC64* inRow; 434 psF64 real; 435 psF64 imag; 436 psF64 numElementsSquared = numCols * numCols * numRows * numRows; 437 438 out = psImageRecycle(out, numCols, numRows, PS_TYPE_F64); 439 for (psU32 row = 0; row < numRows; row++) { 440 outRow = out->data.F64[row]; 441 inRow = in->data.C64[row]; 442 443 for (psU32 col = 0; col < numCols; col++) { 444 real = creal(inRow[col]); 445 imag = cimag(inRow[col]); 446 outRow[col] = (real * real + imag * imag) / numElementsSquared; 447 } 448 } 449 } else { 450 char* typeStr; 451 PS_TYPE_NAME(typeStr,type); 452 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 453 _("Input psImage type, %s, is required to be either psC32 or psC64."), 454 typeStr); 455 psFree(out); 456 return NULL; 457 } 458 459 return out; 460 461 } 247 *outImag = psImageRecycle(*outImag, numCols, numRows, PS_TYPE_F32); 248 targetImag = psMemIncrRefCounter(*outImag); 249 } 250 251 for (int y = 0; y < numRows; y++) { 252 for (int x = 0; x < numCols; x++) { 253 // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i 254 float real = in1Real->data.F32[y][x] * in2Real->data.F32[y][x] - 255 in1Imag->data.F32[y][x] * in2Imag->data.F32[y][x]; 256 float imag = in1Imag->data.F32[y][x] * in2Real->data.F32[y][x] + 257 in1Real->data.F32[y][x] * in2Imag->data.F32[y][x]; 258 targetReal->data.F32[y][x] = real; 259 targetImag->data.F32[y][x] = imag; 260 } 261 } 262 263 if ((*outReal)->parent) { 264 *outReal = psImageCopy(*outReal, targetReal, PS_TYPE_F32); 265 } 266 if ((*outImag)->parent) { 267 *outImag = psImageCopy(*outImag, targetImag, PS_TYPE_F32); 268 } 269 270 psFree(targetReal); 271 psFree(targetImag); 272 273 return true; 274 } -
trunk/psLib/src/fft/psImageFFT.h
r11248 r11703 1 /* @file psImageFFT.h 2 * @brief Contains FFT transform related functions for psImage 3 * 4 * @author Robert DeSonia, MHPCC 5 * 6 * @version $Revision: 1.7 $ $Name: not supported by cvs2svn $ 7 * @date $Date: 2007-01-23 22:47:22 $ 8 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 9 */ 1 /// @file psImageFFT.h 2 /// @brief Contains FFT transform related functions for psImage 3 /// 4 /// @author Paul Price, IfA 5 /// @author Robert DeSonia, MHPCC 6 /// 7 /// @version $Revision: 1.8 $ $Name: not supported by cvs2svn $ 8 /// @date $Date: 2007-02-08 04:17:58 $ 9 /// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 10 /// 10 11 11 12 #ifndef PS_IMAGE_FFT_H … … 13 14 14 15 #include "psImage.h" 15 #include "psVectorFFT.h" // for psFFTFlags16 16 17 17 /// @addtogroup MathOps Mathematical Operations 18 18 /// @{ 19 19 20 /** Forward and reverse FFT calculations. 21 * 22 * This takes as input the image of interest (in) and the direction 23 * (direction), which is specified by an enumerated type psFftDirection. 24 * The input image may be of type psF32 or psC32, the result is always 25 * psC32. If the input vector is psF32, the direction must be forward. 26 * 27 * @return psImage* the FFT transformation result 28 */ 29 psImage* psImageFFT( 30 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 31 const psImage* image, ///< the psImage to apply transform to 32 psFFTFlags direction ///< the direction of the transform 33 ); 20 /// Forward FFT of an image 21 /// 22 /// Applies a forward FFT (exponent -1), with the result returned in both real and imaginary parts. The FFT 23 /// is not normalised (a forward followed by a reverse is the original scaled by the total number of pixels). 24 /// The FFT takes advantage of the fact that the input is purely real; hence the output number of columns is 25 /// numCols/2 + 1 (with division rounding down). Only implemented for F32 input. 26 bool psImageForwardFFT(psImage **real, ///< Real part of FFT 27 psImage **imag, ///< Imaginary part of FFT 28 const psImage *in///< Input image (F32) 29 ); 34 30 35 /** extract the real portion of a complex image 36 * 37 * @return psImage* real portion of the input image. 38 */ 39 psImage* psImageReal( 40 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 41 const psImage* in ///< the psImage to extract real portion from 42 ); 31 /// Backward FFT of an image 32 /// 33 /// Applies a backward FFT (exponent +1) from the real and imaginary parts with the (purely) real result 34 /// returned. The FFT is not normalised (a forward followed by a reverse is the original scaled by the total 35 /// number of pixels). The FFT takes advantage of the fact that the output will be purely real; hence the 36 /// input number of columns is numCols/2 + 1 (with division rounding down); to manage the redundancy (is the 37 /// original number of columns even or odd?), we need the original number of columns (the number of columns of 38 /// the image that was input to psImageForwardFFT) to be provided. Only implemented for F32 input. 39 bool psImageBackwardFFT(psImage **out,///< Output image 40 const psImage *real, ///< Real input (F32) 41 const psImage *imag, ///< Imaginary input (F32) 42 int origCols ///< Original number of columns 43 ); 43 44 44 /** extract the imaginary portion of a complex image45 *46 * @return psImage* imaginary portion of the input image.47 */48 psImage* psImageImaginary(49 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made.50 const psImage* in ///< the psImage to extract imaginary portion from51 );52 45 53 /** creates a complex image from separate real and imaginary plane images 54 * 55 * @return psImage* resulting complex image 56 */ 57 psImage* psImageComplex( 58 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 59 const psImage* real, ///< the real plane image 60 const psImage* imag ///< the imaginary plane image 61 ); 46 /// Power spectrum of an image 47 /// 48 /// Generates the power spectrum of an image. Only implemented for F32 input. 49 psImage* psImagePowerSpectrum(psImage *out, const psImage *in); 62 50 63 /** computes the complex conjugate of an image 64 * 65 * @return psImage* the complex conjugate of the 'in' image 66 */ 67 psImage* psImageConjugate( 68 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 69 const psImage* in ///< the psImage to compute conjugate of 70 ); 71 72 /** computes the power spectrum of an image 73 * 74 * @return psImage* the power spectrum of the 'in' image 75 */ 76 psImage* psImagePowerSpectrum( 77 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 78 const psImage* in ///< the psImage to power spectrum of 79 ); 51 /// Multiply complex images 52 /// 53 /// The input images are the real and imaginary parts of each of two images. The real and imaginary parts 54 /// of the output image are returned. Only implemented for F32 input. 55 bool psImageComplexMultiply(psImage **outReal, ///< Real part of output 56 psimage **outImag, ///< Imaginary part of output 57 const psImage *in1Real, ///< Real part of input 1 58 const psImage *in1Imag, ///< Imaginary part of input 1 59 const psImage *in2Real, ///< Real part of input 2 60 const psImage *in2Imag ///< Imaginary part of input 2 61 ); 80 62 81 63 /// @} -
trunk/psLib/src/fft/psVectorFFT.c
r11668 r11703 3 3 * @brief Contains FFT transform related functions for psVector 4 4 * 5 * @author Paul Price, IfA 5 6 * @author Robert DeSonia, MHPCC 6 7 * 7 * @version $Revision: 1.3 6$ $Name: not supported by cvs2svn $8 * @date $Date: 2007-02-0 6 21:36:09$8 * @version $Revision: 1.37 $ $Name: not supported by cvs2svn $ 9 * @date $Date: 2007-02-08 04:17:58 $ 9 10 * 10 11 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii … … 12 13 13 14 #ifdef HAVE_CONFIG_H 14 # include "config.h"15 #include "config.h" 15 16 #endif 16 17 … … 21 22 #include <fftw3.h> 22 23 23 #include "ps VectorFFT.h"24 #include "psAssert.h" 24 25 #include "psError.h" 25 26 #include "psMemory.h" 26 27 #include "psLogMsg.h" 28 #include "psConstants.h" 29 #include "psVectorFFT.h" 30 31 #define FFTW_PLAN_RIGOR FFTW_ESTIMATE // How rigorous the FFTW planning is 32 33 static psBool fftwWisdomImported = false; // Has the system wisdom been imported? 34 35 bool psVectorForwardFFT(psVector **real, psVector **imag, const psVector *in) 36 { 37 PS_ASSERT_VECTOR_NON_NULL(in, false); 38 PS_ASSERT_VECTOR_TYPE(in, PS_TYPE_F32, false); 39 PS_ASSERT_PTR_NON_NULL(real, false); 40 PS_ASSERT_PTR_NON_NULL(imag, false); 41 42 // Make sure the system-level wisdom information is imported. 43 if (!fftwWisdomImported) { 44 fftwf_import_system_wisdom(); 45 fftwWisdomImported = true; 46 } 47 48 long num = in->n; // Number of elements 49 50 // Do the FFT 51 fftwf_complex *out = fftwf_malloc(num * sizeof(fftwf_complex)); // Output data 52 fftwf_plan plan = fftwf_plan_dft_r2c_1d(num, in->data.F32, out, FFTW_PLAN_RIGOR); 53 fftwf_execute(plan); 54 fftwf_destroy_plan(plan); 55 56 // Pull the real and imaginary parts out 57 *real = psVectorRecycle(*real, num/2 + 1, PS_TYPE_F32); 58 *imag = psVectorRecycle(*imag, num/2 + 1, PS_TYPE_F32); 59 for (int i = 0; i < num; i++) { 60 #if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) 61 // C99 complex support 62 (*real)->data.F32[i] = creal(out[i]); 63 (*imag)->data.F32[i] = cimag(out[i]); 64 #else 65 // FFTW's backup complex support 66 (*real)->data.F32[i] = out[i][0]; 67 (*imag)->data.F32[i] = out[i][1]; 68 #endif 69 } 70 fftwf_free(out); 71 72 return true; 73 } 74 75 bool psVectorBackwardFFT(psVector **out, const psVector *real, const psVector *imag) 76 { 77 PS_ASSERT_VECTOR_NON_NULL(real, false); 78 PS_ASSERT_VECTOR_TYPE(real, PS_TYPE_F32, false); 79 PS_ASSERT_VECTOR_NON_NULL(imag, false); 80 PS_ASSERT_VECTOR_TYPE(imag, PS_TYPE_F32, false); 81 PS_ASSERT_VECTORS_SIZE_EQUAL(real, imag, false); 82 PS_ASSERT_PTR_NON_NULL(out, false); 83 84 // Make sure the system-level wisdom information is imported. 85 if (!fftwWisdomImported) { 86 fftwf_import_system_wisdom(); 87 fftwWisdomImported = true; 88 } 89 90 long num = real->n; // Number of elements 91 92 // Stuff the real and imaginary parts in 93 fftwf_complex *in = fftwf_malloc(num * sizeof(fftwf_complex)); // Input data 94 for (int i = 0; i < num; i++) { 95 #if !defined(FFTW_NO_Complex) && defined(_Complex_I) && defined(complex) && defined(I) 96 // C99 complex support 97 in[i] = real->data.F32[i] + imag->data.F32[i] * I; 98 #else 99 // FFTW's backup complex support 100 in[i][0] = real->data.F32[i]; 101 in[i][1] = imag->data.F32[i]; 102 #endif 103 } 27 104 28 105 106 // Do the FFT 107 *out = psVectorRecycle(*out, 2 * num, PS_TYPE_F32); 108 fftwf_plan plan = fftwf_plan_dft_c2r_1d(2 * num, in, (*out)->data.F32, FFTW_PLAN_RIGOR); 109 fftwf_execute(plan); 110 fftwf_destroy_plan(plan); 29 111 30 #define P_FFTW_PLAN_RIGOR FFTW_ESTIMATE 112 fftwf_free(in); 31 113 32 static bool p_fftwWisdomImported = false; 114 return true; 115 } 33 116 34 psVector * psVectorFFT(psVector* out, const psVector* in, psFFTFlags direction)117 psVector *psVectorPowerSpectrum(psVector* out, const psVector* in) 35 118 { 36 psU32 numElements; 37 psElemType type; 38 fftwf_plan plan; 119 PS_ASSERT_VECTOR_NON_NULL(in, NULL); 120 PS_ASSERT_VECTOR_TYPE(in, PS_TYPE_F32, NULL); 39 121 40 /* got good image data? */ 41 if (in == NULL) { 42 psFree(out); 122 psVector *real = NULL; // Real component of FFT 123 psVector *imag = NULL; // Imaginary component of FFT 124 125 if (!psVectorForwardFFT(&real, &imag, in)) { 126 psError(PS_ERR_UNKNOWN, false, "Unable to perform forward FFT."); 43 127 return NULL; 44 128 } 45 129 46 type = in->type.type;130 int num = real->n; // Number of elements 47 131 48 /* make sure the system-level wisdom information is imported. */ 49 if (!p_fftwWisdomImported) { 50 fftwf_import_system_wisdom(); 51 p_fftwWisdomImported = true; 132 float norm = 1.0 / PS_SQR(in->n); 133 for (int i = 0; i < num; i++) { 134 // Power spectrum is the square of the complex modulus 135 real->data.F32[i] = norm * (PS_SQR(real->data.F32[i]) + PS_SQR(imag->data.F32[i])); 136 } 137 psFree(imag); 138 139 return real; 140 } 141 142 bool psVectorComplexMultiply(psVector **outReal, psVector **outImag, 143 const psVector *in1Real, const psVector *in1Imag, 144 const psVector *in2Real, const psVector *in2Imag) 145 { 146 PS_ASSERT_VECTOR_NON_NULL(in1Real, false); 147 PS_ASSERT_VECTOR_NON_NULL(in1Imag, false); 148 PS_ASSERT_VECTOR_NON_NULL(in2Real, false); 149 PS_ASSERT_VECTOR_NON_NULL(in2Imag, false); 150 PS_ASSERT_VECTOR_TYPE(in1Real, PS_TYPE_F32, false); 151 PS_ASSERT_VECTOR_TYPE(in1Imag, PS_TYPE_F32, false); 152 PS_ASSERT_VECTOR_TYPE(in2Real, PS_TYPE_F32, false); 153 PS_ASSERT_VECTOR_TYPE(in2Imag, PS_TYPE_F32, false); 154 PS_ASSERT_VECTORS_SIZE_EQUAL(in1Imag, in1Real, false); 155 PS_ASSERT_VECTORS_SIZE_EQUAL(in2Real, in1Real, false); 156 PS_ASSERT_VECTORS_SIZE_EQUAL(in2Imag, in1Real, false); 157 PS_ASSERT_PTR_NON_NULL(outReal, false); 158 PS_ASSERT_PTR_NON_NULL(outImag, false); 159 if (*outReal) { 160 PS_ASSERT_VECTOR_NON_NULL(*outReal, false); 161 PS_ASSERT_VECTOR_NON_NULL(*outImag, false); 162 } else if (*outImag) { 163 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 164 "If the output real part is provided, the output imaginary part must also be provided."); 165 return false; 52 166 } 53 167 54 numElements = in->n;168 int num = in1Real->n; // Number of elements 55 169 56 out = psVectorCopy(out, in, PS_TYPE_C32);57 out->n = numElements;170 *outReal = psVectorRecycle(*outReal, num, PS_TYPE_F32); 171 *outImag = psVectorRecycle(*outImag, num, PS_TYPE_F32); 58 172 59 if ((direction & PS_FFT_FORWARD) != 0) { 60 plan = fftwf_plan_dft_1d(numElements, 61 (fftwf_complex *) out->data.C32, 62 (fftwf_complex *) out->data.C32, FFTW_FORWARD, P_FFTW_PLAN_RIGOR); 63 } else if ((direction & PS_FFT_REVERSE) != 0) { 64 plan = fftwf_plan_dft_1d(numElements, 65 (fftwf_complex *) out->data.C32, 66 (fftwf_complex *) out->data.C32, FFTW_BACKWARD, P_FFTW_PLAN_RIGOR); 67 } else { 68 psError(PS_ERR_BAD_PARAMETER_VALUE, true, 69 _("Must specify the direction as either PS_FFT_FORWARD or PS_FFT_REVERSE.")); 70 psFree(out); 71 return NULL; 173 for (int i = 0; i < num; i++) { 174 // (a + bi) * (c + di) = (ac - bd) + (bc + ad)i 175 (*outReal)->data.F32[i] = in1Real->data.F32[i] * in2Real->data.F32[i] - 176 in1Imag->data.F32[i] * in2Imag->data.F32[i]; 177 (*outImag)->data.F32[i] = in1Imag->data.F32[i] * in2Real->data.F32[i] + 178 in1Real->data.F32[i] * in2Imag->data.F32[i]; 72 179 } 73 180 74 /* check if a plan exists now */ 75 if (plan == NULL) { 76 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 77 _("Could not create a valid FFT plan to perform the transform.")); 78 psFree(out); 79 return NULL; 80 } 81 82 /* finally, call FFTW with the plan made above */ 83 fftwf_execute(plan); 84 85 fftwf_destroy_plan(plan); 86 87 if ((direction & PS_FFT_REAL_RESULT) != 0) { 88 for (psS32 i = 0; i < numElements; i++) { 89 out->data.F32[i] = out->data.C32[i]; 90 } 91 out->type.type = PS_TYPE_F32; 92 out->data.U8 = psRealloc(out->data.U8,PSELEMTYPE_SIZEOF(PS_TYPE_F32)*out->nalloc); 93 } 94 95 return out; 181 return true; 96 182 } 97 98 psVector* psVectorReal(psVector* out, const psVector* in)99 {100 psElemType type;101 psU32 numElements;102 103 if (in == NULL) {104 psFree(out);105 return NULL;106 }107 108 type = in->type.type;109 numElements = in->n;110 111 /* if not a complex number, this is logically just a copy */112 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {113 // Warn user, as this is probably not expected114 psLogMsg(__func__, PS_LOG_WARN, "Real portion of a non-Complex type called called for. "115 "Just a vector copy was performed.");116 out = psVectorRecycle(out, numElements, type);117 out->n = numElements;118 memcpy(out->data.U8, in->data.U8, numElements * PSELEMTYPE_SIZEOF(type));119 return out;120 }121 122 if (type == PS_TYPE_C32) {123 psF32* outVec;124 psC32* inVec = in->data.C32;125 126 out = psVectorRecycle(out, numElements, PS_TYPE_F32);127 out->n = numElements;128 outVec = out->data.F32;129 130 for (psU32 i = 0; i < numElements; i++) {131 outVec[i] = crealf(inVec[i]);132 }133 } else if (type == PS_TYPE_C64) {134 psF64* outVec;135 psC64* inVec = in->data.C64;136 137 out = psVectorRecycle(out, numElements, PS_TYPE_F64);138 out->n = numElements;139 outVec = out->data.F64;140 141 for (psU32 i = 0; i < numElements; i++) {142 outVec[i] = creal(inVec[i]);143 }144 } else {145 char* typeStr;146 PS_TYPE_NAME(typeStr,type);147 psError(PS_ERR_BAD_PARAMETER_TYPE, true,148 _("Specified psVector type, %s, is not supported."),149 typeStr);150 psFree(out);151 return NULL;152 }153 154 return out;155 }156 157 psVector* psVectorImaginary(psVector* out, const psVector* in)158 {159 psElemType type;160 psU32 numElements;161 162 if (in == NULL) {163 psFree(out);164 return NULL;165 }166 167 type = in->type.type;168 numElements = in->n;169 170 /* if not a complex number, this is logically just zeroed image of same size */171 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {172 // Warn user, as this is probably not expected173 psLogMsg(__func__, PS_LOG_WARN, "Imaginary portion of a non-Complex type called for. "174 "A zeroed vector was returned.");175 out = psVectorRecycle(out, numElements, type);176 out->n = numElements;177 memset(out->data.U8, 0, PSELEMTYPE_SIZEOF(type) * numElements);178 return out;179 }180 181 if (type == PS_TYPE_C32) {182 psF32* outVec;183 psC32* inVec = in->data.C32;184 185 out = psVectorRecycle(out, numElements, PS_TYPE_F32);186 out->n = numElements;187 outVec = out->data.F32;188 189 for (psU32 i = 0; i < numElements; i++) {190 outVec[i] = cimagf(inVec[i]);191 }192 } else if (type == PS_TYPE_C64) {193 psF64* outVec;194 psC64* inVec = in->data.C64;195 196 out = psVectorRecycle(out, numElements, PS_TYPE_F64);197 out->n = numElements;198 outVec = out->data.F64;199 200 for (psU32 i = 0; i < numElements; i++) {201 outVec[i] = cimag(inVec[i]);202 }203 } else {204 char* typeStr;205 PS_TYPE_NAME(typeStr,type);206 psError(PS_ERR_BAD_PARAMETER_TYPE, true,207 _("Specified psVector type, %s, is not supported."),208 typeStr);209 psFree(out);210 return NULL;211 }212 213 return out;214 }215 216 psVector* psVectorComplex(psVector* out, const psVector* real, const psVector* imag)217 {218 psElemType type;219 psU32 numElements;220 221 if (real == NULL || imag == NULL) {222 psFree(out);223 return NULL;224 }225 226 type = real->type.type;227 if (real->n < imag->n) {228 numElements = real->n;229 } else {230 numElements = imag->n;231 }232 233 if (imag->type.type != type) {234 char* typeStrReal;235 char* typeStrImag;236 PS_TYPE_NAME(typeStrReal,type);237 PS_TYPE_NAME(typeStrImag,imag->type.type);238 psError(PS_ERR_BAD_PARAMETER_TYPE, true,239 _("Real psVector type, %s, and imaginary psVector type, %s, must be the same."),240 typeStrReal,typeStrImag);241 psFree(out);242 return NULL;243 }244 245 if (type == PS_TYPE_F32) {246 psC32* outVec;247 psF32* realVec = real->data.F32;248 psF32* imagVec = imag->data.F32;249 250 out = psVectorRecycle(out, numElements, PS_TYPE_C32);251 out->n = numElements;252 outVec = out->data.C32;253 254 for (psU32 i = 0; i < numElements; i++) {255 outVec[i] = realVec[i] + I * imagVec[i];256 }257 } else if (type == PS_TYPE_F64) {258 psC64* outVec;259 psF64* realVec = real->data.F64;260 psF64* imagVec = imag->data.F64;261 262 out = psVectorRecycle(out, numElements, PS_TYPE_C64);263 out->n = numElements;264 outVec = out->data.C64;265 266 for (psU32 i = 0; i < numElements; i++) {267 outVec[i] = realVec[i] + I * imagVec[i];268 }269 } else {270 char* typeStr;271 PS_TYPE_NAME(typeStr,type);272 psError(PS_ERR_BAD_PARAMETER_TYPE, true,273 _("Input psVector type, %s, is required to be either psF32 or psF64."),274 typeStr);275 psFree(out);276 return NULL;277 }278 279 return out;280 }281 282 psVector* psVectorConjugate(psVector* out, const psVector* in)283 {284 psElemType type;285 psU32 numElements;286 287 if (in == NULL) {288 psFree(out);289 return NULL;290 }291 292 type = in->type.type;293 numElements = in->n;294 295 /* if not a complex number, this is logically just a image copy */296 if (!PS_IS_PSELEMTYPE_COMPLEX(type)) {297 // Warn user, as this is probably not expected298 psLogMsg(__func__, PS_LOG_WARN, "Complex Conjugate of a non-Complex type called for. "299 "Vector copy was performed instead.");300 301 out = psVectorRecycle(out, numElements, type);302 out->n = numElements;303 memcpy(out->data.U8, in->data.U8, PSELEMTYPE_SIZEOF(type) * numElements);304 return out;305 }306 307 if (type == PS_TYPE_C32) {308 psC32* outVec;309 psC32* inVec = in->data.C32;310 311 out = psVectorRecycle(out, numElements, PS_TYPE_C32);312 out->n = numElements;313 outVec = out->data.C32;314 315 for (psU32 i = 0; i < numElements; i++) {316 outVec[i] = crealf(inVec[i]) - I * cimagf(inVec[i]);317 }318 } else if (type == PS_TYPE_C64) {319 psC64* outVec;320 psC64* inVec = in->data.C64;321 322 out = psVectorRecycle(out, numElements, PS_TYPE_C64);323 out->n = numElements;324 outVec = out->data.C64;325 326 for (psU32 i = 0; i < numElements; i++) {327 outVec[i] = creal(inVec[i]) - I * cimag(inVec[i]);328 }329 } else {330 char* typeStr;331 PS_TYPE_NAME(typeStr,type);332 psError(PS_ERR_BAD_PARAMETER_TYPE, true,333 _("Input psVector type, %s, is required to be either psC32 or psC64."),334 typeStr);335 psFree(out);336 return NULL;337 }338 339 return out;340 }341 342 psVector* psVectorPowerSpectrum(psVector* out, const psVector* in)343 {344 psElemType type;345 psU32 outNumElements;346 psU32 inNumElements;347 psU32 inHalfNumElements;348 psU32 inNumElementsSquared;349 350 if (in == NULL) {351 psFree(out);352 return NULL;353 }354 355 type = in->type.type;356 inNumElements = in->n;357 inNumElementsSquared = inNumElements * inNumElements;358 inHalfNumElements = inNumElements / 2;359 outNumElements = inHalfNumElements + 1;360 361 if (type == PS_TYPE_C32) {362 psF32* outVec;363 psC32* inVec = in->data.C32;364 psF32 inAbs1;365 psF32 inAbs2;366 367 out = psVectorRecycle(out, outNumElements, PS_TYPE_F32);368 out->n = outNumElements;369 outVec = out->data.F32;370 371 // from ADD: P_0 = |C_0|^2/N^2372 inAbs1 = cabsf(inVec[0]);373 outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;374 375 // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)376 for (psU32 i = 1; i < inHalfNumElements; i++) {377 inAbs1 = cabsf(inVec[i]);378 inAbs2 = cabsf(inVec[inNumElements - i]);379 outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;380 }381 382 // from ADD: P_N/2 = |C_N/2|^2/N^2383 inAbs1 = cabsf(inVec[inHalfNumElements]);384 outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;385 } else if (type == PS_TYPE_C64) {386 psF64* outVec;387 psC64* inVec = in->data.C64;388 psF64 inAbs1;389 psF64 inAbs2;390 391 out = psVectorRecycle(out, outNumElements, PS_TYPE_F64);392 out->n = outNumElements;393 outVec = out->data.F64;394 395 // from ADD: P_0 = |C_0|^2/N^2396 inAbs1 = cabs(inVec[0]);397 outVec[0] = inAbs1 * inAbs1 / inNumElementsSquared;398 399 // from ADD: P_j = (|C_j|^2+|C_N-j|^2)/N^2, where j = 1,2,...,(N/2-1)400 for (psU32 i = 1; i < inHalfNumElements; i++) {401 inAbs1 = cabs(inVec[i]);402 inAbs2 = cabs(inVec[inNumElements - i]);403 outVec[i] = (inAbs1 * inAbs1 + inAbs2 * inAbs2) / inNumElementsSquared;404 }405 406 // from ADD: P_N/2 = |C_N/2|^2/N^2407 inAbs1 = cabs(inVec[inHalfNumElements]);408 outVec[inHalfNumElements] = inAbs1 * inAbs1 / inNumElementsSquared;409 } else {410 char* typeStr;411 PS_TYPE_NAME(typeStr,type);412 psError(PS_ERR_BAD_PARAMETER_TYPE, true,413 _("Input psVector type, %s, is required to be either psC32 or psC64."),414 typeStr);415 psFree(out);416 return NULL;417 }418 419 return out;420 421 } -
trunk/psLib/src/fft/psVectorFFT.h
r11248 r11703 1 /* @file psVectorFFT.h 2 * @brief Contains FFT transform related functions for psVector 3 * 4 * @author Robert DeSonia, MHPCC 5 * 6 * @version $Revision: 1.19 $ $Name: not supported by cvs2svn $ 7 * @date $Date: 2007-01-23 22:47:22 $ 8 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 9 */ 1 /// @file psVectorFFT.h 2 /// @brief Contains FFT transform related functions for psVector 3 /// 4 /// @author Paul Price, IfA 5 /// @author Robert DeSonia, MHPCC 6 /// 7 /// @version $Revision: 1.20 $ $Name: not supported by cvs2svn $ 8 /// @date $Date: 2007-02-08 04:17:58 $ 9 /// Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 10 /// 10 11 11 12 #ifndef PS_VECTOR_FFT_H … … 17 18 /// @{ 18 19 19 /** Specify direction of FFT */ 20 typedef enum { 21 /// psImageFFT/psVectorFFT should perform a forward FFT. 22 PS_FFT_FORWARD = 1, 20 /// Forward FFT of a vector 21 /// 22 /// Applies a forward FFT (exponent -1), with the result returned in both real and imaginary parts. The FFT 23 /// is not normalised (a forward followed by a reverse is the original scaled by the number of elements). The 24 /// FFT takes advantage of the fact that the input is purely real; hence the output size is N/2 + 1 (with 25 /// division rounding down). Only implemented for F32 input. 26 bool psVectorForwardFFT(psVector **real,///< Real part of FFT 27 psVector **imag,///< Imaginary part of FFT 28 const psVector *in ///< Input vector (F32) 29 ); 23 30 24 /// psImageFFT/psVectorFFT should perform a reverse FFT. 25 PS_FFT_REVERSE = 2, 31 /// Backward FFT of a vector 32 /// 33 /// Applies a backward FFT (exponent +1) from the real and imaginary parts with the (purely) real result 34 /// returned. The FFT is not normalised (a forward followed by a reverse is the original scaled by the number 35 /// of elements). The FFT takes advantage of the fact that the output will be purely real; hence the input 36 /// size is N/2 + 1 (with division rounding down); to manage the redundancy (is the original size even or 37 /// odd?), we need the original size (the size of the array that was input to psVectorForwardFFT) to be 38 /// provided. Only implemented for F32 input. 39 bool psVectorBackwardFFT(psVector **out,///< Output vector 40 const psVector *real, ///< Real input (F32) 41 const psVector *imag, ///< Imaginary input (F32) 42 int origN ///< Original number of elements 43 ); 26 44 27 /// psImageFFT/psVectorFFT should perform a reverse FFT with a real result. 28 PS_FFT_REAL_RESULT = 4 29 } psFFTFlags; 45 /// Power spectrum of a vector 46 /// 47 /// Generates the power spectrum of a vector. Only implemented for F32 input. 48 psVector *psVectorPowerSpectrum(psVector *out, ///< Output power spectrum, or NULL 49 const psVector* in ///< Input vector (F32) 50 ); 30 51 31 32 /** Forward and reverse FFT calculations. 33 * 34 * This takes as input the vector of interest (in) and the direction 35 * (direction), which is specified by an enumerated type psFftDirection. 36 * The input vector may be of type psF32 or psC32, the result is always 37 * psC32. If the input vector is psF32, the direction must be forward. 38 * 39 * @return psVector* the FFT transformation result 40 */ 41 psVector* psVectorFFT( 42 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 43 const psVector* in, ///< the vector to apply transform to 44 psFFTFlags direction ///< the direction of the transform 45 ); 46 47 /** extract the real portion of a complex vector 48 * 49 * @return psVector* real portion of the input vector. 50 */ 51 psVector* psVectorReal( 52 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 53 const psVector* in ///< the psVector to extract real portion from 54 ); 55 56 /** extract the imaginary portion of a complex vector 57 * 58 * @return psVector* imaginary portion of the input vector. 59 */ 60 psVector* psVectorImaginary( 61 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 62 const psVector* in ///< the psVector to extract imaginary portion from 63 ); 64 65 /** creates a complex vector from separate real and imaginary vectors 66 * 67 * @return psVector* resulting complex vector 68 */ 69 psVector* psVectorComplex( 70 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 71 const psVector* real, ///< the real vector 72 const psVector* imag ///< the imaginary vector 73 ); 74 75 /** computes the complex conjugate of a vector 76 * 77 * @return psVector* the complex conjugate of the 'in' vector 78 */ 79 psVector* psVectorConjugate( 80 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 81 const psVector* in ///< the psVector to compute conjugate of 82 ); 83 84 /** computes the power spectrum of a vector 85 * 86 * @return psVector* the power spectrum of the 'in' vector 87 */ 88 psVector* psVectorPowerSpectrum( 89 psVector* out, ///< a psVector to recycle. If NULL, a new psVector is made. 90 const psVector* in ///< the psVector to power spectrum of 91 ); 52 /// Multiply complex vectors 53 /// 54 /// The input vectors are the real and imaginary parts of each of two vectors. The real and imaginary parts 55 /// of the output vector are returned. Only implemented for F32 input. 56 bool psVectorComplexMultiply(psVector **outReal, ///< Real part of output 57 psVector **outImag, ///< Imaginary part of output 58 const psVector *in1Real, ///< Real part of input 1 59 const psVector *in1Imag, ///< Imaginary part of input 1 60 const psVector *in2Real, ///< Real part of input 2 61 const psVector *in2Imag ///< Imaginary part of input 2 62 ); 92 63 93 64 /// @} -
trunk/psLib/src/imageops/psImageConvolve.c
r11153 r11703 1 /* @file psImageConvolve.c 2 * 3 * @brief Contains FFT transform related functions for psImage. 4 * 5 * @author Robert DeSonia, MHPCC 6 * 7 * @version $Revision: 1.42 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2007-01-19 04:30:33 $ 9 * 10 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 11 */ 1 /// @file psImageConvolve.c 2 /// 3 /// @brief Contains FFT transform related functions for psImage. 4 /// 5 /// @author Robert DeSonia, MHPCC 6 /// @author Paul Price, IfA 7 /// @author Eugene Magnier, IfA 8 /// 9 /// @version $Revision: 1.43 $ $Name: not supported by cvs2svn $ 10 /// @date $Date: 2007-02-08 04:17:58 $ 11 /// 12 /// Copyright 2004-2007 Institute for Astronomy, University of Hawaii 13 /// 12 14 13 15 #ifdef HAVE_CONFIG_H 14 # include "config.h"16 #include "config.h" 15 17 #endif 16 18 17 19 #include <string.h> 18 20 #include <math.h> 19 #include "psImageConvolve.h" 20 #include "psImageFFT.h" 21 #include "psImageStructManip.h" 22 #include "psBinaryOp.h" 21 #include "psAbort.h" 23 22 #include "psMemory.h" 24 23 #include "psLogMsg.h" 25 24 #include "psError.h" 26 25 #include "psAssert.h" 27 28 29 30 #define FOURIER_PADDING 32 /* padding amount in every side of the image for fourier convolution */ 31 32 static void freeKernel(psKernel* ptr); 33 34 psKernel* psKernelAlloc(int xMin, int xMax, int yMin, int yMax) 35 { 36 psKernel* result; 37 psS32 numRows; 38 psS32 numCols; 39 40 // following is explicitly spelled out in the SDRS as a requirement 26 #include "psScalar.h" 27 #include "psBinaryOp.h" 28 #include "psImageFFT.h" 29 #include "psImageStructManip.h" 30 #include "psImagePixelManip.h" 31 32 #include "psImageConvolve.h" 33 34 static void kernelFree(psKernel *kernel) 35 { 36 if (kernel) { 37 psFree(kernel->image); 38 psFree(kernel->p_kernelRows); 39 } 40 return; 41 } 42 43 psKernel *psKernelAlloc(int xMin, int xMax, int yMin, int yMax) 44 { 45 // Check the inputs to make sure max > min; if not, switch. 41 46 if (yMin > yMax) { 42 47 psLogMsg(__func__, PS_LOG_WARN, … … 44 49 yMin, yMax); 45 50 46 psS32temp = yMin;51 int temp = yMin; 47 52 yMin = yMax; 48 53 yMax = temp; … … 55 60 xMin, xMax); 56 61 57 psS32temp = xMin;62 int temp = xMin; 58 63 xMin = xMax; 59 64 xMax = temp; 60 65 } 61 66 62 numRows = yMax - yMin + 1; 63 numCols = xMax - xMin + 1; 64 65 result = psAlloc(sizeof(psKernel)); 66 result->xMin = xMin; 67 result->xMax = xMax; 68 result->yMin = yMin; 69 result->yMax = yMax; 70 result->image = psImageAlloc(numCols,numRows,PS_TYPE_KERNEL); 71 memset((result->image->p_rawDataBuffer),0,numCols*numRows*PSELEMTYPE_SIZEOF(PS_TYPE_KERNEL)); 72 result->p_kernelRows = psAlloc(sizeof(float*)*numRows); 73 74 float** kernelRows = result->p_kernelRows; 75 float** imageRows = result->image->data.PS_TYPE_KERNEL_DATA; 76 for (psS32 i = 0; i < numRows; i++) { 77 kernelRows[i] = imageRows[i] - xMin; 78 } 79 result->kernel = kernelRows - yMin; 80 81 psMemSetDeallocator(result,(psFreeFunc)freeKernel); 82 83 return result; 84 } 85 86 void freeKernel(psKernel* ptr) 87 { 88 if (ptr != NULL) { 89 psFree(ptr->image); 90 psFree(ptr->p_kernelRows); 91 } 92 } 67 int numRows = yMax - yMin + 1; // Number of rows for kernel image 68 int numCols = xMax - xMin + 1; // Number of columns for kernel image 69 70 psKernel *kernel = psAlloc(sizeof(psKernel)); // The kernel, to be returned 71 psMemSetDeallocator(kernel,(psFreeFunc)kernelFree); 72 73 kernel->xMin = xMin; 74 kernel->xMax = xMax; 75 kernel->yMin = yMin; 76 kernel->yMax = yMax; 77 kernel->image = psImageAlloc(numCols, numRows, PS_TYPE_KERNEL); 78 psImageInit(kernel->image, 0.0); 79 80 // Set up indirections, so we can refer to kernel->kernel[-1][-3] for the (-1,-1) element, instead of 81 // kernel->image[kernel->yMax - kernel->yMin + 1][kernel->yMax - kernel->yMin + 3] (yuk!). 82 kernel->p_kernelRows = psAlloc(sizeof(float*)*numRows); 83 for (int i = 0; i < numRows; i++) { 84 kernel->p_kernelRows[i] = kernel->image->data.PS_TYPE_KERNEL_DATA[i] - xMin; 85 } 86 kernel->kernel = kernel->p_kernelRows - yMin; 87 88 return kernel; 89 } 90 93 91 94 92 … … 96 94 { 97 95 PS_ASSERT_PTR(ptr, false); 98 return ( psMemGetDeallocator(ptr) == (psFreeFunc)freeKernel ); 99 } 100 101 102 psKernel* psKernelGenerate(const psVector* tShifts, 103 const psVector* xShifts, 104 const psVector* yShifts, 105 bool relative) 106 { 107 psS32 lastX; 108 psS32 lastY; 109 psS32 lastT; 110 psS32 x; 111 psS32 y; 112 psS32 t; 113 psS32 xMin = 0; 114 psS32 xMax = 0; 115 psS32 yMin = 0; 116 psS32 yMax = 0; 117 psS32 length = 0; 118 float normalizeTime = 1.0; // fraction of total time for each shift clock 119 psKernel* result = NULL; 120 float** kernel = NULL; 121 122 // got non-NULL vectors? 123 if (tShifts == NULL || xShifts == NULL || yShifts == NULL) { 124 psError(PS_ERR_BAD_PARAMETER_NULL, true, 125 _("Specified shift vectors can not be NULL.")); 96 return ( psMemGetDeallocator(ptr) == (psFreeFunc)kernelFree ); 97 } 98 99 100 psKernel *psKernelGenerate(const psVector *tShifts, 101 const psVector *xShifts, 102 const psVector *yShifts, 103 bool tRelative, 104 bool xyRelative) 105 { 106 PS_ASSERT_VECTOR_NON_NULL(tShifts, NULL); 107 PS_ASSERT_VECTOR_NON_NULL(xShifts, NULL); 108 PS_ASSERT_VECTOR_NON_NULL(yShifts, NULL); 109 PS_ASSERT_VECTORS_SIZE_EQUAL(tShifts, xShifts, NULL); 110 PS_ASSERT_VECTORS_SIZE_EQUAL(tShifts, yShifts, NULL); 111 PS_ASSERT_VECTOR_TYPE(tShifts, PS_TYPE_F32, NULL); 112 PS_ASSERT_VECTOR_TYPE(xShifts, PS_TYPE_S32, NULL); 113 PS_ASSERT_VECTOR_TYPE(yShifts, PS_TYPE_S32, NULL); 114 115 // If there are no shifts, the kernel is just a 1 at 0,0 116 long num = tShifts->n; // Number of shifts 117 if (num == 0) { 118 psKernel *kernel = psKernelAlloc(0,0,0,0); 119 kernel->kernel[0][0] = 1; 120 return kernel; 121 } 122 123 // Get dimensions and scaling 124 int xMin, xMax, yMin, yMax; // Range of values for kernel 125 int xLast, yLast; // Last location, for relative shifts 126 xLast = xMin = xMax = xShifts->data.S32[0]; 127 yLast = yMin = yMax = yShifts->data.S32[0]; 128 float tSum = tShifts->data.F32[0]; // Sum of the times 129 for (long i = 1; i < num; i++) { 130 int x = xShifts->data.S32[i]; // x position in kernel 131 int y = yShifts->data.S32[i]; // y position in kernel 132 if (xyRelative) { 133 x += xLast; 134 y += yLast; 135 xLast = x; 136 yLast = y; 137 } 138 if (x < xMin) { 139 xMin = x; 140 } 141 if (x > xMax) { 142 xMax = x; 143 } 144 if (y < yMin) { 145 yMin = y; 146 } 147 if (y > yMax) { 148 yMax = y; 149 } 150 151 if (tRelative) { 152 tSum += tShifts->data.F32[i]; 153 } 154 } 155 156 if (!tRelative) { 157 // Then the total time is simply the final value 158 // NB: We assume the counter starts at zero! 159 tSum = tShifts->data.F32[tShifts->n - 1]; 160 } 161 162 // One more pass through to set the kernel 163 psKernel *kernel = psKernelAlloc(xMin, xMax, yMin, yMax); // The kernel 164 xLast = xShifts->data.S32[0]; 165 yLast = yShifts->data.S32[0]; 166 float tLast = 0.0; // Last value for t 167 for (int i = 0; i < num; i++) { 168 int x = xShifts->data.S32[i]; // x position in kernel 169 int y = yShifts->data.S32[i]; // y position in kernel 170 if (xyRelative) { 171 x += xLast; 172 y += yLast; 173 xLast = x; 174 yLast = y; 175 } 176 float t = tShifts->data.F32[i]; 177 if (tRelative) { 178 t -= tLast; 179 tLast = tShifts->data.F32[i]; 180 } 181 182 kernel->kernel[y][x] += t; 183 } 184 185 // Normalise the kernel by the total time (kernel sum should be unity) 186 psBinaryOp(kernel->image, kernel->image, "*", psScalarAlloc(1.0 / tSum, PS_TYPE_F32)); 187 188 return kernel; 189 } 190 191 psImage *psImageConvolveDirect(psImage *out, 192 const psImage *in, 193 const psKernel *kernel) 194 { 195 PS_ASSERT_IMAGE_NON_NULL(in, NULL); 196 PS_ASSERT_IMAGE_TYPE_F32_OR_F64(in, NULL); 197 PS_ASSERT_PTR_NON_NULL(kernel, NULL); 198 PS_ASSERT_PTR_NON_NULL(kernel->kernel, NULL); 199 200 // Pull out kernel parameters, for convenience 201 int xMin = kernel->xMin; 202 int xMax = kernel->xMax; 203 int yMin = kernel->yMin; 204 int yMax = kernel->yMax; 205 float **kernelData = kernel->kernel; 206 207 int numRows = in->numRows; // Number of rows 208 int numCols = in->numCols; // Number of columns 209 210 #define SPATIAL_CONVOLVE_CASE(TYPE) \ 211 case PS_TYPE_##TYPE: { \ 212 ps##TYPE **inData = in->data.TYPE; \ 213 out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \ 214 for (int row = 0; row < numRows; row++) { \ 215 ps##TYPE *outRow = out->data.TYPE[row]; \ 216 for (int col = 0; col < numCols; col++) { \ 217 ps##TYPE pixel = 0.0; \ 218 for (int kRow = PS_MAX(yMin, -row); kRow <= PS_MIN(yMax, numRows - row - 1); kRow++) { \ 219 for (int kCol = PS_MAX(xMin, -col); kCol <= PS_MIN(xMax, numCols - col - 1); kCol++) { \ 220 pixel += kernelData[kRow][kCol] * inData[row + kRow][col + kCol]; \ 221 } \ 222 } \ 223 outRow[col] = pixel; \ 224 } \ 225 } \ 226 } \ 227 break; 228 229 switch (in->type.type) { 230 SPATIAL_CONVOLVE_CASE(F32); 231 SPATIAL_CONVOLVE_CASE(F64); 232 default: 233 psAbort(PS_FILE_LINE, "Should never get here: bad type that was asserted on previously."); 234 } 235 236 return out; 237 } 238 239 psImage *psImageConvolveFFT(psImage *out, 240 const psImage *in, 241 const psKernel *kernel, 242 float pad) 243 { 244 PS_ASSERT_IMAGE_NON_NULL(in, NULL); 245 PS_ASSERT_IMAGE_TYPE(in, PS_TYPE_F32, NULL); 246 PS_ASSERT_PTR_NON_NULL(kernel, NULL); 247 PS_ASSERT_IMAGE_NON_NULL(kernel->image, NULL); 248 249 // Pull out kernel parameters, for convenience 250 int xMin = kernel->xMin; 251 int xMax = kernel->xMax; 252 int yMin = kernel->yMin; 253 int yMax = kernel->yMax; 254 255 int numRows = in->numRows; // Number of rows in input image 256 int numCols = in->numCols; // Number of columns in input image 257 258 // Need to pad the input image to protect from wrap-around effects 259 if (xMax - xMin > numCols || yMax - yMin > numRows) { 260 // Cannot pad the image if the kernel is larger. 261 psError(PS_ERR_BAD_PARAMETER_SIZE, true, 262 _("Kernel cannot extend further than input image size (%dx%d vs %dx%d)."), 263 xMax, yMax, numCols, numRows); 126 264 return NULL; 127 265 } 128 129 // types match? 130 if (xShifts->type.type != yShifts->type.type || 131 tShifts->type.type != xShifts->type.type) { 132 char* typeXStr; 133 char* typeYStr; 134 char* typeTStr; 135 PS_TYPE_NAME(typeXStr,xShifts->type.type); 136 PS_TYPE_NAME(typeYStr,yShifts->type.type); 137 PS_TYPE_NAME(typeTStr,tShifts->type.type); 138 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 139 _("Input t-, x-, and y-shift vector types (%s/%s/%s) must match."), 140 typeTStr, typeXStr, typeYStr); 266 int paddedCols = numCols + PS_MAX(-xMin, xMax); // Number of columns in padded image 267 int paddedRows = numRows + PS_MAX(-yMin, yMax); // Number of rows in padded image 268 269 // Generate padded image 270 psImage *paddedImage = psImageAlloc(paddedCols,paddedRows,in->type.type); // Padded input image 271 psImageOverlaySection(paddedImage, in, 0, 0, "="); 272 for (int y = 0; y < numRows; y++) { 273 for (int x = numCols; x < paddedCols; x++) { 274 paddedImage->data.F32[y][x] = pad; 275 } 276 } 277 for (int y = numRows; y < paddedRows; y++) { 278 for (int x = 0; x < paddedCols; x++) { 279 paddedImage->data.F32[y][x] = pad; 280 } 281 } 282 283 // Result of FFT 284 psImage *inRealFFT = NULL, *inImagFFT = NULL; 285 if (!psImageForwardFFT(&inRealFFT, &inImagFFT, paddedImage)) { 286 psError(PS_ERR_UNKNOWN, false, _("Failed to fourier transform input image.")); 287 psFree(paddedImage); 141 288 return NULL; 142 289 } 143 144 // sizes match? 145 length = xShifts->n; 146 if (length != yShifts->n || 147 length != tShifts->n) { 148 psError(PS_ERR_BAD_PARAMETER_SIZE, true, 149 "Shift vectors can not be of different sizes."); 290 psFree(paddedImage); 291 292 // Generate padded kernel image 293 psImage *paddedKernel = psImageAlloc(paddedCols, paddedRows, PS_TYPE_F32); 294 psImageInit(paddedKernel, 0.0); 295 for (int y = PS_MIN(-1, yMin); y <= PS_MIN(-1, yMax); y++) { 296 // y is negative 297 for (int x = PS_MIN(-1, xMin); x <= PS_MIN(-1, xMax); x++) { 298 // x is negative 299 paddedKernel->data.F32[paddedRows + y][paddedCols + x] = kernel->kernel[y][x]; 300 } 301 for (int x = PS_MAX(0, xMin); x <= PS_MAX(0, xMax); x++) { 302 // x is positive 303 paddedKernel->data.F32[paddedRows + y][x] = kernel->kernel[y][x]; 304 } 305 } 306 for (int y = PS_MAX(0, yMin); y <= PS_MAX(0, yMax); y++) { 307 // y is positive 308 for (int x = PS_MIN(-1, xMin); x <= PS_MIN(-1, xMax); x++) { 309 // x is negative 310 paddedKernel->data.F32[y][paddedCols + x] = kernel->kernel[y][x]; 311 } 312 for (int x = PS_MAX(0, xMin); x <= PS_MAX(0, xMax); x++) { 313 // x is positive 314 paddedKernel->data.F32[y][x] = kernel->kernel[y][x]; 315 } 316 } 317 318 psImage *kernelRealFFT = NULL, *kernelImagFFT = NULL; 319 if (!psImageForwardFFT(&kernelRealFFT, &kernelImagFFT, paddedKernel)) { 320 psError(PS_ERR_UNKNOWN, false, _("Failed to fourier transform kernel.")); 321 psFree(inRealFFT); 322 psFree(inImagFFT); 323 psFree(paddedKernel); 150 324 return NULL; 151 325 } 152 153 // if no shifts, the kernel is just a 1 at 0,0 154 if (length < 1) { 155 result = psKernelAlloc(0,0,0,0); 156 result->kernel[0][0] = 1; 157 return result; 158 } 159 160 #define KERNEL_GENERATE_CASE(TYPE) \ 161 case PS_TYPE_##TYPE: { \ 162 ps##TYPE *tShiftData = tShifts->data.TYPE; \ 163 ps##TYPE *xShiftData = xShifts->data.TYPE; \ 164 ps##TYPE *yShiftData = yShifts->data.TYPE; \ 165 lastX = xShiftData[length-1]; \ 166 lastY = yShiftData[length-1]; \ 167 lastT = tShiftData[length-1]; \ 168 \ 169 for (int lcv = 0; lcv < length; lcv++) { \ 170 x = lastX - xShiftData[lcv]; \ 171 y = lastY - yShiftData[lcv]; \ 172 \ 173 if (x < xMin) { \ 174 xMin = x; \ 175 } else if (x > xMax) { \ 176 xMax = x; \ 177 } \ 178 if (y < yMin) { \ 179 yMin = y; \ 180 } else if (y > yMax) { \ 181 yMax = y; \ 182 } \ 183 } \ 184 \ 185 normalizeTime = 1.0 / (float)(tShiftData[length-1]); \ 186 result = psKernelAlloc(xMin,xMax,yMin,yMax); \ 187 kernel = result->kernel; \ 188 \ 189 psS32 prevT = 0; \ 190 for (int i = 0; i < length; i++) { \ 191 t = tShiftData[i] - prevT; \ 192 x = lastX - xShiftData[i]; \ 193 y = lastY - yShiftData[i]; \ 194 \ 195 kernel[y][x] += (float)t / (float)lastT; \ 196 prevT = tShiftData[i]; \ 197 } \ 198 break; \ 199 } 200 201 #define RELATIVE_KERNEL_GENERATE_CASE(TYPE) \ 202 case PS_TYPE_##TYPE: { \ 203 ps##TYPE *tShiftData = tShifts->data.TYPE; \ 204 ps##TYPE *xShiftData = xShifts->data.TYPE; \ 205 ps##TYPE *yShiftData = yShifts->data.TYPE; \ 206 \ 207 x = 0; \ 208 y = 0; \ 209 t = 0; \ 210 \ 211 for (int lcv = length-1; lcv >= 0; lcv--) { \ 212 t += tShiftData[lcv]; \ 213 \ 214 if (x < xMin) { \ 215 xMin = x; \ 216 } else if (x > xMax) { \ 217 xMax = x; \ 218 } \ 219 if (y < yMin) { \ 220 yMin = y; \ 221 } else if (y > yMax) { \ 222 yMax = y; \ 223 } \ 224 x -= xShiftData[lcv]; \ 225 y -= yShiftData[lcv]; \ 226 \ 227 } \ 228 result = psKernelAlloc(xMin,xMax,yMin,yMax); \ 229 kernel = result->kernel; \ 230 \ 231 normalizeTime = 1.0 / (float)t; \ 232 x = 0; \ 233 y = 0; \ 234 for (psS32 i = length-1; i >= 0; i--) { \ 235 kernel[y][x] += (float)(tShiftData[i]) * normalizeTime; \ 236 x -= xShiftData[i]; \ 237 y -= yShiftData[i]; \ 238 \ 239 } \ 240 break; \ 241 } 242 243 if (relative) { 244 switch (xShifts->type.type) { 245 RELATIVE_KERNEL_GENERATE_CASE(U8); 246 RELATIVE_KERNEL_GENERATE_CASE(U16); 247 RELATIVE_KERNEL_GENERATE_CASE(U32); 248 RELATIVE_KERNEL_GENERATE_CASE(U64); 249 RELATIVE_KERNEL_GENERATE_CASE(S8); 250 RELATIVE_KERNEL_GENERATE_CASE(S16); 251 RELATIVE_KERNEL_GENERATE_CASE(S32); 252 RELATIVE_KERNEL_GENERATE_CASE(S64); 253 RELATIVE_KERNEL_GENERATE_CASE(F32); 254 RELATIVE_KERNEL_GENERATE_CASE(F64); 255 RELATIVE_KERNEL_GENERATE_CASE(C32); 256 RELATIVE_KERNEL_GENERATE_CASE(C64); 257 258 default: { 259 char* typeStr; 260 PS_TYPE_NAME(typeStr,xShifts->type.type); 261 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 262 _("Specified psImage type, %s, is not supported."), 263 typeStr); 264 } 265 } 266 } else { 267 switch (xShifts->type.type) { 268 KERNEL_GENERATE_CASE(U8); 269 KERNEL_GENERATE_CASE(U16); 270 KERNEL_GENERATE_CASE(U32); 271 KERNEL_GENERATE_CASE(U64); 272 KERNEL_GENERATE_CASE(S8); 273 KERNEL_GENERATE_CASE(S16); 274 KERNEL_GENERATE_CASE(S32); 275 KERNEL_GENERATE_CASE(S64); 276 KERNEL_GENERATE_CASE(F32); 277 KERNEL_GENERATE_CASE(F64); 278 KERNEL_GENERATE_CASE(C32); 279 KERNEL_GENERATE_CASE(C64); 280 281 default: { 282 char* typeStr; 283 PS_TYPE_NAME(typeStr,xShifts->type.type); 284 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 285 _("Specified psImage type, %s, is not supported."), 286 typeStr); 287 } 288 } 289 } 290 291 return result; 292 } 293 294 psImage* psImageConvolve(psImage* out, 295 const psImage* in, 296 const psKernel* kernel, 297 bool direct) 298 { 299 if (in == NULL) { 300 psFree(out); 326 psFree(paddedKernel); 327 328 // Convolution in fourier domain is just a pixel-wise multiplication 329 if (!psImageComplexMultiply(&inRealFFT, &inImagFFT, inRealFFT, inImagFFT, kernelRealFFT, kernelImagFFT)) { 330 psError(PS_ERR_UNKNOWN, false, _("Unable to multiply fourier transformts.")); 331 psFree(inRealFFT); 332 psFree(inImagFFT); 333 psFree(kernelRealFFT); 334 psFree(kernelImagFFT); 301 335 return NULL; 302 336 } 303 304 if (kernel == NULL) { 305 psError(PS_ERR_BAD_PARAMETER_NULL, true, 306 _("Specified psKernel can not be NULL.")); 307 psFree(out); 337 psFree(kernelRealFFT); 338 psFree(kernelImagFFT); 339 340 psImage *paddedConvolved = NULL; // Padded convolved image 341 if (!psImageBackwardFFT(&paddedConvolved, inRealFFT, inImagFFT, paddedCols)) { 342 psError(PS_ERR_UNKNOWN, false, _("Failed to invert fourier transform of convolution image.")); 343 psFree(inRealFFT); 344 psFree(inImagFFT); 308 345 return NULL; 309 346 } 310 psS32 xMin = kernel->xMin; 311 psS32 xMax = kernel->xMax; 312 psS32 yMin = kernel->yMin; 313 psS32 yMax = kernel->yMax; 314 float** kData = kernel->kernel; 315 316 // make the output image to the proper size and type 317 psS32 numRows = in->numRows; 318 psS32 numCols = in->numCols; 319 320 321 322 if (direct) { 323 // spatial convolution 324 325 #define SPATIAL_CONVOLVE_CASE(TYPE) \ 326 case PS_TYPE_##TYPE: { \ 327 ps##TYPE** inData = in->data.TYPE; \ 328 out = psImageRecycle(out, numCols, numRows, PS_TYPE_##TYPE); \ 329 for (psS32 row=0;row<numRows;row++) { \ 330 ps##TYPE* outRow = out->data.TYPE[row]; \ 331 for (psS32 col=0;col<numCols;col++) { \ 332 ps##TYPE pixel = 0.0; \ 333 for (psS32 kRow = yMin; kRow < yMax; kRow++) { \ 334 if (row-kRow >= 0 && row-kRow < numRows) { \ 335 for (psS32 kCol = xMin; kCol < xMax; kCol++) { \ 336 if (col-kCol >= 0 && col-kCol < numCols) { \ 337 pixel += kData[kRow][kCol] * inData[row-kRow][col-kCol]; \ 338 } \ 339 } \ 340 } \ 341 } \ 342 outRow[col] = pixel; \ 343 } \ 344 } \ 345 } \ 346 break; 347 348 switch (in->type.type) { 349 SPATIAL_CONVOLVE_CASE(U8) 350 SPATIAL_CONVOLVE_CASE(U16) 351 SPATIAL_CONVOLVE_CASE(U32) 352 SPATIAL_CONVOLVE_CASE(U64) 353 SPATIAL_CONVOLVE_CASE(S8) 354 SPATIAL_CONVOLVE_CASE(S16) 355 SPATIAL_CONVOLVE_CASE(S32) 356 SPATIAL_CONVOLVE_CASE(S64) 357 SPATIAL_CONVOLVE_CASE(F32) 358 SPATIAL_CONVOLVE_CASE(F64) 359 SPATIAL_CONVOLVE_CASE(C32) 360 SPATIAL_CONVOLVE_CASE(C64) 361 362 default: { 363 char* typeStr; 364 PS_TYPE_NAME(typeStr,in->type.type); 365 psError(PS_ERR_BAD_PARAMETER_TYPE, true, 366 _("Specified psImage type, %s, is not supported."), 367 typeStr); 368 psFree(out); 369 return NULL; 370 371 } 372 } 373 374 375 } else { 376 // fourier convolution 377 psS32 paddedCols = numCols+2*FOURIER_PADDING; 378 psS32 paddedRows = numRows+2*FOURIER_PADDING; 379 380 // check to see if kernel is smaller, otherwise padding it up will fail. 381 psS32 kRows = kernel->image->numRows; 382 psS32 kCols = kernel->image->numCols; 383 if (kRows >= numRows || kCols >= numCols) { 384 psError(PS_ERR_BAD_PARAMETER_SIZE, true, 385 _("Specified psKernel size, %dx%d, can not be larger than input psImage size, %dx%d."), 386 kCols,kRows, 387 numCols, numRows); 388 psFree(out); 389 return NULL; 390 } 391 392 // pad the image 393 psImage* paddedImage = psImageAlloc(paddedCols,paddedRows,in->type.type); 394 psS32 elementSize = PSELEMTYPE_SIZEOF(in->type.type); 395 396 // zero out padded area on top and bottom 397 memset(paddedImage->data.U8[0],0,FOURIER_PADDING*paddedCols*elementSize); 398 memset(paddedImage->data.U8[FOURIER_PADDING+numRows-1],0,FOURIER_PADDING*paddedCols*elementSize); 399 400 // fill in the image-containing rows. 401 psS32 sidePaddingSize = FOURIER_PADDING*elementSize; 402 psS32 imageRowSize = numCols*elementSize; 403 psU8* paddedData = paddedImage->data.U8[FOURIER_PADDING]; 404 for (psS32 row=0;row<numRows;row++) { 405 // zero out padded area on left edge. 406 memset(paddedData,0,sidePaddingSize); 407 paddedData += sidePaddingSize; 408 memcpy(paddedData,in->data.U8[row],imageRowSize); 409 paddedData += imageRowSize; 410 // zero out padded area on right edge. 411 memset(paddedData,0,sidePaddingSize); 412 paddedData += sidePaddingSize; 413 } 414 415 // pad the kernel to the same size of paddedImage 416 psImage* paddedKernel = psImageAlloc(paddedCols,paddedRows,PS_TYPE_KERNEL); 417 memset(paddedKernel->data.U8[0],0,sizeof(float)*numCols*numRows); // zero-out image 418 psS32 yMax = kernel->yMax; 419 psS32 xMax = kernel->xMax; 420 for (psS32 row = kernel->yMin; row <= yMax;row++) { 421 psS32 padRow = row; 422 if (padRow < 0) { 423 padRow += paddedRows; 424 } 425 float* padData = paddedKernel->data.PS_TYPE_KERNEL_DATA[padRow]; 426 float* kernelRow = kernel->kernel[row]; 427 for (psS32 col = kernel->xMin; col <= xMax; col++) { 428 if (col < 0) { 429 padData[col+paddedCols] = kernelRow[col]; 430 } else { 431 padData[col] = kernelRow[col]; 432 } 433 } 434 } 435 436 psImage* kernelFourier = psImageFFT(NULL, paddedKernel, PS_FFT_FORWARD); 437 if (kernelFourier == NULL) { 438 psError(PS_ERR_UNKNOWN, false, 439 _("Failed to perform a fourier transform of kernel.")); 440 psFree(out); 441 return NULL; 442 } 443 444 psImage* inFourier = psImageFFT(NULL, paddedImage, PS_FFT_FORWARD); 445 if (inFourier == NULL) { 446 psError(PS_ERR_UNKNOWN, false, 447 _("Failed to perform a fourier transform of input image.")); 448 psFree(out); 449 return NULL; 450 } 451 452 // convolution in fourier domain is just a pixel-wise multiplication 453 for (int row = 0; row < paddedRows; row++) { 454 psC32* inRow = inFourier->data.C32[row]; 455 psC32* kRow = kernelFourier->data.C32[row]; 456 for (int col = 0; col < paddedCols; col++) { 457 inRow[col] *= kRow[col]; 458 } 459 } 460 461 psImage* complexOut = psImageFFT(NULL, inFourier, 462 PS_FFT_REVERSE); 463 if (complexOut == NULL) { 464 psError(PS_ERR_UNKNOWN, false, 465 _("Failed to perform a fourier transform of input image.")); 466 psFree(out); 467 return NULL; 468 } 469 470 // subset out the padded area now. 471 psImage* complexOutSansPad = psImageSubset(complexOut, 472 psRegionSet(FOURIER_PADDING, FOURIER_PADDING+numCols,FOURIER_PADDING,FOURIER_PADDING+numRows)); 473 474 out = psImageRecycle(out,numCols,numRows,PS_TYPE_F32); 475 float factor = 1.0f/(float)paddedCols/(float)paddedRows; 476 for (psS32 row = 0; row < numRows; row++) { 477 psF32* outRow = out->data.F32[row]; 478 psC32* resultRow = complexOutSansPad->data.C32[row]; 479 for (psS32 col = 0; col < numCols; col++) { 480 outRow[col] = crealf(resultRow[col])*factor; 481 } 482 } 483 484 psFree(complexOut); // frees complexOutSansPad, as it is a child. 485 psFree(kernelFourier); 486 psFree(inFourier); 487 psFree(paddedImage); 488 psFree(paddedKernel); 489 490 } 347 psFree(inRealFFT); 348 psFree(inImagFFT); 349 350 // Trim off the padding, then renormalise (which also does a copy, so there's no parent for the output) 351 psImage *convolved = psImageSubset(paddedConvolved, psRegionSet(0, numCols, 0, numRows)); 352 out = (psImage*)psBinaryOp(out, convolved, "*", 353 psScalarAlloc(1.0 / paddedCols / paddedRows, PS_TYPE_F32)); 354 psFree(convolved); 355 psFree(paddedConvolved); 491 356 492 357 return out; … … 661 526 IMAGESMOOTH_CASE(F64); 662 527 default: { 663 char *typeStr;528 char *typeStr; 664 529 PS_TYPE_NAME(typeStr,image->type.type); 665 530 psError(PS_ERR_BAD_PARAMETER_TYPE, true, -
trunk/psLib/src/imageops/psImageConvolve.h
r11248 r11703 5 5 * @author Robert DeSonia, MHPCC 6 6 * 7 * @version $Revision: 1.1 5$ $Name: not supported by cvs2svn $8 * @date $Date: 2007-0 1-23 22:47:23$7 * @version $Revision: 1.16 $ $Name: not supported by cvs2svn $ 8 * @date $Date: 2007-02-08 04:17:58 $ 9 9 * Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii 10 10 */ … … 20 20 #include "psType.h" 21 21 22 #define PS_TYPE_KERNEL PS_TYPE_F32 / **< the data member to use for kernel image */23 #define PS_TYPE_KERNEL_DATA F32 / **< the data member to use for kernel image */24 #define PS_TYPE_KERNEL_NAME "psF32" / **< the data type for kernel as a string */22 #define PS_TYPE_KERNEL PS_TYPE_F32 ///< the data member to use for kernel image */ 23 #define PS_TYPE_KERNEL_DATA F32 ///< the data member to use for kernel image */ 24 #define PS_TYPE_KERNEL_NAME "psF32" ///< the data type for kernel as a string */ 25 25 26 /** Kernel Type 27 * 28 * A floating-point data type used for storing kernel data. 29 * 30 */ 31 //typedef float psKernelType; 32 33 /** A convolution kernel */ 26 /// A convolution kernel 34 27 typedef struct 35 28 { 36 psImage *image; ///< Kernel data, in the form of an image29 psImage *image; ///< Kernel data, in the form of an image 37 30 int xMin; ///< Most negative x index 38 31 int yMin; ///< Most negative y index 39 32 int xMax; ///< Most positive x index 40 33 int yMax; ///< Most positive y index 41 float **kernel; ///< Pointer to the kernel data42 float **p_kernelRows; ///< Pointer to the rows of the kernel data; not intended for user use.34 float **kernel; ///< Pointer to the kernel data 35 float **p_kernelRows; ///< Pointer to the rows of the kernel data; not intended for user use. 43 36 } 44 37 psKernel; 45 38 46 / **Allocates a convolution kernel of the given range47 * 48 *In order to perform a convolution, we need to define the convolution49 *kernel. We need a more general object than a psImage so that we can50 *incorporate the offset from the (0, 0) pixel to the (0, 0) value of the51 *kernel. It might be convenient to allow both positive and negative52 *indices to convey the positive and negative shifts. One might consider53 *setting the x0 and y0 members of a psImage to the appropriate offsets,54 *but this is not the purpose of these members, and doing so may affect the55 *behavior of other psImage operations.56 * 57 *This construction allows the kernel member to use negative indices, while58 *preserving the location of psMemBlocks relative to allocated memory.59 * 60 *The maximum extent of the kernel shifts shall be defined by the xMin,61 *xMax, yMin and yMax members. Note that xMin and yMin, under normal62 *circumstances, should be negative numbers. That is,63 *myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or64 *more negative than -3 and -2, respectively.65 * 66 *In the event that one of the minimum values is greater than the67 *corresponding maximum value, the function shall generate a warning, and68 *the offending values shall be exchanged.69 * 70 *@return psKernel* A new kernel object71 */72 psKernel *psKernelAlloc(39 /// Allocates a convolution kernel of the given range 40 /// 41 /// In order to perform a convolution, we need to define the convolution 42 /// kernel. We need a more general object than a psImage so that we can 43 /// incorporate the offset from the (0, 0) pixel to the (0, 0) value of the 44 /// kernel. It might be convenient to allow both positive and negative 45 /// indices to convey the positive and negative shifts. One might consider 46 /// setting the x0 and y0 members of a psImage to the appropriate offsets, 47 /// but this is not the purpose of these members, and doing so may affect the 48 /// behavior of other psImage operations. 49 /// 50 /// This construction allows the kernel member to use negative indices, while 51 /// preserving the location of psMemBlocks relative to allocated memory. 52 /// 53 /// The maximum extent of the kernel shifts shall be defined by the xMin, 54 /// xMax, yMin and yMax members. Note that xMin and yMin, under normal 55 /// circumstances, should be negative numbers. That is, 56 /// myKernel->kernel[-3][-2] may be defined if yMin and xMin are equal to or 57 /// more negative than -3 and -2, respectively. 58 /// 59 /// In the event that one of the minimum values is greater than the 60 /// corresponding maximum value, the function shall generate a warning, and 61 /// the offending values shall be exchanged. 62 /// 63 /// @return psKernel* A new kernel object 64 /// 65 psKernel *psKernelAlloc( 73 66 int xMin, ///< Most negative x index 74 67 int xMax, ///< Most positive x index … … 77 70 ); 78 71 79 / **Checks the type of a particular pointer.80 * 81 *Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.82 * 83 *@return bool: True if the pointer matches a psKernel structure, false otherwise.84 */72 /// Checks the type of a particular pointer. 73 /// 74 /// Uses the appropriate deallocation function in psMemBlock to check the ptr datatype. 75 /// 76 /// @return bool: True if the pointer matches a psKernel structure, false otherwise. 77 /// 85 78 bool psMemCheckKernel( 86 79 psPtr ptr ///< the pointer whose type to check … … 88 81 89 82 90 /** Generates a kernel given a list of shift values 91 * 92 * Given a list of values (e.g., shifts made in the course of OT guiding), 93 * psKernelGenerate shall return the appropriate kernel. The vectors xShifts 94 * and yShifts, which are a list of shifts relative to some starting point, 95 * will be supplied by the user. The elements of the vectors should be of an 96 * integer type; otherwise the values shall be truncated to integers. The 97 * output kernel shall be normalized such that the sum over the kernel is 98 * unity. 99 * 100 * If the vectors are not of the same number of elements, then the function 101 * shall generate a warning shall be generated, following which, the longer 102 * vector trimmed to the length of the shorter, and the function shall continue. 103 * 104 * @return psKernel* new Kernel object 105 */ 106 psKernel* psKernelGenerate( 107 const psVector* tShifts, ///< list of time shifts 108 const psVector* xShifts, ///< list of x-axis shifts 109 const psVector* yShifts, ///< list of y-axis shifts 110 bool relative 111 /**< specifies the starting point for the shifts; true=relative to previous shift 112 * false = relative to some other starting point. */ 83 /// Generates a kernel given a list of shift values 84 /// 85 /// Given a list of values (e.g., shifts made in the course of OT guiding), 86 /// psKernelGenerate shall return the appropriate kernel. The vectors xShifts 87 /// and yShifts, which are a list of shifts relative to some starting point, 88 /// will be supplied by the user. The elements of the vectors should be of an 89 /// integer type; otherwise the values shall be truncated to integers. The 90 /// output kernel shall be normalized such that the sum over the kernel is 91 /// unity. 92 /// 93 /// If the vectors are not of the same number of elements, then the function 94 /// shall generate a warning shall be generated, following which, the longer 95 /// vector trimmed to the length of the shorter, and the function shall continue. 96 /// 97 /// @return psKernel* new Kernel object 98 /// 99 psKernel *psKernelGenerate( 100 const psVector *tShifts, ///< list of time shifts (F32) 101 const psVector *xShifts, ///< list of x-axis shifts (S32) 102 const psVector *yShifts, ///< list of y-axis shifts (S32) 103 bool tRelative, ///< Are times relative (durations) or absolute? 104 bool xyRelative ///< Are x,y positions relative (shifts) or absolute? 113 105 ); 114 106 115 /** convolve an image with a kernel 116 * 117 * Given an input image and the convolution kernel, psImageConvolve shall 118 * convolve the input image, in, with the kernel, kernel and return the 119 * convolved image, out. 120 * 121 * Two methods shall be available for the convolution: if direct is true, 122 * then the convolution shall be performed in real space (appropriate for 123 * small kernels); otherwise, the convolution shall be performed using Fast 124 * Fourier Transforms (FFTs; appropriate for larger kernels). The latter 125 * option involves padding the input image, copying the kernel into an image 126 * of the same size as the padded input image, performing an FFT on each, 127 * multiplying the FFTs, and performing an inverse FFT before trimming the 128 * image back to the original size. 129 * 130 * @return psImage* resulting image 131 */ 132 psImage* psImageConvolve( 133 psImage* out, ///< a psImage to recycle. If NULL, a new psImage is made. 134 const psImage* in, ///< the psImage to convolve 135 const psKernel* kernel, ///< kernel to colvolve with 136 bool direct ///< specifies method, true=direct convolution, false=fourier 107 /// Convolve an image with a kernel, using a direct convolution 108 /// 109 /// This is appropriate for small kernels, where there is no time saving to use FFT method. 110 /// 111 /// @return psImage* resulting image 112 /// 113 psImage *psImageConvolveDirect( 114 psImage *out, ///< a psImage to recycle. If NULL, a new psImage is made. 115 const psImage *in, ///< the psImage to convolve 116 const psKernel *kernel ///< kernel to colvolve with 137 117 ); 138 118 139 /** Smooths an image by parts using 1D Gaussian independently in x and y. 140 * 141 * Applies a circularly symmetric Gaussian smoothing first in x and then in y 142 * directions with just a vector. This process is 2N faster than 2D convolutions (in general). 143 * 144 * @return bool TRUE if successful, otherwise FALSE 145 */ 119 /// Convolve an image with a kernel, using the FFT 120 /// 121 /// This is appropriate for larger kernels, where the direct convolution is slow. The input image and kernel 122 /// are suitably padded to avoid wrap-around effects. 123 psImage *psImageConvolveFFT( 124 psImage *out, ///< a psImage to recycle. If NULL, a new psImage is made. 125 const psImage *in, ///< the psImage to convolve 126 const psKernel *kernel, ///< kernel to colvolve with 127 float pad ///< Value to use to pad the input image 128 ); 129 130 /// Smooths an image by parts using 1D Gaussian independently in x and y. 131 /// 132 /// Applies a circularly symmetric Gaussian smoothing first in x and then in y 133 /// directions with just a vector. This process is 2N faster than 2D convolutions (in general). 134 /// 135 /// @return bool TRUE if successful, otherwise FALSE 136 /// 146 137 bool psImageSmooth( 147 138 psImage *image, ///< the image to be smoothed -
trunk/psLib/test/imageops
- Property svn:ignore
-
old new 23 23 tap_psImageShift 24 24 tap_psImageShiftKernel 25 tap_psImageConvolve 26 tap_psImageConvolve2
-
- Property svn:ignore
-
trunk/psLib/test/imageops/.cvsignore
r9926 r11703 23 23 tap_psImageShift 24 24 tap_psImageShiftKernel 25 tap_psImageConvolve 26 tap_psImageConvolve2 -
trunk/psLib/test/imageops/Makefile.am
r11691 r11703 21 21 tap_psImageStructManip \ 22 22 tap_psImageConvolve \ 23 tap_psImageConvolve2 \ 23 24 tap_psImagePixelExtract 24 25
Note:
See TracChangeset
for help on using the changeset viewer.
