Click here to Skip to main content
Click here to Skip to main content

FFT & DCT Library

By , 25 Mar 2004
 

Introduction

This Library is specifically designed for image processing analysis. You may just use it in your app, but it isn’t optimized yet (if someone interested to optimize my code, please notify me via email). My main purpose is to provide a simple way for people who is interested in FFT & DCT for Digital Image Processing. Mainly for FFT, I have noticed that, many programmers forget the essence of FrequencyDomain Image Processing, that is, we actually cannot change the FFTed image into RGB, and process the RGB (it doesn’t make sense).

Issues with CxImage

This is my critic for CxImage which provides a simple FFT but at the same time forget about few things:

  • The result of CxImage’s FFT is an image (it is supposed to be complex numbers)

  • It is ridiculous to process FFT’s result as an image

  • It doesn’t give any complex data that we can process further

  • The scaling is quite good, but scaling is supposed to be done only when we want to view the result.

  • It changes the image into grayscaled image.

  • The most minus point is, it allows us to reverse transform from an image ! How could this be done after scaling process (read: many truncations happen) has been performed.

How It Works

The library only does two basic functions: FFT (forwardreverse) and DCT (forwardreverse). I’ve seen many algorithm for FFT, and I’ve chosen one of them that I think it is sufficient. The input form is matrices (I use class Cmatrix made by R.Allen) so that you don’t have to use malloc/free or such (It’s better to use new/delete, just like I did). The numbers are all in doubles, this will assure the accuracy.

Only for FFT, the dimension of matrix must be exponent of 2, e.g 256, 512, 1024, etc. You can use many method to compensate this limitation (bilinear, bicubic, nearest nighbour), remember that resampling to a larger dimension yields a better result, but more slower execution time. CxImage class have provide these resample methods perfectly. After processing, you can then resample back the matrix to it’s original image (The difference in using resample or not is not very significant, thus can be ignored for some reasons). The result of the forward FFT is complex numbers, while the result of reverse FFT is real numbers. In reverse FFT, you only need the Real values.

For DFT, I chose the unoptimized method since I don’t know why the optimized version (uses fft) is irreversible. As a result, the DFT doesn’t have dimension requirements and yet very slow. But DFT is often used only for compression and analysis. It’s not commonly used in a process.

Multi Thread for True Color Image

Life is full of colors, so do the image. But the problem is, if we want to transform a true color image, we have to process it 3 times. This will slow the execution time three times. This library tries to minimize this phenomena by using Multi Threading support in MFC. It will process the R, G, and B matrices parallel. The priority is set to normal, but you can change it to suit your need. This trick may not be perfect, but at least it helps.

For DFT, I only made the support for TrueColor DFT for fun. In fact, using this functions is no fun at all (sooo slow). Since DFT is only used for analysis, it is much recommended to gray scale the image before DFT and use the regular DFT. But if you want to make it reversible, there is no choice, you have to use the true color DFT.

Function Lists

//direct Functions
BOOL FFT(CMatrix *ReInput, CMatrix *ImInput, 
  CMatrix *ReOutput, CMatrix *ImOutput, int dir = 1);
BOOL DCT(CMatrix *Source, CMatrix *Destination = NULL, int dir = 1);
           
//true color (uses parallel processing, use with care)
BOOL TRUEFFT(CMatrix *ReInput[3], CMatrix *ImInput[3], 
  CMatrix *ReOutput[3], CMatrix *ImOutput[3],int dir = 1);
BOOL TRUEDCT(CMatrix *Source[3],
  CMatrix *Destination[3],int dir = 1    );

Examples of use

Just include the header (ArisImageRoutines1.h) and link the ArisImageRoutines.lib in your project. The most easy to link is by rightclicking at your project’s name and choose Add Files to Project, and browse for the lib. This uses a DLL, so don’t forget to place the DLL in your project’s environment directory.

(it is assumed that you use CxImage class, if not just modify the SetPixel/GetPixel method)

extern CxImage *image; // assumed that image is in gray scale
image->resample(512,512);

CMatrix ReIn(512,512);
CMatrix ReOut(512,512);
//CMatrix ImIn(512,512); we currently don’t need it
CMatrix ImOut(512,512);

for(int x=0;x<512;x++)
{
  for(int y=0;y<512;y++)
  {
    ReIn.SetElement(x,y, image>GetPixelGray(x,y));
  }
}

ArisImageRoutines fft;

if (!fft.FFT(&ReIn, //input Real
  NULL, //input Imaginary
  &ReOut, //output Real
  &ImOut, //output Imaginary
  1)) return;

//here the ImOut contains the imaginary result, 
//and ReOut contains the Real, respectively.

Tips

If you want to acquire the power of the FFT result and then view it as an image, use sqrt(...) for each element in Real and Imaginary output. Then use this formula:

RE = ReOut.GetElement(x,y);
IM = ImOut.GetElement(x,y);
MAG = sqrt((double)(RE*RE+IM+IM)); 
  //this calculates the magnitudes

BYTE Gray = max(0,min(255,(BYTE)(512*log((double)(1+MAG))))); 
  //this calculates the power 512 is the scale factor

Image>SetPixelGray(x,y,Gray);

Actually, there are many methods to perform scaling on the resulted matrix. But the above scaling is enough if you just want to see what FFT looks like. Commonly, scientists does not the above resulted image directly. But it process the image first so that we can interpret it better. The image result is usually have higher power value on every corners. The most common way is to change the image like this:

Last Words

The codes are quite self explained, so I think that you can learn while trying them.

Future Works

I am currently trying to add a functions that can perform convolution with a given kernel in frequency domain. I am still confused about what padding method should I use to compensate the image dimension which is surely much bigger than the kernel. If some one can help, please notify me.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Aris Adrianto S
Business Analyst HSBC, Citi
Indonesia Indonesia
Multi Platform System Analyst, Application Developer, Database Designer, and Project Manager in a wide variety of Business Applications and Industrial Automations.
 
Experienced for in-depth data analysis, data warehousing, reporting, and actively involve in supporting the business growth.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralQuestion.memberFlying_Doc28-Mar-09 0:57 
i have a question if it's allowed to ask..hehe..actually it's sound like silly question but it's important for me..
The question is:
1. why on DCT the function use a cosine function, if we see on the image and the function there is no connection with angle.
2. why the function using cosine, why not using sine? what is the influence for the compression ratio? Confused | :confused:
GeneralMAG = sqrt((double)(RE*RE+IM+IM));memberchernobyl16-Mar-07 21:13 
it should be MAG = sqrt((double)(RE*RE+IM*IM)); am i right?
QuestionWhy the result is different from the fft() in Matlab??memberKarlzheng9-Nov-06 18:45 
Hello,Aris!Nice to see your article.I try to use you class,and make a test program:
void CFFTDlg::OnButton1()
{
// TODO: Add your control notification handler code here
CFFT1D fft1(4);
// int iInput[4] = {1,2,3,4};
CComplex cplxInput[4] ={1,2,3,4};
// cplxInput[0] = iInput[0];
fft1.SetInputAt(0, 1, 0);
fft1.SetInputAt(1, 2, 0);
fft1.SetInputAt(2, 3, 0);
fft1.SetInputAt(3, 4, 0);
fft1.ForwardTransform();
cplxInput[0] = fft1.GetOutputAt(0);
cplxInput[0] = fft1.GetOutputAt(1);
cplxInput[2] = fft1.GetOutputAt(2);
cplxInput[3] = fft1.GetOutputAt(3);
 
// fft1.SetInputAt(0, cplxInput[0].GetReal() , cplxInput[0].GetImag());
// fft1.SetInputAt(1, cplxInput[1].GetReal(), cplxInput[1].GetImag());
// fft1.SetInputAt(2, cplxInput[2].GetReal() , cplxInput[2].GetImag());
// fft1.SetInputAt(3, cplxInput[3].GetReal() , cplxInput[3].GetImag());
 
fft1.ReverseTransform();
cplxInput[0] = fft1.GetOutputAt(0);
cplxInput[0] = fft1.GetOutputAt(1);
cplxInput[2] = fft1.GetOutputAt(2);
cplxInput[3] = fft1.GetOutputAt(3);

}

 
but the result after fft1.ForwardTransform()is not the same with Matlab,and the other problem:do fft1.ForwardTransform() and then ReverseTransform(),it has not get the same result with the original data.....
 
so my question: 1. is there any wrong in my calling code?
2.my suggestion: can it be changed to output the same result with Matlab?(as I know,the Fouriar transform,take the different coefficience 1/sqrt(n) or 1/n,or 1 may result different result.
 
Thank you!

 
KarlZheng
GeneralUsage; Frequency histogram [modified]memberMarkus Mayer11-Jul-06 8:11 
Hey.
 
In an upcoming project of mine I need to implement a measurement of the overall "sharpness" of an image as a whole or, later, given image regions. Additional (and I suppose: somewhat equal) to that, I'd like to get a measure of the image's fine details so that the application will on some day be able to numerically distinguish between, say, hair, corn fields, stone texture and, whatever, steel plates. (Yeah, I believe it's all numbers)
 
From what I've read about the (or: a) frequency domain of an image given as 2D data something like a frequency histogram could be what I need. Unfortunately, I haven't touched fourier, wavelet or cosine transforms yet, so I don't have any idea how to move on. It's all a bit of mathematical mumbo-jumbo to me...
 
Is a FT/CT what I need to accomplish these measurements? What would be your general approach to that?
 
Thanks in advance!
Markus
 

BTW.: The frequency domain "image" seems to be equally mirrored around the center of the image (or it's corners, whatever display method you use). This would lead to a severe reduction of necessary data - Is that so?
 
-- modified at 14:14 Tuesday 11th July, 2006
GeneralBugmemberVertical_Horizon11-Feb-06 4:34 
I think there's bug in the library and especially in relative large images. No large image was able to be DCT-ed because there's an access violation..
The Debug points to the code where column and row transformation of the matrix takes place..
GeneralRe: BugmemberVertical_Horizon11-Feb-06 5:00 
I got it.. Must be power of 2. Sorry..
QuestionHow to link my picture?memberuumeme1-Dec-05 4:53 
but why do i calculate MAG result to be 0? How to link my picture?
 
ArisImageRoutines fft;
 
if (!fft.FFT(&ReIn, //input Real
NULL, //input Imaginary
&ReOut, //output Real
&ImOut, //output Imaginary
1)) return;
 
fft.FFT(&ReIn, NULL, &ReOut, &ImOut, 1);
 
RE = ReOut.GetElement(x,y);
IM = ImOut.GetElement(x,y);
MAG = sqrt((double)(RE*RE+IM*IM));
//this calculates the magnitudes
 
prediff.Format("%.7f - ",RE);
m_sample.AddString(prediff);
GeneralconfusionmemberTassadaqHussain24-Oct-05 15:30 
i m confused about fft,dct
it convert signal form time domain to space domain
how is it work i mean what it do with pixel's value
 
could u explain it
what effect it will make on 3x3 gray scale image if it have values
 
120 100 101
201 200 54
0 100 125

and what is our end result
 
plz help me about this
 
Communication Engineer
GeneralRe: confusionmemberChristian Graus24-Oct-05 16:12 
Why not read the article, create the bitmap in question, pass it through, and view the result ?

 
Christian Graus - Microsoft MVP - C++
QuestionCan i be taught how to use this library?memberredishung12-Nov-04 21:34 
After i have complied the library, it results in a DLL file.
How to use that file to compute the transform?
 
By the way, what is the minmium size of the picture? Is that 256x256?
 
redishung, a student doing a project on FFT
AnswerRe: Can i be taught how to use this library?memberlabrosgiamp18-Mar-05 1:47 
If you find out please let me know too..
 
I'm a student who has a project in image watermarking..
Generalbinary filesmembermiklosiklaci21-Apr-04 23:06 
I wasn't able to use the binary files, I thought you may help me. I added the lib file to the project then I added and included the header file and placed the dll file in the project directory. When I compile my project it asks for the Matrix.h file which you included in the ArisImageRoutines1.h. So I copied the files Matrix. h and Matrix.cpp (from the source files you gave us) to the project directory and added it to the project. Now it gives me 32 error messages:
 
Matrix.cpp
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(104) : error C2027: use of undefined type 'tagVARIANT'
c:\program files\microsoft visual studio\vc98\mfc\include\afxwin.h(1290) : see declaration of 'tagVARIANT'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(104) : error C2228: left of '.vt' must have class/struct/union type
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(104) : error C2065: 'VT_ARRAY' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(106) : error C2027: use of undefined type 'tagVARIANT'
c:\program files\microsoft visual studio\vc98\mfc\include\afxwin.h(1290) : see declaration of 'tagVARIANT'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(106) : error C2228: left of '.vt' must have class/struct/union type
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(106) : error C2065: 'VT_R8' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(109) : error C2065: 'SAFEARRAY' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(109) : error C2065: 'psa' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(109) : error C2027: use of undefined type 'tagVARIANT'
c:\program files\microsoft visual studio\vc98\mfc\include\afxwin.h(1290) : see declaration of 'tagVARIANT'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(109) : error C2228: left of '.parray' must have class/struct/union type
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(110) : error C2227: left of '->cDims' must point to class/struct/union
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(118) : error C2065: 'SafeArrayGetLBound' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(121) : error C2065: 'SafeArrayGetUBound' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(138) : error C2065: 'SafeArrayAccessData' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(147) : error C2065: 'SafeArrayUnaccessData' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(943) : error C2027: use of undefined type 'tagVARIANT'
c:\program files\microsoft visual studio\vc98\mfc\include\afxwin.h(1290) : see declaration of 'tagVARIANT'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(944) : error C2079: 'var' uses undefined struct 'tagVARIANT'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(946) : error C2106: '=' : left operand must be l-value
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2065: 'SAFEARRAYBOUND' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2146: syntax error : missing ';' before identifier 'bounds'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2065: 'bounds' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2109: subscript requires array or pointer type
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2059: syntax error : '{'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(947) : error C2143: syntax error : missing ';' before '{'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(948) : error C2143: syntax error : missing ';' before '}'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(948) : error C2143: syntax error : missing ';' before ','
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(949) : error C2143: syntax error : missing ';' before '{'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(949) : error C2143: syntax error : missing ';' before '}'
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(952) : error C2065: 'VariantClear' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(953) : error C2228: left of '.vt' must have class/struct/union type
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(955) : error C2065: 'SafeArrayCreate' : undeclared identifier
D:\User\Laci\visual studio\img proc\diblook_changed\Matrix.cpp(972) : error C2228: left of '.parray' must have class/struct/union type
Generating Code...
Error executing cl.exe.
Generaloptimizing your codememberTeajay20-Apr-04 20:47 
Nice article on very basic FFT algorithm. You mention you intend to optimze your code... FFT algorithms (like Gabriel 2 said in another thread) are all designed and optimized for a specific purpose. Some people have spend some long hours and even years to get the most out their processors and retrive FFT results. I think you should take a look at other well known FFT librairies such as FFTW before optimizing your code. It could save you some time.
Of course it all depends what your aim is : write your own FFT algorithm or try out cool image processing stuff with FFT.
 
By the way, I read in several articles that the FFT algorithm proposed in Numerical Recepies in C (the one you implemented) is supposed to be one of the slowest ever.... If speed is a concern, don't use it. Try another one.
 
Happy image processing and FFT.
GeneralRe: optimizing your codemembermiklosiklaci21-Apr-04 23:11 
I'd like to know how to use FFTW. I tried but I couldn't find the function headers in the header file, so I don't know what functions to call. If somebody knows please help. Thanks
Generalcoolmemberelectro200020-Apr-04 2:14 
great to see someone's effort in DIP Smile | :)
GeneralOne ObservationmemberRoger Wright28-Mar-04 11:02 
I assume it's a typo in the article, not in your code, but the magnitude is given by:
 
MAG=sqrt(RE*RE + IM*IM);
 
not
 
MAG=sqrt(RE*RE + IM+IM);
 
Just a technicality, but it could be confusing for readers.
 
Will Build Nuclear Missile For Food - No Target Too Small
QuestionMultithreading ?memberRick York26-Mar-04 20:25 
I disagree with your use of multithreading in this application. This is (or should be) a processor-bound application and, generally speaking, multithreading seldom improves performance with those. In fact, it often degrades it as the processor spends its time thrashing the cache, depending on the memory access patterns of the app.
 
However, now my curiousity is piqued. I will do some experiments and see what I find.

 
a two cent stamp short of going postal.

AnswerRe: Multithreading ?memberAris A S27-Mar-04 2:20 

The main reason of using multithread in this project is just my experiments. The result i get was quite good, well at least it was better than if we process the R, G, B values sequentially.
 
But again, it's still experimental.
 
Please notify me if you have finished experimenting with the libs. It'll be my pleasure to have your critics and suggestions.
 
Programming or Die?
----C++ 4 ever-----
GeneralRe: Multithreading ?memberRick York27-Mar-04 13:57 
I have been plodding through this but it has not been easy.
 
First, I had continuous linking errors with the project as it was originally built. I bagged that and made it a static library and that is linking fine now.
 
Second, with the TRUEFFT method, it crashes with the imaginary input matrix argument passed as NULL. If you are going to accept a NULL argument you should handle appropriately. If not then it should not be allowed to default to NULL.
 
Third, I dislike polling for thread completion. I converted that to a WaitForMultipleObjects call and each thread will set an event when they complete.
 
Fourth, I think you should have demonstration app with the library. Your comments are not fully correct in their description of using this.
 

 
_________________________________________
 
a two cent stamp short of going postal.

GeneralRe: Multithreading ?memberAris A S27-Mar-04 19:24 
Thank you very much, Rick!
 
I guess I am still a newbie (in programming). But I'll try to improve my skills. Many thanx to CodeProject and it's members.
 
Programming or Die?
----C++ 4 ever-----
GeneralRe: Multithreading ?memberRick York27-Mar-04 14:35 
I stand corrected. There was no significant difference in the performance using multiple threads or one. I am somewhat surprised but that's what I saw. This is with WinXP.
 
FWIW, I did the TRUEFFT on a 512,512 random image and times were 13.0 seconds on my laptop - a 1.6MHz P4M. I will try this on my box at home when I get a chance. It is a 3200+ A64 and runs W2000. Wink | ;)
 
BTW - CFFT2D has a memory leak. It needs to have "delete [] pInput;" added to its destructor.

 
a two cent stamp short of going postal.

AnswerRe: Multithreading ?memberRobert Buldoc27-Mar-04 14:45 
I have not tested or even looked at the code yet. But I have done some experimentation with FFT & DCT Libraries. I think Rick is partially right. While multithreading wouldn't really help on a single processor/non-hyper-threaded system(and could be even slower), I think a hyper-threaded CPU or an SMP system would benefit from it?
 
Compiler optimization is very important in complex mathematical calculations. I benchmarked a simple FFT with MS Visual C++ 2003 compiler and Intel C++ V8.0 on an Athlon64, and Intel C++ binary was about %25 faster than Microsoft's. I think the difference could be even more on an Intel CPU.

GeneralRe: Multithreading ?memberRick York27-Mar-04 17:34 
I agree - I think multithreading could provide a significant improvement on a multiprocessor system.

 
a two cent stamp short of going postal.

GeneralDisagreememberGabriel 226-Mar-04 11:43 
Your critizice on CxImage is based on a theoretical analisis which, althought it's correct, it's completely useless in most situations.
 
CxImage does not provide signal processing functionality. FFT and DCT transformation are intended to perform DATA COMPRESSION, not SIGNAL PROCESSING.
You could use a floating point version of FFT and DCT, but it would be so slow that it would be completely useless. Have you ever tried a floating point DCT on a 2MP image?
Can you imagine Internet Explorer saying "please wait several minutes while I decompress a JPEG image using floating point DCT", every time you click the mouse?
 
"The result of CxImage’s FFT is an image (it is supposed to be complex numbers)"
Right, but in most situations we are only interested in the module of this numbers. The phase is only used on very special situations (signal processing).
 
"It is ridiculous to process FFT’s result as an image"
Not really. In many situations it's realy usefull to see the results as an image. For example when developing all kind of filters, compressors, detectors, etc.
By the way, have you ever used floating point 2D FFT for image processing?
I have only seen this on very special scientific situations, not the general use, so I guess CxImage version on FFT will be OK for most situations.
 
Finally I would say CxImage simply provides a speed-optimized version of FFT, as required in most situations.
I don't say your library is useless, but I wouldn't call your FFT version the "REAL" one nor start the article by saying that "programmers forget the essence of FrequencyDomain Image Processing".
 

GeneralRe: DisagreememberJohn M. Drescher26-Mar-04 11:54 
Gabriel 2 wrote:
please wait several minutes while I decompress a JPEG image using floating point DCT
 
Laugh | :laugh: Sometimes I wish MSFT wrote messages like this.
 
Especially when you click on explorer and it takes 30 seconds to open because for some unknown reason it is trying to reconnect the network shares and you want c:
 
"Please wait 30 seconds while we reconnect the all network resources for no apparent reason."
 
John

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130619.1 | Last Updated 26 Mar 2004
Article Copyright 2004 by Aris Adrianto S
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid