DFT

by wrotenodoc
extremely slow!
an exercise on taking multimedia computing course
test for frequency cut

top left: original image
top right: magnitude
bottom right: phase
bottom left: restored image (frequencies < 100 are discarded)
♥0 | Line 167 | Modified 2015-11-06 13:34:50 | MIT License
play

ActionScript3 source code

/**
 * Copyright wrotenodoc ( http://wonderfl.net/user/wrotenodoc )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/Yd32
 */

package {

    import flash.display.Sprite
    import flash.display.Bitmap
    import flash.display.BitmapData
    
    import com.actionscriptbible.Example
    
    public class FFTTest extends Example {
        
        public function FFTTest() {
            // write as3 code here..
            var bd:BitmapData = new BitmapData(64, 64, true, 0x0)
            bd.perlinNoise(32, 32, 4, 0, false, true, 7, true)
            var bmp:Bitmap = new Bitmap(bd)
            bmp.y = 20
            bmp.scaleX = bmp.scaleY = 3
            addChild(bmp)
            
            // cut frequencies < 100
            var fft:FFTResult = FFT(bd2ary(bd), 100)
            
            var fftBD:Vector.<BitmapData> = fft2bd(fft)
            with(addChild(new Bitmap(fftBD[0]))){
                x = 64 * 3
                y = 20
                scaleX = scaleY = 3
            }
            with(addChild(new Bitmap(fftBD[1]))){
                x = 64 * 3
                y = bmp.y + bmp.height
                scaleX = scaleY = 3
            }
            
            var ifft:Array = IFFT(fft)
            var bd2:BitmapData = bd.clone()
            var col:uint
            for(var i:int=0; i<bd.width; i++){
                for(var j:int=0; j<bd.height; j++){
                    col = ifft[j][i]
                    col = col<<16 | col<<8 | col
                    bd2.setPixel(i, j, col)
                }
            }
            with(addChild(new Bitmap(bd2))){
                scaleX = scaleY = 3
                y = bmp.y + bmp.height
            }
        }
        
        private function bd2ary(bd:BitmapData):Array {
            var ary:Array = []
            for(var y:int=0; y<bd.height; y++){
                ary[y] = []
                for(var x:int=0; x<bd.width; x++){
                    ary[y][x] = bd.getPixel(x, y) & 0xFF
                }
            }
            return ary
        }
        
        private function fft2bd(fft:FFTResult):Vector.<BitmapData> {
            var bd:BitmapData = new BitmapData(fft.width, fft.height, false, 0x0)
            var bd2:BitmapData = new BitmapData(fft.width, fft.height, false, 0x0)
            for(var y:int=0; y<fft.height; y++){
                for(var x:int=0; x<fft.width; x++){
                    var color:uint = uint(Math.log(1 + (fft.mag[y][x] - fft.magMin)/(fft.magMax - fft.magMin)) * 255 * 255)
                    bd.setPixel(x, y, color<<16 | color<<8 | color)
                    color = uint(fft.phase[y][x]/pi2 * 255)
                    bd2.setPixel(x, y, color<<16 | color<<8 | color)
                }
            }
            return Vector.<BitmapData>([bd, bd2]) // magnitude image, phase image
        }
        
        // image: 2D array (row-major)
        private const pi2:Number = Number.PI * 2
        private function FFT(image:Array, freqCut:Number):FFTResult {
            var N:int = image.length // height
            var M:int = image[0].length // width
            var result:FFTResult = new FFTResult(M, N)
            var i:int, j:int
    
            for(i=0; i<N; i++){
                for(j=0; j<N; j++){
                    result.data[i][j] = sum(i, j)
                    result.mag[i][j] = result.data[i][j].magnitude
                    result.phase[i][j] = result.data[i][j].phase
                    if(result.phase[i][j] < 0) result.phase[i][j] += pi2
                }
            }
            
            result.magMin = result.realMin = result.imgMin = Infinity
            result.magMax = result.realMax = result.imgMax = -Infinity
            var r:Number, im:Number, mag:Number
            for(i=0; i<N; i++){
                for(j=0; j<N; j++){
                    r = result.data[i][j].real
                    im = result.data[i][j].img
                    mag = result.mag[i][j]
                    if(mag < freqCut) result.data[i][j].real = result.data[i][j].img = 0 // cut low frequencies
                    if(r > result.realMax) result.realMax = r
                    if(r < result.realMin) result.realMin = r
                    if(im > result.imgMax) result.imgMax = im
                    if(im < result.imgMin) result.imgMin = im
                    if(mag > result.magMax) result.magMax = mag
                    if(mag < result.magMin) result.magMin = mag
                }
            }
    
            return result
    
            function sum(i:int, j:int):Component {
                var real:Number = 0, img:Number = 0
                for(var y:int=0; y<N; y++){
                    for(var x:int=0; x<M; x++){
                        real += image[y][x] * Math.cos(pi2 * (x*j/M + y*i/N))
                        img -= image[y][x] * Math.sin(pi2 * (x*j/M + y*i/N))
                    }
                }
                return new Component(real, img)
            }
        }
        
        private function IFFT(fft:FFTResult):Array {
            var ary:Array = []
            var i:int, j:int
            var N:int = fft.height, M:int = fft.width
            var MN:int = M * N
            
            for(i=0; i<N; i++) ary[i] = []
            for(i=0; i<N; i++){
                for(j=0; j<M; j++){
                    ary[i][j] = sum(i, j)
                }
            }
            
            return ary
            
            function sum(i:int, j:int):Number {
                var real:Number = 0, img:Number = 0
                var s:Number, c:Number, r:Number, im:Number
                for(var y:int=0; y<N; y++){
                    for(var x:int=0; x<M; x++){
                        c = Math.cos(pi2 * (x*j/M + y*i/N))
                        s = Math.sin(pi2 * (x*j/M + y*i/N))
                        r = fft.data[y][x].real
                        im = fft.data[y][x].img
                        real += r * c - im * s
                        img += im * c + r * s
                    }
                }
                // original signal had only real part, so as a result of summation, img is 0
                return real / MN
            }
        }
        
    }
    
}

class FFTResult {
    public var data:Array, mag:Array, phase:Array
    public var realMax:Number, realMin:Number
    public var imgMax:Number, imgMin:Number
    public var magMax:Number, magMin:Number
    public var width:int, height:int
    public function FFTResult(width:int, height:int) {
        data = []
        for(var i:int=0; i<height; i++) data[i] = []
        mag = []
        for(i=0; i<height; i++) mag[i] = []
        phase = []
        for(i=0; i<height; i++) phase[i] = []
        this.width = width
        this.height = height
    }
}

class Component {
    public var real:Number
    public var img:Number
    public function Component(r:Number, i:Number) {
        real = r
        img = i
    }
    public function get magnitude():Number {
        return Math.sqrt(real*real + img*img)
    }
    public function get phase():Number {
        return Math.atan2(real, img)
    }
}

Forked