コード例 #1
0
ファイル: Distance.cs プロジェクト: vosen/UAM.ImageProcessing
 private static double[][] ToInitialDistanceMap(PNM image, ref int pixelCount)
 {
     double[][] distanceMap = new double[image.Width][];
     for(int i =0; i< image.Width; i++)
     {
         distanceMap[i] = new double[image.Height];
     }
     byte l;
     for (int i = 0; i < image.Width; i++)
     {
         for (int j = 0; j < image.Height; j++)
         {
             image.GetPixel(i, j, out l, out l, out l);
             if (l == 255)
             {
                 distanceMap[i][j] = 0;
                 pixelCount++;
             }
             else
             {
                 distanceMap[i][j] = Double.PositiveInfinity;
             }
         }
     }
     return distanceMap;
 }
コード例 #2
0
 public static Func<byte, byte, byte, Pixel> Equalize(PNM pnm)
 {
     double[] rawDataR = pnm.GetHistogramRed();
     double[] rawDataG = pnm.GetHistogramGreen();
     double[] rawDataB = pnm.GetHistogramBlue();
     double[] rlum = new double[256];
     double[] glum = new double[256];
     double[] blum = new double[256];
     double cumulatedR = 0;
     double cumulatedG = 0;
     double cumulatedB = 0;
     for (int i = 0; i < 256; i++)
     {
         cumulatedR += rawDataR[i];
         cumulatedG += rawDataG[i];
         cumulatedB += rawDataB[i];
         rlum[i] = cumulatedR;
         glum[i] = cumulatedG;
         blum[i] = cumulatedB;
     }
     return (r, g, b) =>
     {
         return new Pixel(Convert.ToByte(rlum[r] * 255), Convert.ToByte(glum[g] * 255), Convert.ToByte(blum[b] * 255));
     };
 }
コード例 #3
0
        public static Func <byte, byte, byte, Pixel> Equalize(PNM pnm)
        {
            double[] rawDataR   = pnm.GetHistogramRed();
            double[] rawDataG   = pnm.GetHistogramGreen();
            double[] rawDataB   = pnm.GetHistogramBlue();
            double[] rlum       = new double[256];
            double[] glum       = new double[256];
            double[] blum       = new double[256];
            double   cumulatedR = 0;
            double   cumulatedG = 0;
            double   cumulatedB = 0;

            for (int i = 0; i < 256; i++)
            {
                cumulatedR += rawDataR[i];
                cumulatedG += rawDataG[i];
                cumulatedB += rawDataB[i];
                rlum[i]     = cumulatedR;
                glum[i]     = cumulatedG;
                blum[i]     = cumulatedB;
            }
            return((r, g, b) =>
            {
                return new Pixel(Convert.ToByte(rlum[r] * 255), Convert.ToByte(glum[g] * 255), Convert.ToByte(blum[b] * 255));
            });
        }
コード例 #4
0
 private static PNM DrawLines(PNM image, IEnumerable <Tuple <Point, Point> > lines)
 {
     // prepare the bitmap
     using (Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb))
     {
         BitmapData data          = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         byte[]     stridedBuffer = Stride(image.raster, image.Width, image.Height);
         Marshal.Copy(stridedBuffer, 0, data.Scan0, stridedBuffer.Length);
         bitmap.UnlockBits(data);
         using (Graphics graphics = Graphics.FromImage(bitmap))
         {
             // draw the lines
             foreach (var tuple in lines)
             {
                 graphics.DrawLine(Pens.Blue, tuple.Item1, tuple.Item2);
             }
         }
         // get raw data
         PNM lineImage = new PNM(image.Width, image.Height);
         data             = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         lineImage.raster = UnStride(data.Scan0, image.Width, image.Height);
         bitmap.UnlockBits(data);
         return(lineImage);
     }
 }
コード例 #5
0
        internal static int[][] HoughVote(PNM image, int maxW)
        {
            // initialize voting board
            int[][] votingBoard = new int[180][];
            for (int i = 0; i < 180; i++)
            {
                votingBoard[i] = new int[maxW * 2];
            }
            int size = image.Width * image.Height;

            for (int i = 0; i < size; i++)
            {
                byte l;
                image.GetPixel(i, out l, out l, out l);
                if (l < 255)
                {
                    continue;
                }
                double centeredX = i % image.Width - (image.Width / 2d);
                double centeredY = (image.Height / 2d) - i / image.Width;
                // pixel is white - vote in all directions
                for (int angle = 0; angle < 180; angle++)
                {
                    double radianAngle = Math.PI * angle / 180d;
                    double w           = centeredX * Math.Cos(radianAngle) + centeredY * Math.Sin(radianAngle);
                    votingBoard[angle][(int)w + maxW]++;
                }
            }
            return(votingBoard);
        }
コード例 #6
0
        // assume k = -0.2 and R = 15x15
        // needs 1 pixel width padding
        public static Pixel Niblack(PNM image, int index, double k = -0.2, int length = 15, int padding = 7)
        {
            byte r, g, b;

            float[] surrounding = new float[length * length];
            int     i           = 0;

            for (int m = 0; m < length; m++)
            {
                for (int n = 0; n < length; n++)
                {
                    image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
                    surrounding[i] = PNM.RGBToLuminosity(r, g, b);
                    i++;
                }
            }
            float  mean      = surrounding.Average();
            double threshold = mean + (k * (mean / surrounding.Length));

            image.GetPixel(index, out r, out g, out b);
            byte luminosity = PNM.RGBToLuminosity(r, g, b);

            if (luminosity < threshold)
            {
                return(Pixel.Black);
            }
            return(Pixel.White);
        }
コード例 #7
0
        public static Func <byte, byte, byte, Pixel> Otsu(PNM image)
        {
            double[] histogram   = image.GetHistogramLuminosity();
            object   compareLock = new object();

            double[] variances = new double[256];
            Parallel.For(0, 256, i =>
            {
                double[] background     = histogram.Take(i).ToArray();
                double[] foreground     = histogram.Skip(i).ToArray();
                double backgroundWeight = background.Sum();
                double foregroundWeight = 1 - backgroundWeight;
                double backgroundMean   = background.Length == 0 ? 0 : background.Select((d, idx) => d * idx).Average();
                double foregroundMean   = foreground.Select((d, idx) => d * idx).Average();
                double variance         = backgroundWeight * foregroundWeight * Math.Pow(foregroundMean - backgroundMean, 2);
                variances[i]            = variance;
            });
            byte   threshold   = 0;
            double maxVariance = Double.NegativeInfinity;

            for (int i = 0; i < 256; i++)
            {
                if (variances[i] > maxVariance)
                {
                    maxVariance = variances[i];
                    threshold   = (byte)i;
                }
            }

            return((r, g, b) => Plain(r, g, b, threshold));
        }
コード例 #8
0
        public static Func<byte, byte, byte, Pixel> Otsu(PNM image)
        {
            double[] histogram = image.GetHistogramLuminosity();
            object compareLock = new object();
            double[] variances = new double[256];
            Parallel.For(0, 256, i =>
            {
                double[] background = histogram.Take(i).ToArray();
                double[] foreground = histogram.Skip(i).ToArray();
                double backgroundWeight = background.Sum();
                double foregroundWeight = 1 - backgroundWeight;
                double backgroundMean = background.Length == 0 ? 0 : background.Select((d, idx) => d * idx).Average();
                double foregroundMean = foreground.Select((d, idx) => d * idx).Average();
                double variance = backgroundWeight * foregroundWeight * Math.Pow(foregroundMean - backgroundMean, 2);
                variances[i] = variance;
            });
            byte threshold = 0;
            double maxVariance = Double.NegativeInfinity;
            for(int i =0; i< 256;i++)
            {
                if (variances[i] > maxVariance)
                {
                    maxVariance = variances[i];
                    threshold = (byte)i;
                }
            }

            return (r, g, b) => Plain(r, g, b, threshold);
        }
コード例 #9
0
        public static Pixel Erosion(PNM image, int index)
        {
            int length = 3;
            int padding = 1;
            byte r = 0, g = 0, b = 0;
            byte tempR, tempG, tempB;
            for (int m = 0; m < length; m++)
            {
                for (int n = 0; n < length; n++)
                {
                    image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out tempR, out tempG, out tempB);
                    // check for "white pixel"
                    if (tempR > 127)
                        r = 255;
                    if (tempG > 127)
                        g = 255;
                    if (tempB > 127)
                        b = 255;
                }

                if (r != 0 && g != 0 && b != 0)
                    break;
            }
            return new Pixel(r, g, b);
        }
コード例 #10
0
ファイル: Blur.cs プロジェクト: vosen/UAM.ImageProcessing
 // assume mask 3x3, image is already 1 pixel padded
 public static Pixel Median(PNM image, int index)
 {
     byte[] workArrayR = new byte[9];
     byte[] workArrayG = new byte[9];
     byte[] workArrayB = new byte[9];
     int length = 3;
     int padding = 1;
     byte r, g, b;
     int i = 0;
     for (int m = 0; m < length; m++)
     {
         for (int n = 0; n < length; n++)
         {
             image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
             workArrayR[i] = r;
             workArrayG[i] = g;
             workArrayB[i] = b;
             i++;
         }
     }
     Array.Sort(workArrayR);
     Array.Sort(workArrayG);
     Array.Sort(workArrayB);
     return new Pixel(workArrayR[4], workArrayG[4], workArrayB[4]);
 }
コード例 #11
0
ファイル: Lines.cs プロジェクト: vosen/UAM.ImageProcessing
 private static PNM DrawLines(PNM image, IEnumerable<Tuple<Point, Point>> lines)
 {
     // prepare the bitmap
     using (Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb))
     {
         BitmapData data = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         byte[] stridedBuffer = Stride(image.raster, image.Width, image.Height);
         Marshal.Copy(stridedBuffer, 0, data.Scan0, stridedBuffer.Length);
         bitmap.UnlockBits(data);
         using (Graphics graphics = Graphics.FromImage(bitmap))
         {
             // draw the lines
             foreach(var tuple in lines)
             {
                 graphics.DrawLine(Pens.Blue, tuple.Item1, tuple.Item2);
             }
         }
         // get raw data
         PNM lineImage = new PNM(image.Width, image.Height);
         data = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         lineImage.raster = UnStride(data.Scan0, image.Width, image.Height);
         bitmap.UnlockBits(data);
         return lineImage;
     }
 }
コード例 #12
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        internal static Pixel ConvoluteWithModule(PNM image, int index, float[] matrix1, float[] matrix2, int length, int padding)
        {
            float sumR1 = 0;
            float sumR2 = 0;
            float sumG1 = 0;
            float sumG2 = 0;
            float sumB1 = 0;
            float sumB2 = 0;
            byte  r, g, b;

            for (int m = 0; m < length; m++)
            {
                for (int n = 0; n < length; n++)
                {
                    image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
                    float coeff1 = matrix1[(m * length) + n];
                    float coeff2 = matrix2[(m * length) + n];
                    sumR1 += r * coeff1;
                    sumR2 += r * coeff2;
                    sumG1 += g * coeff1;
                    sumG2 += g * coeff2;
                    sumB1 += b * coeff1;
                    sumB2 += b * coeff2;
                }
            }
            return(new Pixel(Filter.Coerce(Module(sumR1, sumR2)),
                             Filter.Coerce(Module(sumG1, sumG2)),
                             Filter.Coerce(Module(sumB1, sumB2))));
        }
コード例 #13
0
        public static Pixel Dilation(PNM image, int index)
        {
            int length = 3;
            int padding = 1;
            byte r = 255, g = 255, b = 255;
            byte tempR, tempG, tempB;
            for (int m = 0; m < length; m++)
            {
                for (int n = 0; n < length; n++)
                {
                    image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out tempR, out tempG, out tempB);
                    // check for "black pixel"
                    if (tempR < 128)
                        r = 0;
                    if (tempG < 128)
                        g = 0;
                    if (tempB < 128)
                        b = 0;
                }

                if (r == 0 && g == 0 && b == 0)
                    break;
            }
            return new Pixel(r, g, b);
        }
コード例 #14
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        private static void HysteresisConnect(PNM image, int index, int width, int height, byte lowValue, byte highValue)
        {
            int x = index % width;
            int y = index / height;

            for (int x0 = x - 1; x0 < x + 1; x0++)
            {
                for (int y0 = y - 1; y0 < y + 1; y0++)
                {
                    int currentIndex = (width * y0) + x0;
                    if (!IsPixelOnEdge(currentIndex, width, height))
                    {
                        byte l;
                        image.GetPixel(currentIndex, out l, out l, out l);
                        if (l != 255)
                        {
                            if (l >= lowValue)
                            {
                                image.SetPixel(currentIndex, 255, 255, 255);
                                HysteresisConnect(image, currentIndex, width, height, lowValue, highValue);
                            }
                        }
                    }
                }
            }
        }
コード例 #15
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
 private static Pixel FishEye(PNM image, int index, double maxR)
 {
     byte r,g,b;
     double radius, angle;
     image.ToPolar(index, out radius, out angle);
     image.GetPixel((radius * radius) / maxR, angle, out r, out g, out b);
     return new Pixel(r, g, b);
 }
コード例 #16
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
        private static Pixel FishEye(PNM image, int index, double maxR)
        {
            byte   r, g, b;
            double radius, angle;

            image.ToPolar(index, out radius, out angle);
            image.GetPixel((radius * radius) / maxR, angle, out r, out g, out b);
            return(new Pixel(r, g, b));
        }
コード例 #17
0
        // pixel_intensity >= threshold == white object
        // pixel_intensity < threshold == black background
        public static Pixel Plain(byte r, byte g, byte b, byte threshold)
        {
            byte lum = PNM.RGBToLuminosity(r, g, b);

            if (lum >= threshold)
            {
                return(Pixel.White);
            }
            return(Pixel.Black);
        }
コード例 #18
0
        public static PNM ApplyHoughDetector(this PNM image)
        {
            // greyscale, edge detection, thresholding
            PNM workImage = PNM.Copy(image).ApplyPointProcessing(Color.ToGrayscale)
                            .ApplyGradientEdgesDetection()
                            .ApplyPointProcessing(Thresholding.Entropy(image));
            IEnumerable <Tuple <Point, Point> > lines = GenerateHoughLines(workImage);

            return(DrawLines(image, lines));
        }
コード例 #19
0
 private Bitmap ReadBitmap(string path)
 {
     if (path.EndsWith("pgm"))
     {
         return(PNM.ReadPNM(listdata[pathIndex].filePath) as Bitmap);
     }
     else
     {
         return(new Bitmap(listdata[pathIndex].filePath));
     }
 }
コード例 #20
0
        public static Func <byte, byte, byte, Pixel> Triangle(PNM image)
        {
            double[] histogram = image.GetHistogramLuminosity();
            int      minx = 0, maxx = 0;
            double   minValue = Double.PositiveInfinity, maxValue = Double.NegativeInfinity;

            // find min max
            for (int i = 0; i < 256; i++)
            {
                if (histogram[i] >= maxValue)
                {
                    maxValue = histogram[i];
                    maxx     = i;
                }
                if (histogram[i] < minValue)
                {
                    minValue = histogram[i];
                    minx     = i;
                }
            }
            // find line
            double a = -(maxValue - minValue) / (maxx - minx);
            double b = 1;
            double c = (minx * (maxValue - minValue) / (maxx - minx)) - minValue;

            // find the furthest point
            double distance, maxDistance = Double.NegativeInfinity;
            byte   threshold = 0;
            int    from, to;

            if (minx < maxx)
            {
                from = minx + 1;
                to   = maxx;
            }
            else
            {
                to   = minx + 1;
                from = maxx;
            }
            for (int i = from; i < to; i++)
            {
                distance = PointToLineDistance(a, b, c, i, histogram[i]);
                if (distance >= maxDistance)
                {
                    maxDistance = distance;
                    threshold   = (byte)i;
                }
            }
            return((pr, pg, pb) => Plain(pr, pg, pb, threshold));
        }
コード例 #21
0
ファイル: Distance.cs プロジェクト: vosen/UAM.ImageProcessing
 public static PNM ApplyDistanceTransform(this PNM image)
 {
     PNM binaryImage = image.ApplyCannyDetector();
     int pixelCount = 0;
     double[][] distanceMap = ToInitialDistanceMap(binaryImage, ref pixelCount);
     CalculateDistances(distanceMap, pixelCount);
     PNM distancedImage = new PNM(image.Width, image.Height);
     for(int i=0; i < image.Width * image.Height; i++)
     {
         byte distance = Filter.Coerce(distanceMap[i % image.Width][i / image.Width]);
         distancedImage.SetPixel(i, distance, distance, distance);
     }
     return distancedImage;
 }
コード例 #22
0
        public static Func<byte, byte, byte, Pixel> Triangle(PNM image)
        {
            double[] histogram = image.GetHistogramLuminosity();
            int minx=0, maxx=0;
            double minValue = Double.PositiveInfinity, maxValue = Double.NegativeInfinity;
            // find min max
            for (int i = 0; i < 256; i++)
            {
                if (histogram[i] >= maxValue)
                {
                    maxValue = histogram[i];
                    maxx = i;
                }
                if (histogram[i] < minValue)
                {
                    minValue = histogram[i];
                    minx = i;
                }
            }
            // find line
            double a = -(maxValue - minValue) / (maxx - minx);
            double b = 1;
            double c = (minx * (maxValue - minValue) / (maxx - minx)) - minValue;

            // find the furthest point
            double distance, maxDistance = Double.NegativeInfinity;
            byte threshold = 0;
            int from, to;
            if (minx < maxx)
            {
                from = minx + 1;
                to = maxx;
            }
            else
            {
                to = minx + 1;
                from = maxx;
            }
            for(int i = from; i< to; i++)
            {
                distance = PointToLineDistance(a,b,c,i,histogram[i]);
                if (distance >= maxDistance)
                {
                    maxDistance = distance;
                    threshold = (byte)i;
                }
            }
            return (pr, pg, pb) => Plain(pr, pg, pb, threshold);
        }
コード例 #23
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        private static PNM ApplyHysteresis(byte[] suppressed, int width, int height, double low, double high)
        {
            PNM  image     = new PNM(width, height);
            byte lowValue  = (byte)(low * 255);
            byte highValue = (byte)(high * 255);

            for (int i = 0; i < suppressed.Length; i++)
            {
                if (suppressed[i] >= highValue)
                {
                    image.SetPixel(i, 255, 255, 255);
                    HysteresisConnect(image, i, width, height, lowValue, highValue);
                }
            }
            return(image);
        }
コード例 #24
0
ファイル: Distance.cs プロジェクト: vosen/UAM.ImageProcessing
        public static PNM ApplyDistanceTransform(this PNM image)
        {
            PNM binaryImage = image.ApplyCannyDetector();
            int pixelCount  = 0;

            double[][] distanceMap = ToInitialDistanceMap(binaryImage, ref pixelCount);
            CalculateDistances(distanceMap, pixelCount);
            PNM distancedImage = new PNM(image.Width, image.Height);

            for (int i = 0; i < image.Width * image.Height; i++)
            {
                byte distance = Filter.Coerce(distanceMap[i % image.Width][i / image.Width]);
                distancedImage.SetPixel(i, distance, distance, distance);
            }
            return(distancedImage);
        }
コード例 #25
0
ファイル: Lines.cs プロジェクト: vosen/UAM.ImageProcessing
 // returns list tuples which encode interesection of lines with image edges
 private static IEnumerable<Tuple<Point, Point>> GenerateHoughLines(PNM image)
 {
     double maxR = Math.Sqrt(Math.Pow(image.Width / 2d, 2) + Math.Pow(image.Height / 2d, 2));
     int maxW = (int)Math.Ceiling(maxR);
     int[][] votingBoard = HoughVote(image, maxW);
     // prepare function
     Func<double, double, Tuple<Point, Point>> polarLinesToEdges = (dist, ang) => PolarLineToImageEdges(dist, ang, image.Width, image.Height);
     // pick top 10
     // MADNESS
     return votingBoard
         //.AsParallel()
            .SelectMany((array, angle) => array.Select((count, distance) => new { Angle = angle, Distance = distance, Count = count }))
            .Where(a => a.Count > maxW/2 )
            .Select((a) => new { Distance = a.Distance - maxW, Angle = Math.PI * a.Angle / 180d })
            .Select((a) => polarLinesToEdges(a.Distance, a.Angle))
            .Where(tupl => tupl != null);
 }
コード例 #26
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        public static PNM ApplyCannyDetector(this PNM image)
        {
            PNM workImage = image.ApplyPointProcessing(Color.ToGrayscale)
                            .ApplyConvolutionMatrix(new float[] { 0, 0.01F, 0.02F, 0.01F, 0,
                                                                  0.01F, 0.06F, 0.1F, 0.06F, 0.01F,
                                                                  0.02F, 0.1F, 0.16F, 0.1F, 0.02F,
                                                                  0.01F, 0.06F, 0.1F, 0.06F, 0.01F,
                                                                  0, 0.01F, 0.02F, 0.01F, 0 }, 1, 0);

            Filter.Pad(workImage, 1);
            float[] xraster     = Filter.ApplyConvolutionUnbound(workImage, SobelX, 3).Item1;
            float[] yraster     = Filter.ApplyConvolutionUnbound(workImage, SobelY, 3).Item1;
            var     vectorField = xraster.Zip(yraster, (x, y) => Tuple.Create(Module(x, y), GetOrientation(x, y))).ToArray();

            byte[] suppressed = NonMaximumSuppression(vectorField, image.Width, image.Height);
            return(ApplyHysteresis(suppressed, image.Width, image.Height, 0.05, 0.2));
        }
コード例 #27
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        public static PNM ApplyZeroCrossingDetector(this PNM image)
        {
            // preprare
            PNM workImage = PNM.Copy(image);

            Filter.Pad(workImage, 4);
            // apply loG
            Tuple <float[], float[], float[]> LoGRaster = Filter.ApplyConvolutionUnbound(workImage, LoG, 9);
            PNM returnImage = new PNM(image.Width, image.Height);

            // Apply zero crossing except last row and last column
            Parallel.For(0, image.Height - 1, i =>
            {
                for (int j = 0; j < image.Width - 1; j++)
                {
                    byte r = 0;
                    byte g = 0;
                    byte b = 0;
                    // current index position
                    int position     = i * image.Width + j;
                    float currentR   = LoGRaster.Item1[position];
                    float neighbourR = LoGRaster.Item1[position + image.Width + 1];
                    float currentG   = LoGRaster.Item2[position];
                    float neighbourG = LoGRaster.Item2[position + image.Width + 1];
                    float currentB   = LoGRaster.Item3[position];
                    float neighbourB = LoGRaster.Item3[position + image.Width + 1];
                    if ((currentR * neighbourR) < 0 && (Math.Abs(currentR) < Math.Abs(neighbourR)))
                    {
                        r = 255;
                    }
                    if ((currentG * neighbourG) < 0 && (Math.Abs(currentG) < Math.Abs(neighbourG)))
                    {
                        g = 255;
                    }
                    if ((currentB * neighbourB) < 0 && (Math.Abs(currentB) < Math.Abs(neighbourB)))
                    {
                        b = 255;
                    }

                    returnImage.SetPixel(position, r, g, b);
                }
            });
            return(returnImage);
        }
コード例 #28
0
        // returns list tuples which encode interesection of lines with image edges
        private static IEnumerable <Tuple <Point, Point> > GenerateHoughLines(PNM image)
        {
            double maxR = Math.Sqrt(Math.Pow(image.Width / 2d, 2) + Math.Pow(image.Height / 2d, 2));
            int    maxW = (int)Math.Ceiling(maxR);

            int[][] votingBoard = HoughVote(image, maxW);
            // prepare function
            Func <double, double, Tuple <Point, Point> > polarLinesToEdges = (dist, ang) => PolarLineToImageEdges(dist, ang, image.Width, image.Height);

            // pick top 10
            // MADNESS
            return(votingBoard
                   //.AsParallel()
                   .SelectMany((array, angle) => array.Select((count, distance) => new { Angle = angle, Distance = distance, Count = count }))
                   .Where(a => a.Count > maxW / 2)
                   .Select((a) => new { Distance = a.Distance - maxW, Angle = Math.PI * a.Angle / 180d })
                   .Select((a) => polarLinesToEdges(a.Distance, a.Angle))
                   .Where(tupl => tupl != null));
        }
コード例 #29
0
        private void SetPathToWindow(TrainingData path)
        {
            FilePathValue.Text     = path.filePath.Substring(path.filePath.LastIndexOf('\\') + 1);
            currentData.filePath   = path.filePath;
            CBGender.SelectedValue = path.label;
            Bitmap source;

            if (currentData.filePath.EndsWith("pgm"))
            {
                Bitmap     bmp      = PNM.ReadPNM(currentData.filePath) as Bitmap;
                ColorImage imgDetec = new ColorImage(bmp);
                faces = _genderClassifier.GetFaces(imgDetec);
                foreach (Rectangle face in faces)
                {
                    imgDetec.Draw(face, new Emgu.CV.Structure.Bgr(System.Drawing.Color.AliceBlue), 1);
                }
                TBFaces.Text = string.Format("Faces: {0}", faces.Count);
                source       = imgDetec.ToBitmap();
            }
            else
            {
                BitmapImage bi3 = new BitmapImage();
                bi3.BeginInit();
                bi3.UriSource = new Uri(currentData.filePath, UriKind.Absolute);
                bi3.EndInit();
                ColorImage imgDetec = new ColorImage(bi3.ToBitmap());
                faces = _genderClassifier.GetFaces(imgDetec);
                foreach (Rectangle face in faces)
                {
                    imgDetec.Draw(face, new Emgu.CV.Structure.Bgr(System.Drawing.Color.AliceBlue), 1);
                }
                TBFaces.Text = string.Format("Faces: {0}", faces.Count);
                source       = imgDetec.ToBitmap();
            }
            TBDimen.Text = string.Format("W: {0} H: {1}", (int)source.Width, (int)source.Height);
            if (CBTrainer.IsChecked == true)
            {
                GrayImage img = new GrayImage(source);
                img.Processing();
                source = img.ToBitmap();
            }
            Photo.Source = source.ToBitmapImage();
        }
コード例 #30
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Mirror(PNM image, int index)
 {
     byte r, g, b;
     int x = index % image.Width;
     int y = index / image.Width;
     if (x < image.Width / 2d)
         image.GetPixel((y * image.Width) + (image.Width - x - 1), out r, out g, out b);
     else
         image.GetPixel((y * image.Width) + x, out r, out g, out b);
     /*
     image.ToPolar(index, out radius, out angle);
     double newX = image.Width * angle/Math.PI*2;
     double newY = image.Height * radius / maxR;
     int newIndex = ((int)newX * image.Width) + (int)newY;
     // correct for pixels outside the image
     if(newIndex < 0 || newIndex >= image.Width * image.Height)
         newIndex = index;
     image.GetPixel(radius, angle + radius, out r, out g, out b);
      * */
     return new Pixel(r, g, b);
 }
コード例 #31
0
        // Kapur's method
        public static Func <byte, byte, byte, Pixel> Entropy(PNM image)
        {
            double[] histogram  = image.GetHistogramLuminosity();
            double   entropySum = Double.NegativeInfinity;
            int      threshold  = 0;

            for (int i = 0; i < 256; i++)
            {
                double[] background = histogram.Take(i + 1).ToArray();
                double[] foreground = histogram.Skip(i + 1).ToArray();
                double   sumB       = background.Sum();
                double   sumF       = foreground.Sum();
                double   entropyB   = -background.Select(p =>
                {
                    if (p == 0)
                    {
                        return(0);
                    }
                    return((p / sumB) * Math.Log(p / sumB, 2));
                })
                                      .Sum();
                double entropyF = -foreground.Select(p =>
                {
                    if (p == 0)
                    {
                        return(0);
                    }
                    return((p / (1 - sumB)) * Math.Log(p / (1 - sumB), 2));
                })
                                  .Sum();
                double sum = entropyB + entropyF;
                if (sum > entropySum)
                {
                    entropySum = sum;
                    threshold  = i;
                }
            }

            return((r, g, b) => Plain(r, g, b, (byte)threshold));
        }
コード例 #32
0
        public static Func <byte, byte, byte, Pixel> Stretch(PNM pnm)
        {
            double[] histogramR  = pnm.GetHistogramRed();
            double[] histogramG  = pnm.GetHistogramGreen();
            double[] histogramB  = pnm.GetHistogramBlue();
            double[] cumulativeR = CumulativeHistogram(histogramR);
            double[] cumulativeG = CumulativeHistogram(histogramG);
            double[] cumulativeB = CumulativeHistogram(histogramB);

            Tuple <int, int> rangeR = FindPercentiles(cumulativeR);
            Tuple <int, int> rangeG = FindPercentiles(cumulativeG);
            Tuple <int, int> rangeB = FindPercentiles(cumulativeB);

            int[] LUTR = GetStretchLUT(histogramR, rangeR.Item1, rangeR.Item2);
            int[] LUTG = GetStretchLUT(histogramG, rangeG.Item1, rangeG.Item2);
            int[] LUTB = GetStretchLUT(histogramB, rangeB.Item1, rangeB.Item2);

            return((r, g, b) =>
            {
                return new Pixel(Convert.ToByte(LUTR[r]), Convert.ToByte(LUTG[g]), Convert.ToByte(LUTB[b]));
            });
        }
コード例 #33
0
        public static Func<byte, byte, byte, Pixel> Stretch(PNM pnm)
        {
            double[] histogramR = pnm.GetHistogramRed();
            double[] histogramG = pnm.GetHistogramGreen();
            double[] histogramB = pnm.GetHistogramBlue();
            double[] cumulativeR = CumulativeHistogram(histogramR);
            double[] cumulativeG = CumulativeHistogram(histogramG);
            double[] cumulativeB = CumulativeHistogram(histogramB);

            Tuple<int, int> rangeR = FindPercentiles(cumulativeR);
            Tuple<int, int> rangeG = FindPercentiles(cumulativeG);
            Tuple<int, int> rangeB = FindPercentiles(cumulativeB);

            int[] LUTR = GetStretchLUT(histogramR, rangeR.Item1, rangeR.Item2);
            int[] LUTG = GetStretchLUT(histogramG, rangeG.Item1, rangeG.Item2);
            int[] LUTB = GetStretchLUT(histogramB, rangeB.Item1, rangeB.Item2);

            return (r, g, b) =>
            {
                return new Pixel(Convert.ToByte(LUTR[r]), Convert.ToByte(LUTG[g]), Convert.ToByte(LUTB[b]));
            };
        }
コード例 #34
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
        public static PNM ApplyZeroCrossingDetector(this PNM image)
        {
            // preprare
            PNM workImage = PNM.Copy(image);
            Filter.Pad(workImage, 4);
            // apply loG
            Tuple<float[], float[], float[]> LoGRaster = Filter.ApplyConvolutionUnbound(workImage, LoG, 9);
            PNM returnImage = new PNM(image.Width, image.Height);
            // Apply zero crossing except last row and last column
            Parallel.For(0, image.Height - 1, i =>
            {
                for (int j = 0; j < image.Width - 1; j++)
                {
                    byte r = 0;
                    byte g = 0;
                    byte b = 0;
                    // current index position
                    int position = i * image.Width + j;
                    float currentR = LoGRaster.Item1[position];
                    float neighbourR = LoGRaster.Item1[position + image.Width + 1];
                    float currentG = LoGRaster.Item2[position];
                    float neighbourG = LoGRaster.Item2[position + image.Width + 1];
                    float currentB = LoGRaster.Item3[position];
                    float neighbourB = LoGRaster.Item3[position + image.Width + 1];
                    if ((currentR * neighbourR) < 0 && (Math.Abs(currentR) < Math.Abs(neighbourR)))
                        r = 255;
                    if ((currentG * neighbourG) < 0 && (Math.Abs(currentG) < Math.Abs(neighbourG)))
                        g = 255;
                    if ((currentB * neighbourB) < 0 && (Math.Abs(currentB) < Math.Abs(neighbourB)))
                        b = 255;

                    returnImage.SetPixel(position, r, g, b);
                }
            });
            return returnImage;
        }
コード例 #35
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Oil(PNM image, int index)
 {
     int length = 7;
     int padding = length / 2;
     byte[] rvalues = new byte[length * length];
     byte[] gvalues = new byte[length * length];
     byte[] bvalues = new byte[length * length];
     byte r, g, b;
     int i = 0;
     for (int m = 0; m < length; m++)
     {
         for (int n = 0; n < length; n++)
         {
             image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
             rvalues[i] = r;
             gvalues[i] = g;
             bvalues[i] = b;
             i++;
         }
     }
     return new Pixel(rvalues.GroupBy(p => p).OrderByDescending(gr => gr.Count()).First().Key,
                      gvalues.GroupBy(p => p).OrderByDescending(gr => gr.Count()).First().Key,
                      bvalues.GroupBy(p => p).OrderByDescending(gr => gr.Count()).First().Key);
 }
コード例 #36
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 private static void HysteresisConnect(PNM image, int index, int width, int height, byte lowValue, byte highValue)
 {
     int x = index % width;
     int y = index / height;
     for (int x0 = x - 1; x0 < x + 1; x0++)
     {
         for (int y0 = y - 1; y0 < y + 1; y0++)
         {
             int currentIndex = (width * y0) + x0;
             if (!IsPixelOnEdge(currentIndex, width, height))
             {
                 byte l;
                 image.GetPixel(currentIndex, out l, out l, out l);
                 if (l != 255)
                 {
                     if (l >= lowValue)
                     {
                         image.SetPixel(currentIndex, 255, 255, 255);
                         HysteresisConnect(image, currentIndex, width, height, lowValue, highValue);
                     }
                 }
             }
         }
     }
 }
コード例 #37
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 private static PNM ApplyHysteresis(byte[] suppressed, int width, int height, double low, double high)
 {
     PNM image = new PNM(width, height);
     byte lowValue = (byte)(low*255);
     byte highValue = (byte)(high*255);
     for (int i = 0; i < suppressed.Length; i++)
     {
         if (suppressed[i] >= highValue)
         {
             image.SetPixel(i, 255, 255, 255);
             HysteresisConnect(image, i, width, height, lowValue, highValue);
         }
     }
     return image;
 }
コード例 #38
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Roberts(PNM image, int index)
 {
     return ConvoluteWithModule(image, index, RobertsX, RobertsY, 3, 1);
 }
コード例 #39
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Prewitt(PNM image, int index)
 {
     return ConvoluteWithModule(image, index, PrewittX, PrewittY, 3, 1);
 }
コード例 #40
0
 private static PNM DrawRectangles(PNM image, Tuple<float, float>[][] corners)
 {
     using (Bitmap bitmap = new Bitmap(image.Width, image.Height, PixelFormat.Format24bppRgb))
     {
         BitmapData data = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         byte[] stridedBuffer = Lines.Stride(image.raster, image.Width, image.Height);
         Marshal.Copy(stridedBuffer, 0, data.Scan0, stridedBuffer.Length);
         bitmap.UnlockBits(data);
         using (Graphics graphics = Graphics.FromImage(bitmap))
         {
             // draw the lines
             foreach (var corner in corners)
             {
                 for (int i = 0; i < 4; i++)
                     for (int j = i + 1; j < 4; j++ )
                         graphics.DrawLine(Pens.Blue, corner[i].Item1, corner[i].Item2, corner[j].Item1, corner[j].Item2);
             }
         }
         // get raw data
         PNM rectImage = new PNM(image.Width, image.Height);
         data = bitmap.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
         rectImage.raster = Lines.UnStride(data.Scan0, image.Width, image.Height);
         bitmap.UnlockBits(data);
         return rectImage;
     }
 }
コード例 #41
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Roberts(PNM image, int index)
 {
     return(ConvoluteWithModule(image, index, RobertsX, RobertsY, 3, 1));
 }
コード例 #42
0
ファイル: Color.cs プロジェクト: vosen/UAM.ImageProcessing
        public static Pixel ToGrayscale(byte r, byte g, byte b)
        {
            byte value = PNM.RGBToLuminosity(r, g, b);

            return(new Pixel(value, value, value));
        }
コード例 #43
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
        public static Func <PNM, int, Pixel> GenerateFishEye(PNM image)
        {
            double maxR = Math.Sqrt(Math.Pow(image.Width / 2d, 2) + Math.Pow(image.Height / 2d, 2));

            return((img, idx) => FishEye(img, idx, maxR));
        }
コード例 #44
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Negative(PNM image, int index)
 {
     byte r, g, b;
     image.GetPixel(index, out r, out g, out b);
     return new Pixel((byte)(255 - r), (byte)(255 - g), (byte)(255 - b));
 }
コード例 #45
0
 // assume k = -0.2 and R = 15x15
 // needs 1 pixel width padding
 public static Pixel Niblack(PNM image, int index, double k = -0.2, int length = 15, int padding = 7)
 {
     byte r, g, b;
     float[] surrounding = new float[length * length];
     int i = 0;
     for (int m = 0; m < length; m++)
     {
         for (int n = 0; n < length; n++)
         {
             image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
             surrounding[i] = PNM.RGBToLuminosity(r, g, b);
             i++;
         }
     }
     float mean = surrounding.Average();
     double threshold = mean + (k* (mean / surrounding.Length));
     image.GetPixel(index, out r, out g, out b);
     byte luminosity = PNM.RGBToLuminosity(r,g,b);
     if (luminosity < threshold)
         return Pixel.Black;
     return Pixel.White;
 }
コード例 #46
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Prewitt(PNM image, int index)
 {
     return(ConvoluteWithModule(image, index, PrewittX, PrewittY, 3, 1));
 }
コード例 #47
0
ファイル: Artistic.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Func<PNM,int,Pixel> GenerateFishEye(PNM image)
 {
     double maxR = Math.Sqrt(Math.Pow(image.Width / 2d, 2) + Math.Pow(image.Height / 2d, 2));
     return (img, idx) => FishEye(img, idx, maxR);
 }
コード例 #48
0
        // Kapur's method
        public static Func<byte, byte, byte, Pixel> Entropy(PNM image)
        {
            double[] histogram = image.GetHistogramLuminosity();
            double entropySum = Double.NegativeInfinity;
            int threshold = 0;
            for (int i = 0; i < 256; i++)
            {
                double[]  background = histogram.Take(i + 1).ToArray();
                double[]  foreground = histogram.Skip(i + 1).ToArray();
                double sumB = background.Sum();
                double sumF = foreground.Sum();
                double entropyB = -background.Select(p =>
                    {
                        if(p == 0)
                            return 0;
                        return (p / sumB) * Math.Log(p / sumB, 2);
                    })
                    .Sum();
                double entropyF = -foreground.Select(p => 
                    {
                        if(p == 0)
                            return 0;
                        return (p / (1 - sumB)) * Math.Log(p / (1 - sumB), 2);
                    })
                    .Sum();
                double sum = entropyB + entropyF;
                if (sum > entropySum)
                {
                    entropySum = sum;
                    threshold = i;
                }
            }

            return (r, g, b) => Plain(r, g, b, (byte)threshold);
        }
コード例 #49
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 internal static Pixel ConvoluteWithModule(PNM image, int index, float[] matrix1, float[] matrix2, int length, int padding)
 {
     float sumR1 = 0;
     float sumR2 = 0;
     float sumG1 = 0;
     float sumG2 = 0;
     float sumB1 = 0;
     float sumB2 = 0;
     byte r, g, b;
     for (int m = 0; m < length; m++)
     {
         for (int n = 0; n < length; n++)
         {
             image.GetPixel(index - ((padding - m) * image.Width) - (padding - n), out r, out g, out b);
             float coeff1 = matrix1[(m * length) + n];
             float coeff2 = matrix2[(m * length) + n];
             sumR1 += r * coeff1;
             sumR2 += r * coeff2;
             sumG1 += g * coeff1;
             sumG2 += g * coeff2;
             sumB1 += b * coeff1;
             sumB2 += b * coeff2;
         }
     }
     return new Pixel(Filter.Coerce(Module(sumR1, sumR2)),
                      Filter.Coerce(Module(sumG1, sumG2)),
                      Filter.Coerce(Module(sumB1, sumB2)));
 }
コード例 #50
0
        private static IEnumerable <Tuple <float, float>[]> MaskedHoughVote(PNM image, int center, bool[] mask, int dmax, int dmin, int padding)
        {
            double angleStep    = (Math.PI * 3) / (4d * dmax);
            int    angles       = (int)Math.Floor(Math.PI / angleStep);
            double distanceStep = 3 / 4d;
            int    distances    = (int)Math.Ceiling(dmax / distanceStep);

            int[][] votingBoard = new int[angles][];
            for (int i = 0; i < angles; i++)
            {
                votingBoard[i] = new int[distances];
            }
            int maskSize  = mask.Length;
            int maskIndex = 0;

            for (int i = -dmax / 2; i <= dmax / 2; i++)
            {
                for (int j = -dmax / 2; j <= dmax / 2; j++)
                {
                    if (!mask[maskIndex++])
                    {
                        continue;
                    }
                    int  x         = center % image.Width + i;
                    int  y         = center / image.Width - j;
                    int  realIndex = (y * image.Width) + x;
                    byte l;
                    image.GetPixel(realIndex, out l, out l, out l);
                    if (l < 255)
                    {
                        continue;
                    }
                    for (int angle = 0; angle < angles; angle++)
                    {
                        double radianAngle = angle * angleStep;
                        double w           = i * Math.Cos(radianAngle) + j * Math.Sin(radianAngle);
                        double normalizedW = w + (dmax / 2d);
                        int    steppedW    = (int)(normalizedW / distanceStep);
                        votingBoard[angle][steppedW]++;
                    }
                }
            }
            // votingboard is full - enhance it now
            List <double[]> peaks = EnhanceHoughVotingBoard(votingBoard, dmax, dmin, angleStep, distanceStep);
            List <Tuple <int, int, double> > extendedPeaks = new List <Tuple <int, int, double> >(peaks.Count / 2);

            for (int i = 0; i < peaks.Count; i++)
            {
                for (int j = i + 1; j < peaks.Count; j++)
                {
                    if (Math.Abs(peaks[i][0] - peaks[j][0]) < AngularThreshold &&
                        Math.Abs(peaks[i][1] + peaks[j][1]) < DistanceThreshold &&
                        Math.Abs(peaks[i][2] - peaks[j][2]) < NormalizedThreshold * (peaks[i][2] + peaks[j][2]) / 2)
                    {
                        extendedPeaks.Add(Tuple.Create(i, j, 0.5 * (peaks[i][0] + peaks[j][0])));
                    }
                }
            }
            // extendedPeaks now holds Tuples of (i, j, ak), where i and j are indices of paired peaks and their alpha_k
            List <Tuple <double[][], double[][]> > finalPairs = new List <Tuple <double[][], double[][]> >();

            for (int i = 0; i < extendedPeaks.Count; i++)
            {
                for (int j = i + 1; j < extendedPeaks.Count; j++)
                {
                    if (Math.Abs(Math.Abs(extendedPeaks[i].Item3 - extendedPeaks[j].Item3) - (Math.PI / 2)) < AngularThreshold)
                    {
                        // we got pairs of peak pairs
                        finalPairs.Add(Tuple.Create(new double[][] { peaks[extendedPeaks[i].Item1], peaks[extendedPeaks[i].Item2] }, new double[][] { peaks[extendedPeaks[j].Item1], peaks[extendedPeaks[j].Item2] }));
                    }
                }
            }
            return(finalPairs.Select(pair => new Tuple <double, double>[] {
                PolarLineIntersection(pair.Item1[0][0], pair.Item1[0][1], pair.Item2[0][0], pair.Item2[0][1]),
                PolarLineIntersection(pair.Item1[1][0], pair.Item1[1][1], pair.Item2[0][0], pair.Item2[0][1]),
                PolarLineIntersection(pair.Item1[0][0], pair.Item1[0][1], pair.Item2[1][0], pair.Item2[1][1]),
                PolarLineIntersection(pair.Item1[1][0], pair.Item1[1][1], pair.Item2[1][0], pair.Item2[1][1])
            })
                   .Select(tups => new Tuple <float, float>[] {
                CorrectCoordinates(tups[0].Item1, tups[0].Item2, center, image.Width, image.Height, padding),
                CorrectCoordinates(tups[1].Item1, tups[1].Item2, center, image.Width, image.Height, padding),
                CorrectCoordinates(tups[2].Item1, tups[2].Item2, center, image.Width, image.Height, padding),
                CorrectCoordinates(tups[3].Item1, tups[3].Item2, center, image.Width, image.Height, padding)
            }));
        }
コード例 #51
0
ファイル: Lines.cs プロジェクト: vosen/UAM.ImageProcessing
 internal static int[][] HoughVote(PNM image, int maxW)
 {
     // initialize voting board 
     int[][]  votingBoard = new int[180][];
     for (int i = 0; i < 180; i++)
         votingBoard[i] = new int[maxW * 2];
     int size = image.Width * image.Height;
     for (int i = 0; i < size; i++)
     {
         byte l;
         image.GetPixel(i, out l, out l, out l);
         if (l < 255)
             continue;
         double centeredX = i % image.Width - (image.Width / 2d);
         double centeredY = (image.Height / 2d) - i / image.Width;
         // pixel is white - vote in all directions
         for (int angle = 0; angle < 180; angle++)
         {
             double radianAngle = Math.PI * angle / 180d;
             double w = centeredX * Math.Cos(radianAngle) + centeredY * Math.Sin(radianAngle);
             votingBoard[angle][(int)w + maxW]++;
         }
     }
     return votingBoard;
 }
コード例 #52
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Sobel(PNM image, int index)
 {
     return ConvoluteWithModule(image, index, SobelX, SobelY, 3, 1);
 }
コード例 #53
0
ファイル: Edges.cs プロジェクト: vosen/UAM.ImageProcessing
 public static Pixel Sobel(PNM image, int index)
 {
     return(ConvoluteWithModule(image, index, SobelX, SobelY, 3, 1));
 }
コード例 #54
0
 private static IEnumerable<Tuple<float, float>[]> MaskedHoughVote(PNM image, int center, bool[] mask, int dmax, int dmin, int padding)
 {
     double angleStep = (Math.PI * 3) / (4d * dmax);
     int angles = (int)Math.Floor(Math.PI / angleStep);
     double distanceStep = 3/4d;
     int distances = (int)Math.Ceiling(dmax / distanceStep);
     int[][] votingBoard = new int[angles][];
     for (int i = 0; i < angles; i++)
         votingBoard[i] = new int[distances];
     int maskSize = mask.Length;
     int maskIndex = 0;
     for (int i = -dmax/2; i <= dmax/2; i++)
     {
         for (int j = -dmax / 2; j <= dmax/2; j++)
         {
             if (!mask[maskIndex++])
                 continue;
             int x = center % image.Width + i;
             int y = center / image.Width - j;
             int realIndex = (y * image.Width) + x;
             byte l;
             image.GetPixel(realIndex, out l, out l, out l);
             if (l < 255)
                 continue;
             for (int angle = 0; angle < angles; angle++)
             {
                 double radianAngle = angle * angleStep;
                 double w = i * Math.Cos(radianAngle) + j * Math.Sin(radianAngle);
                 double normalizedW = w + (dmax / 2d);
                 int steppedW = (int)(normalizedW / distanceStep);
                 votingBoard[angle][steppedW]++;
             }
         }
     }
     // votingboard is full - enhance it now
     List<double[]> peaks = EnhanceHoughVotingBoard(votingBoard, dmax, dmin, angleStep, distanceStep);
     List<Tuple<int, int, double>> extendedPeaks = new List<Tuple<int, int, double>>(peaks.Count / 2);
     for (int i = 0; i < peaks.Count; i++)
     {
         for (int j = i + 1; j < peaks.Count; j++)
         {
             if (Math.Abs(peaks[i][0] - peaks[j][0]) < AngularThreshold
                && Math.Abs(peaks[i][1] + peaks[j][1]) < DistanceThreshold
                && Math.Abs(peaks[i][2] - peaks[j][2]) < NormalizedThreshold * (peaks[i][2] + peaks[j][2]) / 2)
             extendedPeaks.Add(Tuple.Create(i, j, 0.5 * (peaks[i][0] + peaks[j][0])));
         }
     }
     // extendedPeaks now holds Tuples of (i, j, ak), where i and j are indices of paired peaks and their alpha_k
     List<Tuple<double[][], double[][]>> finalPairs = new List<Tuple<double[][], double[][]>>();
     for (int i = 0; i < extendedPeaks.Count; i++)
     {
         for (int j = i + 1; j < extendedPeaks.Count; j++)
         {
             if (Math.Abs(Math.Abs(extendedPeaks[i].Item3 - extendedPeaks[j].Item3) - (Math.PI / 2)) < AngularThreshold)
                 // we got pairs of peak pairs
                 finalPairs.Add(Tuple.Create( new double[][] {peaks[extendedPeaks[i].Item1], peaks[extendedPeaks[i].Item2] },  new double[][] { peaks[extendedPeaks[j].Item1], peaks[extendedPeaks[j].Item2] }));
         }
     }
     return finalPairs.Select(pair => new Tuple<double, double>[] { 
             PolarLineIntersection(pair.Item1[0][0], pair.Item1[0][1], pair.Item2[0][0], pair.Item2[0][1]),
             PolarLineIntersection(pair.Item1[1][0], pair.Item1[1][1], pair.Item2[0][0], pair.Item2[0][1]),
             PolarLineIntersection(pair.Item1[0][0], pair.Item1[0][1], pair.Item2[1][0], pair.Item2[1][1]),
             PolarLineIntersection(pair.Item1[1][0], pair.Item1[1][1], pair.Item2[1][0], pair.Item2[1][1])})
         .Select(tups => new Tuple<float, float>[]{
             CorrectCoordinates(tups[0].Item1, tups[0].Item2, center, image.Width, image.Height, padding),
             CorrectCoordinates(tups[1].Item1, tups[1].Item2, center, image.Width, image.Height, padding),
             CorrectCoordinates(tups[2].Item1, tups[2].Item2, center, image.Width, image.Height, padding),
             CorrectCoordinates(tups[3].Item1, tups[3].Item2, center, image.Width, image.Height, padding)});
 }