Example #1
0
        /// <summary>
        /// Very wasteful method that computes the spectrum of a source image
        /// </summary>
        /// <param name="_source"></param>
        /// <returns></returns>
        ImageFile       ComputeSpectrum(ImageFile _source, float _scaleFactor)
        {
            uint size   = _source.Width;
            uint offset = size >> 1;
            uint mask   = size - 1;

            Complex[,]      signal = new Complex[size, size];
            _source.ReadPixels((uint _X, uint _Y, ref float4 _color) => {
                signal[_X, _Y].Set(_color.x, 0);
            });

            SharpMath.FFT.FFT2D_GPU FFT = new SharpMath.FFT.FFT2D_GPU(m_device, size);
            Complex[,]      spectrum = FFT.FFT_Forward(signal);
            FFT.Dispose();

            ImageFile result = new ImageFile(size, size, _source.PixelFormat, _source.ColorProfile);

            result.WritePixels((uint _X, uint _Y, ref float4 _color) => {
                float V = _scaleFactor * (float)spectrum[(_X + offset) & mask, (_Y + offset) & mask].r;
                _color.Set(V, V, V, 1.0f);
            });
            return(result);
        }
Example #2
0
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
            try {
                                #if COMPUTE_RADIAL_SLICE
                m_blueNoise = new ImageFile(new System.IO.FileInfo(@"Images\Data\512_512\LDR_LLL1_0.png"));
//					m_blueNoise = new ImageFile( new System.IO.FileInfo( @"Images\BlueNoiseSpectrumCorners256.png" ) );
//					m_blueNoise = new ImageFile( new System.IO.FileInfo( @"BlueNoise512x512_VoidAndCluster.png" ) );
//					m_blueNoise = new ImageFile( new System.IO.FileInfo( @"BlueNoise256x256_VoidAndCluster.png" ) );

                uint size     = m_blueNoise.Width;
                uint halfSize = size >> 1;

                try {
                    m_device.Init(panelImageSpectrum.Handle, false, false);
                    m_FFT = new SharpMath.FFT.FFT2D_GPU(m_device, size);
                } catch (Exception) {
                }

                if (m_FFT == null)
                {
                    return;
                }

                m_blueNoiseSpectrum = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_handMadeBlueNoise = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_handMadeSpectrum  = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_scanline          = new float4[m_blueNoise.Width];

                //////////////////////////////////////////////////////////////////////////
                // Apply FFT to blue noise
                Complex[,]      input  = new Complex[m_blueNoise.Width, m_blueNoise.Height];
                Complex[,]      output = new Complex[m_blueNoise.Width, m_blueNoise.Height];
                m_blueNoise.ReadPixels((uint X, uint Y, ref float4 _color) => { input[X, Y].Set(_color.x, 0); });
//                  for ( uint Y=0; Y < m_blueNoise.Height; Y++ ) {
//                      m_blueNoise.ReadScanline( Y, m_scanline );
//                      for ( uint X=0; X < m_blueNoise.Width; X++ )
//                          input[X,Y].Set( m_scanline[X].x, 0 );
//                  }
                m_FFT.FFT_Forward(input, output);

                //////////////////////////////////////////////////////////////////////////
                // Build the radial slice average
                Complex[] radialSliceAverage         = new Complex[halfSize];
                uint[]    radialSliceAverageCounters = new uint[halfSize];
                for (uint Y = 0; Y < m_blueNoise.Height; Y++)
                {
                    uint Yoff = (Y + halfSize) & (size - 1);
                    int  Yrel = (int)Y - (int)halfSize;
                    for (uint X = 0; X < m_blueNoise.Width; X++)
                    {
                        uint Xoff     = (X + halfSize) & (size - 1);
                        int  Xrel     = (int)X - (int)halfSize;
                        int  sqRadius = Xrel * Xrel + Yrel * Yrel;
                        if (sqRadius > (halfSize - 1) * (halfSize - 1))
                        {
                            continue;
                        }

                        int radius = (int)Math.Floor(Math.Sqrt(sqRadius));
//							radialSliceAverage[radius] += output[Xoff,Yoff];
                        radialSliceAverage[radius].r += Math.Abs(output[Xoff, Yoff].r);
                        radialSliceAverage[radius].i += Math.Abs(output[Xoff, Yoff].i);
                        radialSliceAverageCounters[radius]++;
                    }
                }
                for (int i = 0; i < halfSize; i++)
                {
//						radialSliceAverage[i].r = Math.Abs( radialSliceAverage[i].r );
                    radialSliceAverage[i] *= radialSliceAverageCounters[i] > 0 ? 1.0f / radialSliceAverageCounters[i] : 1.0f;
                }
                double minAverage = double.MaxValue;
                double maxAverage = double.MinValue;
                for (int i = 0; i < halfSize; i++)
                {
                    if (radialSliceAverage[i].r < 1e-12)
                    {
                        radialSliceAverage[i].r = 1e-12;
                    }
                    if (radialSliceAverage[i].i < 1e-12)
                    {
                        radialSliceAverage[i].i = 1e-12;
                    }

                    if (i > 1)
                    {
                        minAverage = Math.Min(minAverage, radialSliceAverage[i].r);
                        maxAverage = Math.Max(maxAverage, radialSliceAverage[i].r);
                    }
                }

                // Write it to disk
                using (System.IO.FileStream S = new System.IO.FileInfo("BlueNoiseRadialProfile255.complex").Create())
                    using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(S)) {
                        for (int i = 1; i < radialSliceAverage.Length; i++)
                        {
                            W.Write(radialSliceAverage[i].r);
                            W.Write(radialSliceAverage[i].i);
                        }
                    }

                // Smooth it out
                m_radialSliceAverage_Smoothed = new Complex[halfSize];
                Complex average = new Complex();
                for (int i = 1; i < halfSize; i++)
                {
                    average.Zero();
                    for (int j = -4; j <= 4; j++)
                    {
                        average += radialSliceAverage[Math.Max(1, Math.Min(halfSize - 1, i + j))];
                    }
                    m_radialSliceAverage_Smoothed[i] = average / 9.0;
                }


                //////////////////////////////////////////////////////////////////////////
                // Build spectrum noise distribution histogram
                const int BUCKETS_COUNT = 1000;
                uint[]    noiseDistributionHistogram = new uint[BUCKETS_COUNT];
                for (uint Y = 0; Y < m_blueNoise.Height; Y++)
                {
                    uint Yoff = (Y + halfSize) & (size - 1);
                    int  Yrel = (int)Y - (int)halfSize;
                    for (uint X = 0; X < m_blueNoise.Width; X++)
                    {
                        uint   Xoff     = (X + halfSize) & (size - 1);
                        int    Xrel     = (int)X - (int)halfSize;
                        int    sqRadius = Xrel * Xrel + Yrel * Yrel;
                        double radius   = Math.Sqrt(sqRadius) / halfSize;
                        if (radius < 0.01f)
                        {
                            continue;                                           // Avoid central values because of too much imprecision
                        }
                        double random           = Math.Abs(output[Xoff, Yoff].r);
                        double profileAmplitude = RadialProfile(radius);
                        double normalizedRandom = random / profileAmplitude;

                        int bucketIndex = Math.Min(BUCKETS_COUNT - 1, (int)Math.Floor(normalizedRandom * BUCKETS_COUNT));
                        noiseDistributionHistogram[bucketIndex]++;
                    }
                }

                // Write histogram to disk
                using (System.IO.FileStream S = new System.IO.FileInfo("noiseDistribution.float").Create())
                    using (System.IO.BinaryWriter W = new System.IO.BinaryWriter(S)) {
                        for (int i = 0; i < BUCKETS_COUNT; i++)
                        {
                            W.Write((float)noiseDistributionHistogram[i] / BUCKETS_COUNT);
                        }
                    }


                //////////////////////////////////////////////////////////////////////////
                // Build the images

                // Initial Blue Noise Spectrum
                for (uint Y = 0; Y < m_blueNoiseSpectrum.Height; Y++)
                {
                    uint Yoff = (Y + halfSize) & (size - 1);

                    // Initial blue noise spectrum
                    for (uint X = 0; X < m_blueNoiseSpectrum.Width; X++)
                    {
                        uint Xoff = (X + halfSize) & (size - 1);

                        float R = (float)output[Xoff, Yoff].r;
                        float I = (float)output[Xoff, Yoff].i;

                        R *= 500.0f;
                        I *= 500.0f;
                        R  = Math.Abs(R);

                        m_scanline[X].Set(R, R, R, 1.0f);
                    }
                    m_blueNoiseSpectrum.WriteScanline(Y, m_scanline);
                }

                // =====================================================
                // Average radial slice
                m_spectrumRadialSlice = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_spectrumRadialSlice.Clear(float4.One);
                float2 rangeX = new float2(0, halfSize);
//					float2	rangeY = new float2( -8, 0 );
//						m_spectrumRadialSlice.PlotLogGraphAutoRangeY( float4.UnitW, rangeX, ref rangeY, ( float _x ) => {
//                  m_spectrumRadialSlice.PlotLogGraph( new float4( 0, 0, 0, 1 ), rangeX, rangeY, ( float _x ) => {
//                          return (float) radialSliceAverage[(int) Math.Floor( _x )].r;
//                      }, -1.0f, 10.0f );
//
//                  m_spectrumRadialSlice.PlotLogGraph( new float4( 0, 0.5f, 0, 1 ), rangeX, rangeY, ( float _x ) => {
//                          return (float) RadialProfile( _x / halfSize );
//                      }, -1.0f, 10.0f );
//                  m_spectrumRadialSlice.PlotLogGraph( new float4( 0.25f, 0.25f, 0.25f, 1 ), rangeX, rangeY, ( float _x ) => { return (float) radialSliceAverage[(int) Math.Floor( _x )].i; }, -1.0f, 10.0f );
//                  m_spectrumRadialSlice.PlotLogGraph( new float4( 1, 0, 0, 1 ), rangeX, rangeY, ( float _x ) => { return (float) m_radialSliceAverage_Smoothed[(int) Math.Floor( _x )].r; }, -1.0f, 10.0f );
//                  m_spectrumRadialSlice.PlotLogGraph( new float4( 0, 0, 1, 1 ), rangeX, rangeY, ( float _x ) => { return (float) m_radialSliceAverage_Smoothed[(int) Math.Floor( _x )].i; }, -1.0f, 10.0f );
//                  m_spectrumRadialSlice.PlotLogAxes( float4.UnitW, rangeX, rangeY, -16.0f, 10.0f );

                float2 rangeY = new float2(0, 0.0005f);
                m_spectrumRadialSlice.PlotGraph(new float4(0, 0, 0, 1), rangeX, rangeY, ( float _x ) => {
                    return((float)radialSliceAverage[(int)Math.Floor(_x)].r);
                });

                m_spectrumRadialSlice.PlotGraph(new float4(0, 0.5f, 0, 1), rangeX, rangeY, ( float _x ) => {
                    return((float)RadialProfile(_x / halfSize));
                });

                m_spectrumRadialSlice.PlotGraph(new float4(0.25f, 0.25f, 0.25f, 1), rangeX, rangeY, ( float _x ) => { return((float)radialSliceAverage[(int)Math.Floor(_x)].i); });
                m_spectrumRadialSlice.PlotGraph(new float4(1, 0, 0, 1), rangeX, rangeY, ( float _x ) => { return((float)m_radialSliceAverage_Smoothed[(int)Math.Floor(_x)].r); });
                m_spectrumRadialSlice.PlotGraph(new float4(0, 0, 1, 1), rangeX, rangeY, ( float _x ) => { return((float)m_radialSliceAverage_Smoothed[(int)Math.Floor(_x)].i); });

                m_spectrumRadialSlice.PlotAxes(float4.UnitW, rangeX, rangeY, 16.0f, 1e-4f);

                // =====================================================
                // Noise distribution
                m_spectrumNoiseDistribution        = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_spectrumNoiseDistribution_Custom = new ImageFile(m_blueNoise.Width, m_blueNoise.Height, ImageFile.PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_spectrumNoiseDistribution.Clear(float4.One);
                rangeX = new float2(0, 1);
                rangeY = new float2(0, 1.0f / BUCKETS_COUNT);
                m_spectrumNoiseDistribution.PlotGraph(float4.UnitW, rangeX, rangeY, ( float _x ) => {
                    int bucketIndex = Math.Min(BUCKETS_COUNT - 1, (int)Math.Floor(_x * BUCKETS_COUNT));
                    return((float)noiseDistributionHistogram[bucketIndex] / (m_blueNoise.Width * m_blueNoise.Height));
                });
                                #else
                // Create our hand made textures
                const uint NOISE_SIZE = 512;

                try {
                    m_device.Init(panelImageSpectrum.Handle, false, false);
                    m_FFT = new SharpMath.FFT.FFT2D_GPU(m_device, NOISE_SIZE);
                } catch (Exception) {
                }

                m_handMadeBlueNoise = new ImageFile(NOISE_SIZE, NOISE_SIZE, PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
                m_handMadeSpectrum  = new ImageFile(m_handMadeBlueNoise.Width, m_handMadeBlueNoise.Height, PIXEL_FORMAT.RGBA8, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));

                // Read radial profile from disk
                Complex[] radialSliceAverage = new Complex[256];
                using (System.IO.FileStream S = new System.IO.FileInfo("BlueNoiseRadialProfile255.complex").OpenRead())
                    using (System.IO.BinaryReader R = new System.IO.BinaryReader(S)) {
                        for (int i = 1; i < radialSliceAverage.Length; i++)
                        {
                            radialSliceAverage[i].r = R.ReadSingle();
                            radialSliceAverage[i].i = R.ReadSingle();
                        }
                    }
                                #endif

                //////////////////////////////////////////////////////////////////////////
                // Build initial blue-noise
                RebuildNoise();
            } catch (Exception _e) {
                MessageBox.Show(this, "Error during Load() => " + _e.Message, "BlueNoiseGenerator");
            }
        }
Example #3
0
        private void Convert(bool _centralValue, float _signX, float _signY)
        {
            // Initialize gradient filter
            Complex[,]      dx = new Complex[m_size, m_size];
            Complex[,]      dy = new Complex[m_size, m_size];
            Array.Clear(dx, 0, (int)(m_size * m_size));
            Array.Clear(dy, 0, (int)(m_size * m_size));

            if (_centralValue)
            {
                // Central-difference
                dx[m_size - 1, 0].Set(-_signX, 0);
                dx[1, 0].Set(_signX, 0);
                dy[0, m_size - 1].Set(_signY, 0);
                dy[0, 1].Set(-_signY, 0);
            }
            else
            {
                // One-sided difference
                dx[0, 0].Set(-_signX, 0);
                dx[1, 0].Set(_signX, 0);
                dy[0, 0].Set(_signY, 0);
                dy[0, 1].Set(-_signY, 0);
            }

            // Apply forward Fourier transform to obtain spectrum
            SharpMath.FFT.FFT2D_GPU FFT = new SharpMath.FFT.FFT2D_GPU(m_device, m_size);
            Complex[,]      NX = FFT.FFT_Forward(nx);
            Complex[,]      NY = FFT.FFT_Forward(ny);
            Complex[,]      DX = FFT.FFT_Forward(dx);
            Complex[,]      DY = FFT.FFT_Forward(dy);

            // Compute de-convolution
            float factor = m_imageNormal.Width * m_imageNormal.Height;

            Complex[,]      H = new Complex[m_size, m_size];
            Complex temp = new Complex(), sqrtTemp;

            for (uint Y = 0; Y < m_size; Y++)
            {
                for (uint X = 0; X < m_size; X++)
                {
#if !F**K
                    double sqNX = NX[X, Y].SquareMagnitude;
                    double sqNY = NY[X, Y].SquareMagnitude;
                    double num  = sqNX + sqNY;

                    double sqDX = DX[X, Y].SquareMagnitude;
                    double sqDY = DY[X, Y].SquareMagnitude;
                    double den  = factor * (sqDX + sqDY);

                    temp.Set(Math.Abs(den) > 0.0 ? num / den : 0.0, 0.0);
                    sqrtTemp = temp.Sqrt();

                    H[X, Y] = sqrtTemp;
#else
                    Complex Nx  = NX[X, Y];
                    Complex Ny  = NY[X, Y];
                    Complex Dx  = DX[X, Y];
                    Complex Dy  = DY[X, Y];
                    double  den = factor * (DX[X, Y].SquareMagnitude + DY[X, Y].SquareMagnitude);
                    if (Math.Abs(den) > 0.0)
                    {
                        temp    = -(Dx * Nx + Dy * Ny) / den;
                        H[X, Y] = temp;
                    }
                    else
                    {
                        H[X, Y].Zero();
                    }
#endif
                }
            }

            // Apply inverse transform
            Complex[,]      h = FFT.FFT_Inverse(H);

            Complex heightMin = new Complex(double.MaxValue, double.MaxValue);
            Complex heightMax = new Complex(-double.MaxValue, -double.MaxValue);
            for (uint Y = 0; Y < m_size; Y++)
            {
                for (uint X = 0; X < m_size; X++)
                {
                    Complex height = h[X, Y];
                    heightMin.Min(height);
                    heightMax.Max(height);
                }
            }

            // Render result
            if (m_imageHeight != null)
            {
                m_imageHeight.Dispose();
                m_imageHeight = null;
            }

            factor = 1.0f / (float)(heightMax.r - heightMin.r);
// heightMin.r = -1.0f;
// factor = 0.5f;

            m_imageHeight = new ImageFile(m_imageNormal.Width, m_imageNormal.Height, PIXEL_FORMAT.R16, new ColorProfile(ColorProfile.STANDARD_PROFILE.sRGB));
//			m_imageHeight.WritePixels( ( uint X, uint Y, ref float4 _color ) => { _color.x = 1e3f * (float) H[X,Y].Magnitude; } );
//			m_imageHeight.WritePixels( ( uint X, uint Y, ref float4 _color ) => { _color.x = 1e5f * (float) DX[X,Y].Magnitude; } );
            m_imageHeight.WritePixels((uint X, uint Y, ref float4 _color) => { _color.x = factor * (float)(h[X, Y].r - heightMin.r); });

            imagePanelHeight.Bitmap = m_imageHeight.AsBitmap;

            FFT.Dispose();
        }