/*
commonLinesReg -- Peter Lauren, 2020-08-21
SYNOPSIS
commonLinesReg -i -p -o [-f [-l ]]
DESCRIPTION
Uses common lines algorithm to linearly align two volumes. It does this by finding where orthogonal volumes intersect
in Fourier space. If they are rotationally aligned, they should intersect on the appropriate orthogonal cardinal axes.
The slope of the phase shift, along the lines of intersection, reflect the translational misalignment. The translations,
and rotations, are each determined by solving a linear system. These solutions are completely independent of each other.
-o Output of "3dinfo -orient mydset" in the linux command line. This gives the a, y,z z-axes in terms of the axial, coronal
and sagmital direction.
-f Examine effects of frequency range. The search is over a range of spectral lengths and min. frequencies. "min. length"
is the starting length.
NOTES
1/ The current implementation aligns volumes made from unweaving the interlaced sections of a brick.
2/ Two to three projection axes must be chosen:
- ac: Axial and Coronal
- as: Axial and Sagittal
- cs: Coronal and Sagittal
- acs: Axial, Coronal and Sagittal
*/
#include
#include
#include
#include
#include
#include
#include "mrilib.h"
#include "Generic.h"
// #include "VolumeRegistration.h"
#define UNIX 1
int Cleanup(char *inputFileName, COMPLEX ***TwoDFts, THD_3dim_dataset *din);
int open_input_dset(THD_3dim_dataset ** din, char * fname);
int unweave_sections(THD_3dim_dataset *din, THD_3dim_dataset **dodd, THD_3dim_dataset **deven);
int makeProjection(THD_3dim_dataset *din, THD_3dim_dataset **dout, char projCode, char *orientation);
int float2DImage(THD_3dim_dataset *dset);
int getLargestDimension(THD_3dim_dataset *din);
int zeroPadProjection(THD_3dim_dataset *din, THD_3dim_dataset **dout, int largestDimension, int factor);
int doesFileExist(char * searchPath, char * prefix,char *appendage , char * outputFileName);
int FFT2D(COMPLEX **c,int nx,int ny,int dir);
int FFT(int dir,int m,double *x,double *y);
int Powerof2(int n,int *m,int *twopm);
int get2DFourierTransform(THD_3dim_dataset *din, COMPLEX ***TwoDFts);
int outputFourierSpectrum(COMPLEX **TwoDFts, THD_3dim_dataset *din);
int outputRealAndImaginaryPlanes(COMPLEX **TwoDFts, THD_3dim_dataset *din);
COMPLEX **CheckeredInversion(COMPLEX **input, unsigned int dimension);
ERROR_NUMBER OutputFourierIntersectionMap(COMPLEX ***TwoDFts, int lNumberOfImages, int dimension,
IntRange irFrequencyRange, char *csSearchPath, char *csRootName, float *fpCoplanarAngularShift);
ERROR_NUMBER MakeRadialPhaseSampleArray(ComplexPlane cpFourierTransform, float fIncrementInDegrees,
IntRange lrFrequencyRange, float ***fpppRadialSamples, long *lpNumRadialSamples);
ERROR_NUMBER RotScaleComplexPlaneM(ComplexPlane *cppComplexPlane, Matrix mTransMatrix, BOOL bClip, BYTE bInterpOrder);
ERROR_NUMBER RotScaleComplexPlane(ComplexPlane *cppComplexPlane, FloatVector fvTranslation,
float fClockwiseRotationInDegrees, float fScaleX, float fScaleY, BOOL bClip, BYTE bInterpOrder);
ERROR_NUMBER MakeCardinalRadialPhaseSamples(FloatPlane fpReal, FloatPlane fpImaginary,
short *spCardinalIndices, IntRange lrFrequencyRange, float ***fpppRadialSamples);
ERROR_NUMBER ComparePhaseSamples(float *fpTargetSample, float *fpRefSample,
IntRange lrFrequencyRange, float *fpComparisonMetric);
float ExpectedAngularShift(int iCurrentAxis);
float GetAngularShift(FloatVector fvCoords);
void DetermineUnclippedOutputDimensions(long lInputColumns, long lInputRows,
float fClockwiseRotationInDegrees, FloatVector fvTranslation, float fScaleX, float fScaleY);
void MatVectMult(Matrix mMatrix, float *fpInVector, float *fpOutVector);
ERROR_NUMBER MakeRadialPhaseVectorSample(COMPLEX *cpComplexRadialSample, int iLength, float **fppRadialSample);
ERROR_NUMBER MakePhaseShiftVector(float *fpTargetSample, float *fpRefSample, IntRange lrFrequencyRange,
long *lpVectorLength, float **fppPhaseShiftVector);
void GetSlope(float *fVector, IntRange lrFrequencyRange, float *fpSlope, float *fpIntercept);
float GetMSE(float *fVector, float fSlope, float fPhaseShiftIntercept, IntRange lrFrequencyRange);
ERROR_NUMBER MedianFilterFloatVector(float *fpVector, long lLength);
float Float3x1Median(float *fpInput);
void FreeFloatPlane(FloatPlane Float_Plane);
int Round(float arg);
ERROR_NUMBER ErrorOpeningFile(char *csFileName);
float FloatPythag(float fArg1, float fArg2);
double Pythag(double dArg1, double dArg2);
double Interp_BL(float **image, float x_coord, float y_coord);
double Interp_BQ(float **image, float x_coord, float y_coord);
double Interp_BC(float **image, float x_coord, float y_coord);
long DRound(double arg);
ERROR_NUMBER Complex2Float(ComplexPlane cpInputPlane, FloatPlane *fppRealOut, FloatPlane *fppImagOut);
FloatPlane MakeFloatPlane(long iRows, long iColumns, float **Data);
char * RootName(char * csFullPathName);
ERROR_NUMBER GetDirectory(char *csInputFileName, char *csDirectory);
ERROR_NUMBER shortToFloat(THD_3dim_dataset **din);
ERROR_NUMBER analyzeFrequencyRange(COMPLEX ***TwoDFts, int numberOfImages,
int paddedDimension, char *searchPath, char *prefix, int minLength, int startingTargetIndex);
ERROR_NUMBER getImsePeakAndMean(COMPLEX ***TwoDFts, int dimension, int refIndex, int targetIndex,
IntRange irFrequencyRange, float *maxIMSE, float *meanIMSE);
double dCommonLinesAngleErr;
float fCommonLinesPeak, fCommonLinesPeakDiff;
LongVector lvOutputDimensions;
int main( int argc, char *argv[] ) {
ERROR_NUMBER enErrorNumber;
IntRange irFrequencyRange;
float fCoplanarAngularShift;
THD_3dim_dataset * din = NULL, *dodd, *deven;
THD_3dim_dataset* projections[6], *paddedProjections[6];
int i, j, largestDimension, paddingFactor=4, minLength=8;
char *inputFileName=NULL, projectionString[3]={'a','s','\0'};
char orientation[8];
COMPLEX** TwoDFts[6]={NULL, NULL, NULL};
Boolean frequencyRangeEffects = false;
int startingTargetIndex=4;
sprintf(orientation, "ASL");
paddingFactor=2; // DEBUG
for (i=0; i=0; --iSampleIndex) free((*fpppRadialSamples)[iSampleIndex]);
free(*fpppRadialSamples);
(*fpppRadialSamples)=NULL;
return enErrorNumber;
}
// Make real and imaginary planes from Fourier transform
if ((enErrorNumber=Complex2Float(cpRotatedFT, &fpReal, &fpImag))!=ERROR_NONE)
{
FreeComplexPlane(cpRotatedFT);
for (--iSampleIndex; iSampleIndex>=0; --iSampleIndex) free((*fpppRadialSamples)[iSampleIndex]);
free(*fpppRadialSamples);
(*fpppRadialSamples)=NULL;
return enErrorNumber;
}
// Make radial samples from cardinal angles
if ((enErrorNumber=MakeCardinalRadialPhaseSamples(fpReal, fpImag, &(saCardinalIndices[0]),
lrFrequencyRange, fpppRadialSamples))!=ERROR_NONE)
{
FreeComplexPlane(cpRotatedFT);
FreeFloatPlane(fpReal);
FreeFloatPlane(fpImag);
for (--iSampleIndex; iSampleIndex>=0; --iSampleIndex) free((*fpppRadialSamples)[iSampleIndex]);
free ((*fpppRadialSamples));
(*fpppRadialSamples)=NULL;
return enErrorNumber;
}
// Increment angle and cardinal indices
fAngleInDegrees+=fIncrementInDegrees;
for (iQuadIndex=0; iQuadIndex<4; ++iQuadIndex) ++saCardinalIndices[iQuadIndex];
// Cleanup
FreeFloatPlane(fpReal);
FreeFloatPlane(fpImag);
}
// Cleanup
FreeComplexPlane(cpRotatedFT);
return ERROR_NONE;
}
ERROR_NUMBER analyzeFrequencyRange(COMPLEX ***TwoDFts, int numberOfImages,
int paddedDimension, char *searchPath, char *prefix, int minLength, int startingTargetIndex){
ERROR_NUMBER enErrorNumber;
char csAnalysisFileName[512];
IntRange irFrequencyRange;
FILE *outputFile;
int maxMin=paddedDimension/4, maxMax=paddedDimension/2-1;
int minRangeLength=minLength;
// int minRangeLength=250; // DEBUG
float maxIMSE, meanIMSE; // Max and mean inverse mean squared error
struct rusage r_usage; // Track memory usage
for (int lPlaneIndex=startingTargetIndex; lPlaneIndexmaxMax) fprintf(outputFile, "\t0");
else {
irFrequencyRange.iMin = fMin;
irFrequencyRange.iMax = fMin+rangeLength;
if ((enErrorNumber=getImsePeakAndMean(TwoDFts, paddedDimension, 0, lPlaneIndex,
irFrequencyRange, &maxIMSE, &meanIMSE))!=ERROR_NONE){
fclose(outputFile);
return enErrorNumber;
}
fprintf(outputFile, "\t%f", maxIMSE/meanIMSE);
}
}
fprintf(outputFile, "\n");
fprintf(stderr, "\n");
}
fclose(outputFile);
}
return ERROR_NONE;
}
ERROR_NUMBER getImsePeakAndMean(COMPLEX ***TwoDFts, int dimension, int refIndex, int targetIndex, IntRange irFrequencyRange,
float *maxIMSE, float *meanIMSE){
ERROR_NUMBER enErrorNumber;
ComplexPlane cpComplexPlane;
float fIncrementInDegrees=2.0f*(float)(asin(0.5/irFrequencyRange.iMax)/DEGREES2RADIANS);
float **fppRefSamples, **fpTargetSamples, fComparisonMetric;
long lPlaneIndex, lNumTargetSamples, lNumRefSamples, lTargetIndex, lRefIndex;
// Make reference samples
lNumRefSamples=Round(180.0f/fIncrementInDegrees)-1;
cpComplexPlane.iColumns=cpComplexPlane.iRows=dimension;
cpComplexPlane.Data=TwoDFts[refIndex];
if ((enErrorNumber=MakeRadialPhaseSampleArray(cpComplexPlane, fIncrementInDegrees,
irFrequencyRange, &fppRefSamples, &lNumRefSamples))!=ERROR_NONE){
return ERROR_MEMORY_ALLOCATION;
}
// Make target array
lNumTargetSamples=Round(360.0f/fIncrementInDegrees);
cpComplexPlane.iColumns=cpComplexPlane.iRows=dimension;
cpComplexPlane.Data=TwoDFts[targetIndex];
if ((enErrorNumber=MakeRadialPhaseSampleArray(cpComplexPlane, fIncrementInDegrees,
irFrequencyRange, &fpTargetSamples, &lNumTargetSamples))!=ERROR_NONE)
{
for (lRefIndex=0; lRefIndex*maxIMSE) *maxIMSE = fComparisonMetric;
}
}
*meanIMSE /= lNumTargetSamples*lNumRefSamples;
// Cleanup
int numberToFree = lNumRefSamples*4;
for (lRefIndex=0; lRefIndex=0; --i)
{
if (fComparisonMetric<=faPeakValues[i]) break;
sPeakIndex=i;
}
if (sPeakIndex<10)
{
for (j=9; j>sPeakIndex; --j)
{
faPeakValues[j]=faPeakValues[j-1];
fvaPeakCoords[j].x=fvaPeakCoords[j-1].x;
fvaPeakCoords[j].y=fvaPeakCoords[j-1].y;
}
faPeakValues[sPeakIndex]=fComparisonMetric;
fvaPeakCoords[sPeakIndex].x=fIncrementInDegrees*lRefIndex;
fvaPeakCoords[sPeakIndex].y=fIncrementInDegrees*lTargetIndex;
}
// Write graph file entry
fprintf(fpGraphFile, ",%3.2f", fComparisonMetric);
}
// New line in graph file
fprintf(fpGraphFile, "\n");
}
// Close graph file
fclose(fpGraphFile);
// Free target samples
for (lTargetIndex=0; lTargetIndex180) *fpCoplanarAngularShift-=360;
else if (*fpCoplanarAngularShift<-180) *fpCoplanarAngularShift+=360;
// Close analysis file
if (bAnalysisFile) fclose(fpAnalysisFile);
}
// Cleanup
for (lRefIndex=0; lRefIndex180) dCommonLinesAngleErr=360-dCommonLinesAngleErr;
while (dCommonLinesAngleErr<-180) dCommonLinesAngleErr=360+dCommonLinesAngleErr;
*/
fCommonLinesPeak=faPeakValues[0];
for (i=0; i<10; ++i)
{
if (fabs((double)(*fpCoplanarAngularShift-GetAngularShift(fvaPeakCoords[i])))>2.0f)
{
fCommonLinesPeakDiff=faPeakValues[0]-faPeakValues[i];
break;
}
}
return ERROR_NONE;
}
/*********************************************************************/
COMPLEX **CheckeredInversion(COMPLEX **input, unsigned int dimension)
/*********************************************************************/
{
unsigned int i, j;
for (i=0; iimag = 0 - (*(input+i)+j)->imag;
(*(input+i)+j)->real = 0 - (*(input+i)+j)->real;
}
}
for (i=1; iimag = 0 - (*(input+i)+j)->imag;
(*(input+i)+j)->real = 0 - (*(input+i)+j)->real;
}
}
return(input);
}
int outputRealAndImaginaryPlanes(COMPLEX **TwoDFts, THD_3dim_dataset *din){
int x, y, inc;
char *prefix=DSET_PREFIX(din);
char *searchPath=DSET_DIRNAME(din);
char *outputFileName;
float *outData;
char appendageR[] = "Real", appendageI[] = "Imaginary";
COMPLEX *row;
// Determine input dimensions
int ny = DSET_NY(din);
int nx = DSET_NX(din);
// Make output array
if (!(outData=(float*)malloc(nx*ny*sizeof(float)))) return 0;
// Allocate memory to output name buffer
if (!(outputFileName=(char *)malloc(strlen(searchPath)+strlen(prefix)+64))){
free(outData);
return 0;
}
// Fill output data with real data
for (y=inc=0; y=0; --y) free((*TwoDFts)[y]);
free((*TwoDFts));
return 0;
}
// Fill real components with spatial image data
inputImage = DSET_ARRAY(din, 0);
for (y=inc=0; y=0; --y) free(centeredFT[y]);
free(centeredFT);
for (y=0; yd_name,outputFileName)){
outputFileExists=1;
break;
}
}
closedir(d);
}
return outputFileExists;
}
int float2DImage(THD_3dim_dataset *dset){
char *prefix=DSET_PREFIX(dset);
char *searchPath=DSET_DIRNAME(dset);
char *outputFileName;
char appendage[8];
// Subtract mean of perimeter from entire image to avoid spurious edge effects
// in Fourier transform
float mean=0;
int count=0, x, y;
// Get input pixel data
float *indata = DSET_ARRAY(dset, 0);
// Determine input dimensions
int ny = DSET_NY(dset);
int nx = DSET_NX(dset);
int lastY = ny-1;
int lastX = nx-1;
// Get mean value around perimeter
int offset=lastY*nx;
for (x=0; xny)? ((nx>nz)? nx : nz) : ((ny>nz)? ny : nz);
}
int makeProjection(THD_3dim_dataset *din, THD_3dim_dataset **dout, char projCode, char *orientation){
int nz, ny, nx, inInc, outInc, rows, cols, x;
char *prefix=DSET_PREFIX(din);
char *searchPath=DSET_DIRNAME(din);
char *outputCode=(projCode=='a')? "Axial" : ((projCode=='c')? "Coronal" : "Sagittal");
char *outputFileName;
int outputFileExists=0, outPixelCount, start;
THD_ivec3 iv_nxyz;
float *outData=NULL;
// Determine input dimensions
nz = DSET_NZ(din);
ny = DSET_NY(din);
nx = DSET_NX(din);
// Get input voxel data
float *indata = DSET_ARRAY(din, 0);
// Determine axis
switch (tolower(projCode)){
case 'a':
if (orientation[0]=='S' || orientation[0]=='I') projCode='x';
else if (orientation[1]=='S' || orientation[1]=='I') projCode='y';
else if (orientation[2]=='S' || orientation[2]=='I') projCode='z';
else return ERROR_SEARCH;
break;
case 'c':
if (orientation[0]=='A' || orientation[0]=='P') projCode='x';
else if (orientation[1]=='A' || orientation[1]=='P') projCode='y';
else if (orientation[2]=='A' || orientation[2]=='P') projCode='z';
else return ERROR_SEARCH;
break;
case 's':
if (orientation[0]=='L' || orientation[0]=='R') projCode='x';
else if (orientation[1]=='L' || orientation[1]=='R') projCode='y';
else if (orientation[2]=='L' || orientation[2]=='R') projCode='z';
else return ERROR_SEARCH;
break;
}
// Apply required projection
switch (projCode){
case 'z': // Axial projection
// Determine output dimensions
rows=ny;
cols=nx;
// Allocate memory to output voxel data
outPixelCount=rows*cols;
if (!(outData=(float *)calloc(outPixelCount,sizeof(float)))) return 0;
// Fill output data
start=0;
for (inInc=0, outInc=0; outInc=0; --outInc){
for (x=0; xd_name,outputFileName)){
outputFileExists=1;
}
}
closedir(d);
}
/* Output 2D projection volume */
*dout = EDIT_empty_copy(din);
LOAD_IVEC3( iv_nxyz , cols , rows , 1 ) ;
sprintf(outputFileName,"%s%s%s",searchPath,prefix,outputCode);
EDIT_dset_items( *dout ,
ADN_prefix, outputFileName,
ADN_nxyz , iv_nxyz ,
ADN_none ) ;
EDIT_substitute_brick(*dout, 0, MRI_float, outData);
if( !outputFileExists ) { // If even file does not already exist
DSET_write(*dout);
}
// Cleanup (Don't free outData)
free(outputFileName);
return 1;
}
ERROR_NUMBER shortToFloat(THD_3dim_dataset **din){
float *outputBuffer;
short *indata;
int i;
// Determine input dimensions
int nz = DSET_NZ(*din);
int ny = DSET_NY(*din);
int nx = DSET_NX(*din);
int numVoxels=nx*ny*nz;
// Memory allocation
if (!(outputBuffer=(float *)malloc(numVoxels*sizeof(float)))){
return ERROR_MEMORY_ALLOCATION;
}
// Fill input data
indata = DSET_ARRAY(*din, 0);
// Convert short int data to floating point
for (i=0; id_name,outputFileName)){
bEvenFileExists=1;
} else {
sprintf(outputFileName,"%sOdd",prefix);
if (strstr(dir->d_name,outputFileName)){
bOddFileExists=1;
}
}
}
closedir(d);
}
/* Output even volume */
*deven = EDIT_empty_copy(din);
LOAD_IVEC3( iv_nxyz , nx , ny , neven ) ;
sprintf(outputFileName,"%s%sEven",searchPath,prefix);
EDIT_dset_items( *deven ,
ADN_prefix, outputFileName,
ADN_nxyz , iv_nxyz ,
ADN_none ) ;
EDIT_substitute_brick(*deven, 0, MRI_float, evenData);
if( !bEvenFileExists ) { // If even file does not already exist
DSET_write(*deven);
}
*dodd = EDIT_empty_copy(din);
LOAD_IVEC3( iv_nxyz , nx , ny , nodd ) ;
sprintf(outputFileName,"%s%sOdd",searchPath,prefix);
EDIT_dset_items( *dodd ,
ADN_prefix, outputFileName,
ADN_nxyz , iv_nxyz ,
ADN_none ) ;
EDIT_substitute_brick(*dodd, 0, MRI_float, oddData);
if( !bOddFileExists ) { // If odd file does not already exist
DSET_write(*dodd);
}
// Cleanup (Don't free evenData or oddData)
free(outputFileName);
return 1;
}
int open_input_dset(THD_3dim_dataset ** din, char * fname)
{
int errorNumber;
*din = THD_open_dataset(fname);
if( ! *din ) {
fprintf(stderr,"** failed to read input dataset '%s'\n", fname);
return ERROR_READING_FILE;
}
/* refuse to work with anything but float here */
// int brickType=DSET_BRICK_TYPE(*din, 0);
int brickType=DSET_BRICK_TYPE(*din, 0);
if( brickType == MRI_float ) {
/* data is not automatically read in, do it now */
DSET_load(*din);
/*
fprintf(stderr,"** input must be of type float, failing...\n");
return 1;
*/
} else if (brickType==MRI_short){
DSET_load(*din);
if ((errorNumber=shortToFloat(din))!=ERROR_NONE)
return errorNumber;
// TODO: Add code
}
return ERROR_NONE;
}
int Cleanup(char *inputFileName, COMPLEX ***TwoDFts, THD_3dim_dataset *din){
int i, j, ny;
if (inputFileName) free(inputFileName);
for (i=0; i<6; ++i){
if (TwoDFts[i]){
ny = DSET_NY(din);
for (j=0; j x(k) e = forward transform
N / n=0..N-1
---
k=0
Formula: reverse
N-1
---
\ j k 2 pi n / N
X(n) = > x(k) e = forward transform
/ n=0..N-1
---
k=0
*/
int FFT(int dir,int m,double *x,double *y)
{
long nn,i,i1,j,k,i2,l,l1,l2;
double c1,c2,tx,ty,t1,t2,u1,u2,z;
/* Calculate the number of points */
nn = 1;
for (i=0;i> 1;
j = 0;
for (i=0;i>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l=0;l=0; --i) free(buffer.Data[i]);
buffer.Data = NULL;
return(buffer);
}
}
}
return(buffer);
}
/*************************************************/
void FreeComplexPlane(ComplexPlane Complex_Plane)
/*************************************************/
{
int i;
for (i=0; iiColumns, cppComplexPlane->iRows,
fClockwiseRotationInDegrees, fvTranslation, 1.0f/fScaleX, 1.0f/fScaleY);
// Reverse translation since matrix is supposed to give source of data, not destination.
fvTranslation.x = -fvTranslation.x;
fvTranslation.y = -fvTranslation.y;
faaMatrixX[lMatrixIndex++] = fScaleX*(float)dCosTheta;
faaMatrixX[lMatrixIndex++] = -fScaleX*(float)dSinTheta;
faaMatrixX[lMatrixIndex++] = fScaleX*fvTranslation.x;
faaMatrixX[lMatrixIndex++] = fScaleY*(float)dSinTheta;
faaMatrixX[lMatrixIndex++] = fScaleY*(float)dCosTheta;
faaMatrixX[lMatrixIndex++] = fScaleY*fvTranslation.y;
faaMatrixX[lMatrixIndex++] = 0;
faaMatrixX[lMatrixIndex++] = 0;
faaMatrixX[lMatrixIndex++] = 1;
mTransMatrix.fpData=&(faaMatrixX[0]);
mTransMatrix.lRows=mTransMatrix.lColumns=3;
return RotScaleComplexPlaneM(cppComplexPlane, mTransMatrix, bClip, bInterpOrder);
}
ERROR_NUMBER RotScaleComplexPlaneM(ComplexPlane *cppComplexPlane, Matrix mTransMatrix, BOOL bClip, BYTE bInterpOrder)
{
long i, j;
long iMinX=bInterpOrder+1, iMaxX=cppComplexPlane->iColumns-bInterpOrder-1;
long iMinY=bInterpOrder+1, iMaxY=cppComplexPlane->iRows-bInterpOrder-1;
float **fppAmplitudeBuffer, **fppPhaseBuffer, *fpAmplitudeRow, *fpPhaseRow;
float **fppOutputAmplitudeBuffer, **fppOutputPhaseBuffer, *fpOutputAmplitudeRow, *fpOutputPhaseRow;
float fpInputVector[3], fpOutputVector[3];
long lHalfRows=cppComplexPlane->iRows/2, lHalfCols=cppComplexPlane->iColumns/2;
double dReal, dImaginary;
COMPLEX *cpOutputRow;
LongVector lvOriginalDimensions={cppComplexPlane->iColumns, cppComplexPlane->iRows};
// Memory must have been allocated to input plane
if (!(cppComplexPlane->Data)) return ERROR_NULL_PTR;
// If clipping, output dimensions same as those of input
if (bClip)
{
lvOutputDimensions.x=cppComplexPlane->iRows;
lvOutputDimensions.y=cppComplexPlane->iColumns;
}
// Allocate memory to phase and amplitude buffers
if (!(fppAmplitudeBuffer=(float **)malloc(cppComplexPlane->iRows*sizeof(float *))) ||
!(fppPhaseBuffer=(float **)malloc(cppComplexPlane->iRows*sizeof(float *))) ||
!(fppOutputAmplitudeBuffer=(float **)malloc(lvOutputDimensions.y*sizeof(float *))) ||
!(fppOutputPhaseBuffer=(float **)malloc(lvOutputDimensions.y*sizeof(float *))))
{
if (fppAmplitudeBuffer) free(fppAmplitudeBuffer);
if (fppPhaseBuffer) free(fppPhaseBuffer);
if (fppOutputAmplitudeBuffer) free(fppOutputAmplitudeBuffer);
return ERROR_MEMORY_ALLOCATION;
}
for (i=0; iiRows; ++i)
if (!(fppAmplitudeBuffer[i]=(float *)malloc(cppComplexPlane->iColumns*sizeof(float))) ||
!(fppPhaseBuffer[i]=(float *)malloc(cppComplexPlane->iColumns*sizeof(float))))
{
if (fppAmplitudeBuffer[i]) free(fppAmplitudeBuffer[i]);
for (--i; i>=0; --i)
{
free(fppAmplitudeBuffer[i]);
free(fppPhaseBuffer[i]);
}
free(fppAmplitudeBuffer);
free(fppPhaseBuffer);
free(fppOutputAmplitudeBuffer);
free(fppOutputPhaseBuffer);
return ERROR_MEMORY_ALLOCATION;
}
for (i=0; i=0; --i)
{
free(fppOutputAmplitudeBuffer[i]);
free(fppOutputPhaseBuffer[i]);
}
for (i=0; iiRows; ++i)
{
free(fppAmplitudeBuffer[i]);
free(fppPhaseBuffer[i]);
}
free(fppAmplitudeBuffer);
free(fppPhaseBuffer);
free(fppOutputAmplitudeBuffer);
free(fppOutputPhaseBuffer);
return ERROR_MEMORY_ALLOCATION;
}
// Load phase and amplitude buffers
for (i=0; iiRows; ++i)
{
fpAmplitudeRow=fppAmplitudeBuffer[i];
fpPhaseRow=fppPhaseBuffer[i];
for (j=0; jiColumns; ++j)
{
dReal=cppComplexPlane->Data[i][j].real;
dImaginary=cppComplexPlane->Data[i][j].imag;
fpAmplitudeRow[j] = (float)Pythag(dReal, dImaginary);
fpPhaseRow[j] = (float)((dImaginary==0)? 0 : ((dReal==0.0)? ((dImaginary>0)? MATH_PI_2 : -MATH_PI_2 ) : atan((dImaginary/dReal))));
if (dReal<0)
{
fpPhaseRow[j]+=(float)MATH_PI;
if (fpPhaseRow[j]>(float)CIRCLE) fpPhaseRow[j]-=(float)CIRCLE;
else if (fpPhaseRow[j]<0) fpPhaseRow[j]+=(float)CIRCLE;
}
}
}
if (bClip)
{
fpInputVector[2] = 1;
for (i=0; iiRows; ++i)
{
fpOutputAmplitudeRow=fppOutputAmplitudeBuffer[i];
fpOutputPhaseRow=fppOutputPhaseBuffer[i];
fpInputVector[1] = (float)(i-lHalfRows); // Rotation around centre of image
for (j=0; jiColumns; ++j)
{
fpInputVector[0] = (float)(j-lHalfCols); // Rotation around centre of image
// Determine transformation from point on output image to corresponding point on input image
MatVectMult(mTransMatrix, &(fpInputVector[0]), &(fpOutputVector[0]));
// Correct for offset from UL corner to centre
fpOutputVector[0] += lHalfCols;
fpOutputVector[1] += lHalfRows;
// Find corresponding value if input image point within input image
if (bInterpOrder>NEAREST_NEIGHBOR_INTERPOLATION && fpOutputVector[0]>iMinX && fpOutputVector[0]iMinY && fpOutputVector[1]=0 && iNearestNeighbor.xiColumns &&
iNearestNeighbor.y>=0 && iNearestNeighbor.yiRows)
{
fpOutputAmplitudeRow[j] = (fppAmplitudeBuffer[iNearestNeighbor.y][iNearestNeighbor.x]);
fpOutputPhaseRow[j] = (fppPhaseBuffer[iNearestNeighbor.y][iNearestNeighbor.x]);
}
else
fpOutputAmplitudeRow[j] = fpOutputPhaseRow[j] = 0;
}
}
}
}
else
{
LongRange lrXRange, lrYRange;
long lLargeX, lLargeY, lXOffset, lYOffset, x, y;
// Ensure output dimensions have been determined
if (lvOutputDimensions.x==-1) return ERROR_IMAGE_DIMENSIONS;
// Determine dimensions of large rectangle that can just hold both the input and output images
lLargeX=fmax(lvOutputDimensions.x, cppComplexPlane->iColumns);
lLargeY=fmax(lvOutputDimensions.y, cppComplexPlane->iRows);
lHalfCols=lLargeX/2;
lHalfRows=lLargeY/2;
// Determine output range relative to large rectangle
if (lvOutputDimensions.xiColumns)
{
lrXRange.lMin=0;
lrXRange.lMax=lvOutputDimensions.x;
lXOffset=-lLargeX/2;
}
else
{
lrXRange.lMin=fmax(0, (lLargeX-lvOutputDimensions.x)/2);
lrXRange.lMax=fmin(lvOutputDimensions.x, lrXRange.lMin+lvOutputDimensions.x);
lXOffset=(lLargeX-cppComplexPlane->iColumns)/2;
}
if (lvOutputDimensions.yiRows)
{
lrYRange.lMin=0;
lrYRange.lMax=lvOutputDimensions.y;
lYOffset=-lLargeY/2;
}
else
{
lrYRange.lMin=fmax(0, (lLargeY-lvOutputDimensions.y)/2);
lrYRange.lMax=fmin(lvOutputDimensions.y, lrYRange.lMin+lvOutputDimensions.y);
lYOffset=(lLargeY-cppComplexPlane->iRows)/2;
}
// Fill output plane
for (i=lrYRange.lMin, y=0; i=iMaxX ||
fpOutputVector[1]<=iMinY || fpOutputVector[1]>=iMaxY)
{
FixedPtVector iNearestNeighbor={Round(fpOutputVector[0]), Round(fpOutputVector[1])};
if (iNearestNeighbor.x>=0 && iNearestNeighbor.xiColumns &&
iNearestNeighbor.y>=0 && iNearestNeighbor.yiRows)
{
fpOutputAmplitudeRow[j] = (fppAmplitudeBuffer[iNearestNeighbor.y][iNearestNeighbor.x]);
fpOutputPhaseRow[j] = (fppPhaseBuffer[iNearestNeighbor.y][iNearestNeighbor.x]);
}
else
{
if (iNearestNeighbor.x<0) ++(iNearestNeighbor.x);
else if (iNearestNeighbor.x>=cppComplexPlane->iColumns) --(iNearestNeighbor.x);
if (iNearestNeighbor.y<0) ++(iNearestNeighbor.y);
else if (iNearestNeighbor.y>=cppComplexPlane->iRows) --(iNearestNeighbor.y);
if (iNearestNeighbor.x>=0 && iNearestNeighbor.xiColumns &&
iNearestNeighbor.y>=0 && iNearestNeighbor.yiRows)
{
fpOutputAmplitudeRow[j] = fppAmplitudeBuffer[iNearestNeighbor.y][iNearestNeighbor.x];
fpOutputPhaseRow[j] = fppPhaseBuffer[iNearestNeighbor.y][iNearestNeighbor.x];
}
else fpOutputAmplitudeRow[j] = fpOutputPhaseRow[j] = 0;
}
}
else
{
fpOutputAmplitudeRow[j] = (
(bInterpOrder==BILINEAR_INTERPOLATION)? (float)Interp_BL(fppAmplitudeBuffer, fpOutputVector[0], fpOutputVector[1]) :
((bInterpOrder==BIQUADRATIC_INTERPOLATION)? (float)Interp_BQ(fppAmplitudeBuffer, fpOutputVector[0], fpOutputVector[1]) :
(float)Interp_BC(fppAmplitudeBuffer, fpOutputVector[0], fpOutputVector[1])));
fpOutputPhaseRow[j] = (
(bInterpOrder==BILINEAR_INTERPOLATION)? (float)Interp_BL(fppPhaseBuffer, fpOutputVector[0], fpOutputVector[1]) :
((bInterpOrder==BIQUADRATIC_INTERPOLATION)? (float)Interp_BQ(fppPhaseBuffer, fpOutputVector[0], fpOutputVector[1]) :
(float)Interp_BC(fppPhaseBuffer, fpOutputVector[0], fpOutputVector[1])));
}
++x;
}
++y;
}
// Free input plane
FreeComplexPlane(*cppComplexPlane);
// Make new plane with new dimensions
*cppComplexPlane=MakeComplexPlane(lvOutputDimensions.y, lvOutputDimensions.x, NULL);
if (!(cppComplexPlane->Data))
{
for (i=0; iData[i];
for (j=0; j=0; --iQuadIndex) free(cpppComplexRadialSample[iQuadIndex]);
free(cpppComplexRadialSample);
return ERROR_MEMORY_ALLOCATION;
}
// Make complex radial samples
for (iRadiusIndex=0; iRadiusIndex180) fBuffer-=360;
else if (fBuffer<-180) fBuffer+=360;
return fBuffer;
}
void DetermineUnclippedOutputDimensions(long lInputColumns, long lInputRows,
float fClockwiseRotationInDegrees, FloatVector fvTranslation, float fScaleX, float fScaleY)
{
double dRotationInRadians;
double dCosTheta, dSinTheta;
// Confine rotation angle into (-180,180] degree range.
if (fClockwiseRotationInDegrees>180) fClockwiseRotationInDegrees-=360;
else if (fClockwiseRotationInDegrees<=-180) fClockwiseRotationInDegrees+=360;
dRotationInRadians=DEGREES2RADIANS*fClockwiseRotationInDegrees;
// Determine new dimensions on the basis of rotation
if (fClockwiseRotationInDegrees<-90) // Rotation in (-180,-90) degrees
{
dRotationInRadians=-MATH_PI_2-dRotationInRadians;
dCosTheta=cos(dRotationInRadians);
dSinTheta=sin(dRotationInRadians);
lvOutputDimensions.x=DRound((dSinTheta*lInputColumns)+(dCosTheta*lInputRows)+0.4999999999999);
lvOutputDimensions.y=DRound((dCosTheta*lInputColumns)+(dSinTheta*lInputRows)+0.4999999999999);
}
else if (fClockwiseRotationInDegrees<0) // Rotation in [-90,0) degrees
{
dCosTheta=cos(-dRotationInRadians);
dSinTheta=sin(-dRotationInRadians);
lvOutputDimensions.x=DRound((dCosTheta*lInputColumns)+(dSinTheta*lInputRows)+0.4999999999999);
lvOutputDimensions.y=DRound((dSinTheta*lInputColumns)+(dCosTheta*lInputRows)+0.4999999999999);
}
else if (fClockwiseRotationInDegrees<=90) // Rotation in [0,90] degrees
{
dCosTheta=cos(dRotationInRadians);
dSinTheta=sin(dRotationInRadians);
lvOutputDimensions.x=DRound((dCosTheta*lInputColumns)+(dSinTheta*lInputRows)+0.4999999999999);
lvOutputDimensions.y=DRound((dSinTheta*lInputColumns)+(dCosTheta*lInputRows)+0.4999999999999);
}
else // Rotation in (90,180] degrees
{
dRotationInRadians-=MATH_PI_2;
dCosTheta=cos(dRotationInRadians);
dSinTheta=sin(dRotationInRadians);
lvOutputDimensions.x=DRound((dSinTheta*lInputColumns)+(dCosTheta*lInputRows)+0.4999999999999);
lvOutputDimensions.y=DRound((dCosTheta*lInputColumns)+(dSinTheta*lInputRows)+0.4999999999999);
}
// Determine new dimensions on the basis of scaling
lvOutputDimensions.x=Round(fScaleX*lvOutputDimensions.x);
lvOutputDimensions.y=Round(fScaleY*lvOutputDimensions.y);;
// Determine new dimensions on the basis of translation
lvOutputDimensions.x+=DRound(fabs(fvTranslation.x));
lvOutputDimensions.y+=DRound(fabs(fvTranslation.y));
}
void MatVectMult(Matrix mMatrix, float *fpInVector, float *fpOutVector)
{
long i, j;
long lMatrixIndex;
for (i=0; i0.0)?
((cpComplexRadialSample[i].imag>0.0)? 1 : 4) : ((cpComplexRadialSample[i].imag>0.0)? 2 : 3);
fTo=(cpComplexRadialSample[i].real==0)? ((cpComplexRadialSample[i].real>0)? (float)MATH_PI_2 : -(float)MATH_PI_2) :
(float)atan((double)(cpComplexRadialSample[i].imag/cpComplexRadialSample[i].real));
fFrom=fPrevious-fOffset;
if (i>1) switch (iQuad[1])
{
case 1: if (fFrom< -MATH_PI) fTo -= (float)CIRCLE;
if (iQuad[0]==4&&fFrom>MATH_PI)
{
fOffset+=(float)CIRCLE;
}
break;
case 4: if (fFrom> MATH_PI) fTo += (float)CIRCLE;
if (iQuad[0]==1&&fFrom< -(float)MATH_PI)
{
fOffset-=(float)CIRCLE;
}
break;
case 2: if (fFrom> 0) fTo += (float)MATH_PI;
else fTo -= (float)MATH_PI;
break;
case 3: if (fFrom> 0) fTo += (float)MATH_PI;
else fTo -= (float)MATH_PI;
break;
}
fPhase = fOffset+fTo;
// Correct for phase reversal
fChange=fPhase-fPrevious;
if (fChange>dChangeThresh &&
((FloatPythag((cpComplexRadialSample[i].imag), (cpComplexRadialSample[i].real))1400)
{
fprintf(stderr, "fPhase=%f, i=%d, cpComplexRadialSample[i].fReal=%f\n",
fPhase, i, cpComplexRadialSample[i].real);
fprintf(stderr, "cpComplexRadialSample[i].fImaginary=%f, cpComplexRadialSample[i-1].fReal=%f\n",
cpComplexRadialSample[i].imag, cpComplexRadialSample[i-1].real);
fprintf(stderr, "cpComplexRadialSample[i-1].fImaginary=%f\n", cpComplexRadialSample[i-1].imag);
fscanf(stdin, "%c", &cDummy);
fprintf(stderr, "\n");
}
}
#endif
(*fppRadialSample)[i]=fPhase;
/**/
}
/**/
return ERROR_NONE;
}
ERROR_NUMBER MakePhaseShiftVector(float *fpTargetSample, float *fpRefSample, IntRange lrFrequencyRange,
long *lpVectorLength, float **fppPhaseShiftVector)
{
ERROR_NUMBER enErrorNumber;
int iInIndex, iOutIndex;
float *fpShiftPtr;
float fPrevious, fDiffUp, fDiffDown, fDiffHere, fOffset=0, fDiffThresh=0.1f;
// Allocate memory to phase shift vector
*lpVectorLength=lrFrequencyRange.iMax+1;
if (!(*fppPhaseShiftVector=(float *)malloc(*lpVectorLength*sizeof(float)))) return ERROR_MEMORY_ALLOCATION;
// Fill vector.
fpShiftPtr=&((*fppPhaseShiftVector)[0]);
for (iOutIndex=0, iInIndex=0; iOutIndex<*lpVectorLength; ++iOutIndex)
{
*fpShiftPtr=fpTargetSample[iInIndex]-fpRefSample[iInIndex]+fOffset;
if (iOutIndex)
{
fDiffUp=(*fpShiftPtr+(float)CIRCLE)-fPrevious;
fDiffDown=fPrevious-*fpShiftPtr+(float)CIRCLE;
fDiffHere=(float)fabs(fPrevious-*fpShiftPtr);
do
{
if (fDiffHere>fDiffDown || fDiffHere>fDiffUp)
{
if (fDiffUpfDiffDown || fDiffHere>fDiffUp);
}
fPrevious=(iOutIndex<3)? *fpShiftPtr : (2.0f*fPrevious + *fpShiftPtr)/3.0f;
++fpShiftPtr;
++iInIndex;
}
// Median filter phase shift vector
if ((enErrorNumber=MedianFilterFloatVector(*fppPhaseShiftVector, *lpVectorLength))!=ERROR_NONE)
{
free(*fppPhaseShiftVector);
return enErrorNumber;
}
return ERROR_NONE;
}
void GetSlope(float *fVector, IntRange lrFrequencyRange, float *fpSlope, float *fpIntercept)
{
float fXY, fXSquared, *fpShiftPtr;
float fXMean=0, fYMean=0, fXMinusXMean, fYMinusYMean;
int i, iCount=lrFrequencyRange.iMax-lrFrequencyRange.iMin;
// Determine mean X and MeanY
fpShiftPtr=fVector+lrFrequencyRange.iMin;
for (i=lrFrequencyRange.iMin; ifpInput[1])? ((fpInput[1]>fpInput[2])? fpInput[1] : (fpInput[0]>fpInput[2])? fpInput[2] : fpInput[0]) :
((fpInput[1]Data)) return ERROR_MEMORY_ALLOCATION;
*fppImagOut=MakeFloatPlane(cpInputPlane.iRows, cpInputPlane.iColumns, NULL);
if (!(fppImagOut->Data))
{
FreeFloatPlane(*fppRealOut);
fppRealOut->Data=NULL;
fppImagOut->Data=NULL;
return ERROR_MEMORY_ALLOCATION;
}
// Fill output planes with data from input plane
for (iRow=0; iRowData[iRow];
fpImaginaryRowPtr=fppImagOut->Data[iRow];
cpInputRowPtr=cpInputPlane.Data[iRow];
for (iCol=0; iCol=0; --i) free(buffer.Data[i]);
free(buffer.Data);
buffer.Data = NULL;
return(buffer);
}
}
}
return(buffer);
}
/**************************************/
char * RootName(char * csFullPathName)
/**************************************/
{
int iPeriodIndex, iBackslashIndex;
char * FileNameNoExt, * buffer;
int iFullLength=(int)strlen(csFullPathName);
if ((FileNameNoExt=(char *)malloc(iFullLength+1))==NULL) return NULL;
if ((buffer=(char *)malloc(iFullLength+1))==NULL)
{
free(FileNameNoExt);
return NULL;
}
sprintf(FileNameNoExt, "%s", csFullPathName);
if ((iPeriodIndex = (int)(strrchr( csFullPathName, '.' ) - csFullPathName)) > 0)
FileNameNoExt[iPeriodIndex] = '\0';
#if UNIX
if ((iBackslashIndex = strrchr( FileNameNoExt, '/' ) - FileNameNoExt) > 0)
#else
if ((iBackslashIndex = (int)(strrchr( FileNameNoExt, '\\' ) - FileNameNoExt)) > 0)
#endif
sprintf(buffer, "%s", &(FileNameNoExt[iBackslashIndex+1]));
else
sprintf(buffer, "%s", FileNameNoExt);
free(FileNameNoExt);
return (buffer);
}
/*******************************************************************/
ERROR_NUMBER GetDirectory(char *csInputFileName, char *csDirectory)
/*******************************************************************/
{
char *csCurrentDirectory;
long iBackslashIndex;
#if UNIX
csCurrentDirectory;
iBackslashIndex = (long)(strrchr( csInputFileName, '/' ));
#else
char csCurrentDirectory[150];
longlong iBackslashIndex = (longlong)(strrchr( csInputFileName, '\\' ));
#endif
if (iBackslashIndex == 0)
{
#if UNIX
if ((csCurrentDirectory = getcwd(NULL, 150)) == NULL)
{
perror("pwd");
return ERROR_GETTING_DIRECTORY;
}
#else
GetCurrentDirectory( 150, (LPSTR)csCurrentDirectory );
#endif
sprintf(csDirectory, "%s", csCurrentDirectory);
}
else
{
#if UNIX
iBackslashIndex -= (long)csInputFileName;
#else
iBackslashIndex -= (longlong)csInputFileName;
#endif
sprintf(csDirectory, "%s", csInputFileName);
csDirectory[iBackslashIndex] = '\0';
}
return ERROR_NONE;
}