Пример #1
0
    // Test the methods

    /*
     * z = [139 144 149 153 155 155 155 155;
     * 144 151 153 156 159 156 156 156;
     * 150 155 160 163 158 156 156 156;
     * 159 161 162 160 160 159 159 159;
     * 159 160 161 162 162 155 155 155;
     * 161 161 161 161 160 157 157 157;
     * 162 162 161 163 162 157 157 157;
     * 162 162 161 161 163 158 158 158];
     *
     * octave:55> x = dct(z)
     * x =
     * 436.99      444.06      448.31      452.19      452.19         443         443         443
     * -21.818     -14.969     -9.3908     -6.4728      -5.921     -1.7745     -1.7745     -1.7745
     * -8.8097     -7.5031     -7.3446     -4.6522     -1.2737    -0.46194    -0.46194    -0.46194
     * -2.4118     -3.7457     -3.9958     -3.0683     -1.4969     -1.7704     -1.7704     -1.7704
     * 0.70711    -0.70711    -0.70711     -2.4749     0.35355     0.35355     0.35355     0.35355
     * 1.365     0.22465     0.90791     0.57409     -1.7777      1.2224      1.2224      1.2224
     * -0.94311     -1.4843     0.74614     0.77897     -2.1512    -0.19134    -0.19134    -0.19134
     * -1.8165      -1.685     0.14561      2.9764     0.20231     -2.3922     -2.3922     -2.3922
     *
     * octave:56> idct(x)
     * ans =
     * 139         144         149         153         155         155         155         155
     * 144         151         153         156         159         156         156         156
     * 150         155         160         163         158         156         156         156
     * 159         161         162         160         160         159         159         159
     * 159         160         161         162         162         155         155         155
     * 161         161         161         161         160         157         157         157
     * 162         162         161         163         162         157         157         157
     * 162         162         161         161         163         158         158         158
     */
    public static void test()
    {
        double[][] vals = new double[][] {
            new double[] { 139.0, 144.0, 149.0, 153.0, 155.0, 155.0, 155.0, 155.0 },
            new double[] { 144.0, 151.0, 153.0, 156.0, 159.0, 156.0, 156.0, 156.0 },
            new double[] { 150.0, 155.0, 160.0, 163.0, 158.0, 156.0, 156.0, 156.0 },
            new double[] { 159.0, 161.0, 162.0, 160.0, 160.0, 159.0, 159.0, 159.0 },
            new double[] { 159.0, 160.0, 161.0, 162.0, 162.0, 155.0, 155.0, 155.0 },
            new double[] { 161.0, 161.0, 161.0, 161.0, 160.0, 157.0, 157.0, 157.0 },
            new double[] { 162.0, 162.0, 161.0, 163.0, 162.0, 157.0, 157.0, 157.0 },
            new double[] { 162.0, 162.0, 161.0, 161.0, 163.0, 158.0, 158.0, 158.0 }
        };

        DctMethods.PrintMatrix(vals);

        DctComirva dctCom = new DctComirva(vals.Length, vals[0].Length);

        // dct
        double[][] dctVals = dctCom.dct(vals);
        DctMethods.PrintMatrix(dctVals);

        // idct
        double[][] idctVals = dctCom.idct(dctVals);
        DctMethods.PrintMatrix(idctVals);
    }
Пример #2
0
        // test 1
        // Octave results:
        // format short g
        // z = [1 2 3; 4 5 6; 7 8 9; 10 11 12];
        //
        //octave:5> dct(z)
        //ans =
        //          11          13          15
        //     -6.6913     -6.6913     -6.6913
        //           0           0           0
        //    -0.47554    -0.47554    -0.47554
        //
        //octave:6> dct2(z)
        //ans =
        //      22.517     -2.8284           0
        //      -11.59           0           0
        //           0           0           0
        //    -0.82366           0           0
        //
        #endregion
        private static void Test1(bool _2D = true)
        {
            var vals = new double[][] {
                new double[] { 1.0, 2.0, 3.0 },
                new double[] { 4.0, 5.0, 6.0 },
                new double[] { 7.0, 8.0, 9.0 },
                new double[] { 10.0, 11.0, 12.0 }
            };

            const double offset = 0.0;

            Console.WriteLine("vals: ");
            DctMethods.PrintMatrix(vals);

            Stopwatch stopWatch = Stopwatch.StartNew();
            long      startS    = stopWatch.ElapsedTicks;

            double[][] result;
            if (_2D)
            {
                result = DctMethods.dct2(vals, offset);
                Console.WriteLine("dct2 result: ");
            }
            else
            {
                result = DctMethods.dct(vals, offset);
                Console.WriteLine("dct result: ");
            }
            long endS = stopWatch.ElapsedTicks;

            DctMethods.PrintMatrix(result);
            Console.WriteLine("time in ticks: " + (endS - startS));

            //result = Filter(result, 1.0);
            //Console.WriteLine("dct2 filtered: ");
            //PrintMatrix(result);

            long startE = stopWatch.ElapsedTicks;

            double[][] ivals;
            if (_2D)
            {
                ivals = DctMethods.idct2(result, -offset);
                Console.WriteLine("idct2 result: ");
            }
            else
            {
                ivals = DctMethods.idct(result, -offset);
                Console.WriteLine("idct result: ");
            }
            long endE = stopWatch.ElapsedTicks;

            DctMethods.PrintMatrix(ivals);
            Console.WriteLine("Time in ticks: " + (endE - startE));

            Assert.Equal(vals, ivals, new JaggedDoubleComparer(0.001));
            // Assert.That(ivals, Is.EqualTo(vals).AsCollection.Within(0.001), "fail at [0]");
        }
Пример #3
0
        // test 2
        // Octave results:
        // format short g

        /*
         * octave:43>
         * z = [139 144 149 153 155 155 155 155;
         * 144 151 153 156 159 156 156 156;
         * 150 155 160 163 158 156 156 156;
         * 159 161 162 160 160 159 159 159;
         * 159 160 161 162 162 155 155 155;
         * 161 161 161 161 160 157 157 157;
         * 162 162 161 163 162 157 157 157;
         * 162 162 161 161 163 158 158 158];
         *
         * octave:43> g = dct2(z-128)
         * g =
         * 235.62     -1.0333     -12.081     -5.2029       2.125     -1.6724      -2.708      1.3238
         * -22.59     -17.484     -6.2405     -3.1574     -2.8557   -0.069456     0.43417     -1.1856
         * -10.949     -9.2624     -1.5758      1.5301     0.20295    -0.94186    -0.56694   -0.062924
         * -7.0816     -1.9072     0.22479      1.4539     0.89625   -0.079874   -0.042291     0.33154
         * -0.625    -0.83811      1.4699      1.5563      -0.125    -0.66099     0.60885      1.2752
         * 1.7541    -0.20286      1.6205    -0.34244    -0.77554      1.4759       1.041    -0.99296
         * -1.2825    -0.35995    -0.31694     -1.4601    -0.48996      1.7348      1.0758    -0.76135
         * -2.5999      1.5519     -3.7628     -1.8448      1.8716      1.2139    -0.56788    -0.44564
         *
         * octave:44> idct2(g)+128
         * ans =
         * 139         144         149         153         155         155         155         155
         * 144         151         153         156         159         156         156         156
         * 150         155         160         163         158         156         156         156
         * 159         161         162         160         160         159         159         159
         * 159         160         161         162         162         155         155         155
         * 161         161         161         161         160         157         157         157
         * 162         162         161         163         162         157         157         157
         * 162         162         161         161         163         158         158         158
         *
         * octave:49> g = dct(z-128)
         * g =
         *
         * 74.953      82.024      86.267      90.156      90.156      80.964      80.964      80.964
         * -21.818     -14.969     -9.3908     -6.4728      -5.921     -1.7745     -1.7745     -1.7745
         * -8.8097     -7.5031     -7.3446     -4.6522     -1.2737    -0.46194    -0.46194    -0.46194
         * -2.4118     -3.7457     -3.9958     -3.0683     -1.4969     -1.7704     -1.7704     -1.7704
         * 0.70711    -0.70711    -0.70711     -2.4749     0.35355     0.35355     0.35355     0.35355
         * 1.365     0.22465     0.90791     0.57409     -1.7777      1.2224      1.2224      1.2224
         * -0.94311     -1.4843     0.74614     0.77897     -2.1512    -0.19134    -0.19134    -0.19134
         * -1.8165      -1.685     0.14561      2.9764     0.20231     -2.3922     -2.3922     -2.3922
         *
         * octave:50> idct(g)+128
         * ans =
         *
         * 139         144         149         153         155         155         155         155
         * 144         151         153         156         159         156         156         156
         * 150         155         160         163         158         156         156         156
         * 159         161         162         160         160         159         159         159
         * 159         160         161         162         162         155         155         155
         * 161         161         161         161         160         157         157         157
         * 162         162         161         163         162         157         157         157
         * 162         162         161         161         163         158         158         158
         */
        #endregion
        public static void Test2(bool _2D = true, bool random = false)
        {
            double[][] vals;
            if (random)
            {
                // Generate random integers between 0 and 255
                const int N         = 8;
                var       generator = new Random();

                vals = new double[N][];
                int val;
                for (int x = 0; x < N; x++)
                {
                    vals[x] = new double[N];
                    for (int y = 0; y < N; y++)
                    {
                        val        = generator.Next(255);
                        vals[x][y] = val;
                    }
                }
            }
            else
            {
                vals = new double[][] {
                    new double[] { 139.0, 144.0, 149.0, 153.0, 155.0, 155.0, 155.0, 155.0 },
                    new double[] { 144.0, 151.0, 153.0, 156.0, 159.0, 156.0, 156.0, 156.0 },
                    new double[] { 150.0, 155.0, 160.0, 163.0, 158.0, 156.0, 156.0, 156.0 },
                    new double[] { 159.0, 161.0, 162.0, 160.0, 160.0, 159.0, 159.0, 159.0 },
                    new double[] { 159.0, 160.0, 161.0, 162.0, 162.0, 155.0, 155.0, 155.0 },
                    new double[] { 161.0, 161.0, 161.0, 161.0, 160.0, 157.0, 157.0, 157.0 },
                    new double[] { 162.0, 162.0, 161.0, 163.0, 162.0, 157.0, 157.0, 157.0 },
                    new double[] { 162.0, 162.0, 161.0, 161.0, 163.0, 158.0, 158.0, 158.0 }
                };
            }
            const double offset = -128.0;

            Console.WriteLine("vals: ");
            DctMethods.PrintMatrix(vals);

            Stopwatch stopWatch = Stopwatch.StartNew();
            long      startS    = stopWatch.ElapsedTicks;

            double[][] result;
            if (_2D)
            {
                result = DctMethods.dct2(vals, offset);
                Console.WriteLine("dct2 result: ");
            }
            else
            {
                result = DctMethods.dct(vals, offset);
                Console.WriteLine("dct result: ");
            }
            long endS = stopWatch.ElapsedTicks;

            DctMethods.PrintMatrix(result);

            Console.WriteLine("Time in ticks: " + (endS - startS));

            //result = Filter(result, 0.25);
            //result = CutLeastSignificantCoefficients(result);
            //Console.WriteLine("dct2 filtered: ");
            //PrintMatrix(result);

            long startE = stopWatch.ElapsedTicks;

            double[][] ivals;
            if (_2D)
            {
                ivals = DctMethods.idct2(result, -offset);
                Console.WriteLine("idct2 result: ");
            }
            else
            {
                ivals = DctMethods.idct(result, -offset);
                Console.WriteLine("idct result: ");
            }
            long endE = stopWatch.ElapsedTicks;

            DctMethods.PrintMatrix(ivals);
            Console.WriteLine("Time in ticks: " + (endE - startE));

            Assert.That(ivals, Is.EqualTo(vals).AsCollection.Within(0.001), "fail at [0]");
        }
Пример #4
0
        public void TestDctMatrix()
        {
            #region Output from Octave

            /*
             * z = [139 144 149 153 155 155 155 155;
             * 144 151 153 156 159 156 156 156;
             * 150 155 160 163 158 156 156 156;
             * 159 161 162 160 160 159 159 159;
             * 159 160 161 162 162 155 155 155;
             * 161 161 161 161 160 157 157 157;
             * 162 162 161 163 162 157 157 157;
             * 162 162 161 161 163 158 158 158];
             *
             * octave:55> x = dct(z)
             * x =
             * 436.99      444.06      448.31      452.19      452.19         443         443         443
             * -21.818     -14.969     -9.3908     -6.4728      -5.921     -1.7745     -1.7745     -1.7745
             * -8.8097     -7.5031     -7.3446     -4.6522     -1.2737    -0.46194    -0.46194    -0.46194
             * -2.4118     -3.7457     -3.9958     -3.0683     -1.4969     -1.7704     -1.7704     -1.7704
             * 0.70711    -0.70711    -0.70711     -2.4749     0.35355     0.35355     0.35355     0.35355
             * 1.365     0.22465     0.90791     0.57409     -1.7777      1.2224      1.2224      1.2224
             * -0.94311     -1.4843     0.74614     0.77897     -2.1512    -0.19134    -0.19134    -0.19134
             * -1.8165      -1.685     0.14561      2.9764     0.20231     -2.3922     -2.3922     -2.3922
             *
             * octave:56> idct(x)
             * ans =
             * 139         144         149         153         155         155         155         155
             * 144         151         153         156         159         156         156         156
             * 150         155         160         163         158         156         156         156
             * 159         161         162         160         160         159         159         159
             * 159         160         161         162         162         155         155         155
             * 161         161         161         161         160         157         157         157
             * 162         162         161         163         162         157         157         157
             * 162         162         161         161         163         158         158         158
             */
            #endregion

            var vals = new double[][] {
                new double[] { 139.0, 144.0, 149.0, 153.0, 155.0, 155.0, 155.0, 155.0 },
                new double[] { 144.0, 151.0, 153.0, 156.0, 159.0, 156.0, 156.0, 156.0 },
                new double[] { 150.0, 155.0, 160.0, 163.0, 158.0, 156.0, 156.0, 156.0 },
                new double[] { 159.0, 161.0, 162.0, 160.0, 160.0, 159.0, 159.0, 159.0 },
                new double[] { 159.0, 160.0, 161.0, 162.0, 162.0, 155.0, 155.0, 155.0 },
                new double[] { 161.0, 161.0, 161.0, 161.0, 160.0, 157.0, 157.0, 157.0 },
                new double[] { 162.0, 162.0, 161.0, 163.0, 162.0, 157.0, 157.0, 157.0 },
                new double[] { 162.0, 162.0, 161.0, 161.0, 163.0, 158.0, 158.0, 158.0 }
            };

            DctMethods.PrintMatrix(vals);

            var dctCom = new DctMatrix(vals.Length, vals[0].Length);

            // dct
            double[][] dctVals = dctCom.Dct(vals);
            DctMethods.PrintMatrix(dctVals);

            // idct
            double[][] idctVals = dctCom.InverseDct(dctVals);
            DctMethods.PrintMatrix(idctVals);

            Assert.That(idctVals, Is.EqualTo(vals).AsCollection.Within(0.001), "fail at [0]");
        }
Пример #5
0
        /// <summary>
        /// Calcutate the perceptual hash of an image according to the algorithm given by Dr. Neal Krawetz
        /// on his blog: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html.
        /// </summary>
        /// <param name="image">The image to hash.</param>
        /// <returns>Returns a 'binary string' (aka bitstring) (like. 001010111011100010) which is easy to do a hamming distance on.</returns>
        private string GetHash(Bitmap img)
        {
            // 1. Reduce size.
            // Like Average Hash, pHash starts with a small image.
            // However, the image is larger than 8x8; 32x32 is a good size.
            // This is really done to simplify the DCT computation and not
            // because it is needed to reduce the high frequencies.
            var simg = CommonUtils.ImageUtils.Resize(img, size, size);

            // 2. Reduce color.
            // The image is reduced to a grayscale just to further simplify
            // the number of computations.
            var gimg = CommonUtils.ImageUtils.MakeGrayscale(simg);

            double[][] vals = new double[size][];

            // for faster pixels access
            // http://csharpexamples.com/fast-image-processing-c/

            unsafe
            {
                BitmapData bitmapData = gimg.LockBits(new Rectangle(0, 0, gimg.Width, gimg.Height), ImageLockMode.ReadWrite, gimg.PixelFormat);

                int   bytesPerPixel  = System.Drawing.Bitmap.GetPixelFormatSize(gimg.PixelFormat) / 8;
                int   heightInPixels = bitmapData.Height;
                int   widthInBytes   = bitmapData.Width * bytesPerPixel;
                byte *PtrFirstPixel  = (byte *)bitmapData.Scan0;

                for (int x = 0; x < widthInBytes; x = x + bytesPerPixel)
                {
                    vals[x / 4] = new double[size];
                    for (int y = 0; y < heightInPixels; y++)
                    {
                        byte *currentLine = PtrFirstPixel + (y * bitmapData.Stride);

                        // Console.WriteLine("x ({0})  y ({1})", x, y);

                        // when the image is grayscale RGB has the same value
                        vals[x / 4][y] = currentLine[x];
                    }
                }

                gimg.UnlockBits(bitmapData);
                simg.Dispose();
                gimg.Dispose();
            }

            //  3. Calcutate the DCT.
            //	The DCT separates the image into a collection of frequencies
            //	and scalars. While JPEG uses an 8x8 DCT, this algorithm uses
            //	a 32x32 DCT.
            double[][] dctVals = DctMethods.dct2(vals);
            //double[][] dctVals = DctMethods.idct2(vals);

            // 4. Reduce the DCT.
            // This is the magic step. While the DCT is 32x32, just keep the
            // top-left 8x8. Those represent the lowest frequencies in the
            // picture.

            // 5 a) Calcutate the average value.
            // Like the Average Hash, compute the mean DCT value (using only
            // the 8x8 DCT low-frequency values and excluding the first term
            // since the DC coefficient can be significantly different from
            // the other values and will throw off the average).
            double total = 0;

            for (int x = 0; x < smallerSize; x++)
            {
                for (int y = 0; y < smallerSize; y++)
                {
                    total += dctVals[x][y];
                }
            }
            total -= dctVals[0][0];

            // 5. b) Calcutate the average value.
            double avg = total / (double)((smallerSize * smallerSize) - 1);

            // 6. Further reduce the DCT.
            // This is the magic step. Set the 64 hash bits to 0 or 1
            // depending on whether each of the 64 DCT values is above or
            // below the average value. The result doesn't tell us the
            // actual low frequencies; it just tells us the very-rough
            // relative scale of the frequencies to the mean. The result
            // will not vary as long as the overall structure of the image
            // remains the same; this can survive gamma and color histogram
            // adjustments without a problem.
            string hash = String.Empty;

            for (int x = 0; x < smallerSize; x++)
            {
                for (int y = 0; y < smallerSize; y++)
                {
                    if (x != 0 && y != 0)
                    {
                        hash += (dctVals[x][y] > avg ? "1" : "0");
                    }
                }
            }

            return(hash);
        }
Пример #6
0
        /// <summary>
        /// Computes the perceptual hash of an image according to the algorithm given by Dr. Neal Krawetz
        /// on his blog: http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html.
        /// </summary>
        /// <param name="img">The image to hash.</param>
        /// <returns>Returns a 'binary string' (aka bitstring) (like. 001010111011100010) which is easy to do a hamming distance on.</returns>
        public string GetHash(Bitmap img)
        {
                        #if DEBUG
            img.Save("ImagePHash 1-orig.png");
                        #endif

            // 1. Reduce size.
            // Like Average Hash, pHash starts with a small image.
            // However, the image is larger than 8x8; 32x32 is a good size.
            // This is really done to simplify the DCT computation and not
            // because it is needed to reduce the high frequencies.
            img = ImageUtils.Resize(img, size, size);

                        #if DEBUG
            img.Save("ImagePHash 2-reduced.png");
                        #endif

            // 2. Reduce color.
            // The image is reduced to a grayscale just to further simplify
            // the number of computations.
            img = ImageUtils.MakeGrayscale(img);

                        #if DEBUG
            img.Save("ImagePHash 3-grayscale.png");
                        #endif

            var vals = new double[size][];
            for (int x = 0; x < img.Width; x++)
            {
                vals[x] = new double[size];
                for (int y = 0; y < img.Height; y++)
                {
                    // when the image is grayscale RGB has the same value
                    vals[x][y] = img.GetPixel(x, y).B;
                }
            }

                        #if DEBUG
            // create byte array to be able to save the image
            var grayscaleByteArray = new byte[size * size];
            for (int x = 0; x < size; x++)
            {
                for (int y = 0; y < size; y++)
                {
                    // add to byte array
                    grayscaleByteArray[x + (y * size)] = Convert.ToByte(vals[x][y]);
                }
            }
            Image grayscale = ImageUtils.ByteArrayGrayscaleToImage(grayscaleByteArray, size, size);
            grayscale.Save("ImagePHash 4-grayscale-array.png");
                        #endif

            //  3. Compute the DCT.
            //	The DCT separates the image into a collection of frequencies
            //	and scalars. While JPEG uses an 8x8 DCT, this algorithm uses
            //	a 32x32 DCT.
            double[][] dctVals = DctMethods.dct2(vals);

                        #if DEBUG
            // create image array to be able to save DCT image

            // array to keep only the highest frequency items
            var dctValsOnlyHighFreq = new double[size][];

            // Compressing Range By taking Log
            var dctLogVals = new double[dctVals.Length][];
            for (int x = 0; x < size; x++)
            {
                dctValsOnlyHighFreq[x] = new double[dctVals[x].Length];
                dctLogVals[x]          = new double[dctVals[x].Length];
                for (int y = 0; y < size; y++)
                {
                    dctLogVals[x][y] = Math.Log(1 + Math.Abs((int)dctVals[x][y]));
                }
            }

            // Normalizing Array
            double dctMin = 0;
            double dctMax = 0;
            MathUtils.ComputeMinAndMax(dctLogVals, out dctMin, out dctMax);
            var dctPixels = new byte[size * size];
            for (int x = 0; x < size; x++)
            {
                for (int y = 0; y < size; y++)
                {
                    // Constrain Range between 0 and 255
                    int dctPixelVal = (int)(((float)(dctLogVals[x][y] - dctMin) / (float)(dctMax - dctMin)) * 255);
                    dctPixels[x + (y * size)] = Convert.ToByte(dctPixelVal);
                }
            }

            Image dctImage = ImageUtils.ByteArrayGrayscaleToImage(dctPixels, size, size);
            dctImage.Save("ImagePHash 5-dct.png");
                        #endif

            // 4. Reduce the DCT.
            // This is the magic step. While the DCT is 32x32, just keep the
            // top-left 8x8. Those represent the lowest frequencies in the
            // picture.

                        #if DEBUG
            // create image array to be able to save DCT image
            var dctPixelsSmallerSize = new byte[smallerSize * smallerSize];
                        #endif

            // 5 a) Compute the average value.
            // Like the Average Hash, compute the mean DCT value (using only
            // the 8x8 DCT low-frequency values and excluding the first term
            // since the DC coefficient can be significantly different from
            // the other values and will throw off the average).
            double total = 0;
            for (int x = 0; x < smallerSize; x++)
            {
                for (int y = 0; y < smallerSize; y++)
                {
                                        #if DEBUG
                    dctValsOnlyHighFreq[x][y] = dctVals[x][y];                     // store in new dct val array

                    // convert to pixel values
                    int dctPixelSmallerVal = (int)(((float)(dctLogVals[x][y] - dctMin) / (float)(dctMax - dctMin)) * 255);
                    dctPixelsSmallerSize[x + (y * smallerSize)] = Convert.ToByte(dctPixelSmallerVal);
                                        #endif
                    total += dctVals[x][y];
                }
            }
            total -= dctVals[0][0];

                        #if DEBUG
            Image dctImageSmaller = ImageUtils.ByteArrayGrayscaleToImage(dctPixelsSmallerSize, smallerSize, smallerSize);
            dctImageSmaller.Save("ImagePHash 6-dct-smaller.png");

            // Inverse DCT
            double[][] inverseDctVals = DctMethods.idct2(dctValsOnlyHighFreq);

            var idctPixels = new byte[size * size];
            for (int x = 0; x < size; x++)
            {
                for (int y = 0; y < size; y++)
                {
                    double inverseDctVal = inverseDctVals[x][y];
                    inverseDctVal = inverseDctVal < 0 ? 0 : inverseDctVal;
                    inverseDctVal = inverseDctVal > 255 ? 255 : inverseDctVal;
                    idctPixels[x + (y * size)] = Convert.ToByte(inverseDctVal);
                }
            }
            Image idctImage = ImageUtils.ByteArrayGrayscaleToImage(idctPixels, size, size);
            idctImage.Save("ImagePHash 7-idct.png");
                        #endif

            // 5. b) Compute the average value.
            double avg = total / (double)((smallerSize * smallerSize) - 1);

            // 6. Further reduce the DCT.
            // This is the magic step. Set the 64 hash bits to 0 or 1
            // depending on whether each of the 64 DCT values is above or
            // below the average value. The result doesn't tell us the
            // actual low frequencies; it just tells us the very-rough
            // relative scale of the frequencies to the mean. The result
            // will not vary as long as the overall structure of the image
            // remains the same; this can survive gamma and color histogram
            // adjustments without a problem.
            string hash = "";
            for (int x = 0; x < smallerSize; x++)
            {
                for (int y = 0; y < smallerSize; y++)
                {
                    if (x != 0 && y != 0)
                    {
                        hash += (dctVals[x][y] > avg ? "1" : "0");
                    }
                }
            }

            return(hash);
        }