Пример #1
0
        /// <summary>
        /// Release all managed resources associated with this background model.
        /// </summary>
        protected override void ReleaseManagedResources()
        {
            if (_fgMask != null)
            {
                _fgMask.Dispose();
            }
            if (_bgMask != null)
            {
                _bgMask.Dispose();
            }

            base.ReleaseManagedResources();
        }
Пример #2
0
        void stitch(OneSidePcb oneSidePcb)
        {
            double or_hl = 0.23;  // lower bound for horizontal overlap ratio
            double or_hu = 0.24;  // upper
            double or_vl = 0.065; // vertical
            double or_vu = 0.08;
            double dr_hu = 0.01;  // upper bound for horizontal drift ratio
            double dr_vu = 0.01;  //

            Bitmap bitmap = oneSidePcb.bitmaps.Dequeue();

            Emgu.CV.Image <Bgr, Byte> currentFrame = new Emgu.CV.Image <Bgr, Byte>(bitmap);
            Mat imgOld = new Mat();

            CvInvoke.BitwiseAnd(currentFrame, currentFrame, imgOld);
            int edge = 3;
            Mat img  = new Mat(imgOld, new Rectangle(new Point(edge, edge), new Size(imgOld.Width - edge * 2, imgOld.Height - edge * 2)));

            //第一行
            if (oneSidePcb.currentRow == 0)
            {
                if (oneSidePcb.currentCol == 0)
                {
                    #region 判断s型还是z字形
                    if (oneSidePcb.zTrajectory) //Z形
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.left;
                        int x       = Convert.ToInt32(img.Cols * (oneSidePcb.allCols - 1) * dr_hu);
                        int y       = Convert.ToInt32(img.Rows * (oneSidePcb.allRows - 1) * dr_vu);
                        int dstRows = Convert.ToInt32(img.Rows * (oneSidePcb.allRows + (oneSidePcb.allRows - 1) * (dr_vu * 2 - or_vl)));
                        int dstCols = Convert.ToInt32(img.Cols * (oneSidePcb.allCols + (oneSidePcb.allCols - 1) * (dr_hu * 2 - or_hl)));
                        oneSidePcb.roi = new Rectangle(x, y, img.Cols, img.Rows);
                        oneSidePcb.dst = new Mat(dstRows, dstCols, img.Depth, 3); // 第一张图不要0,0 最好留一些像素
                    }
                    else // S型
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.right;

                        int dstRows = Convert.ToInt32(img.Rows * (oneSidePcb.allRows + (oneSidePcb.allRows - 1) * (dr_vu * 2 - or_vl)));
                        int dstCols = Convert.ToInt32(img.Cols * (oneSidePcb.allCols + (oneSidePcb.allCols - 1) * (dr_hu * 2 - or_hl)));
                        int x       = Convert.ToInt32((dstCols - img.Cols) * (1 - dr_hu));
                        int y       = Convert.ToInt32(img.Rows * (oneSidePcb.allRows - 1) * dr_vu);
                        oneSidePcb.roi = new Rectangle(x, y, img.Cols, img.Rows);
                        oneSidePcb.dst = new Mat(dstRows, dstCols, img.Depth, 3); // 第一张图不要0,0 最好留一些像素
                    }
                    #endregion
                }
                else
                {
                    AoiAi.stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu));
                }
                //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                AoiAi.copy_to(oneSidePcb.dst.Ptr, img.Ptr, oneSidePcb.roi);
                //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                img.Dispose();
                currentFrame.Dispose();
                bitmap.Dispose();
                oneSidePcb.currentCol++;
                if (oneSidePcb.currentCol >= oneSidePcb.allCols)
                {
                    oneSidePcb.currentCol = 0;
                    oneSidePcb.currentRow++;
                    if (oneSidePcb.trajectorySide == (int)AoiAi.side.left)
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.right;
                    }
                    else if (oneSidePcb.trajectorySide == (int)AoiAi.side.right)
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.left;
                    }
                }
                //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\row1.jpg");
            }
            else // 其他行
            {
                if (Convert.ToBoolean(oneSidePcb.currentRow % 2)) //偶行
                {
                    if (oneSidePcb.currentCol == 0)
                    {
                        AoiAi.stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, (int)AoiAi.side.up, Convert.ToInt32(img.Cols * or_vl), Convert.ToInt32(img.Cols * or_vu), Convert.ToInt32(img.Rows * dr_hu));
                        //oneSidePcb.roi = oneSidePcb.roi0;
                    }
                    else
                    {
                        AoiAi.stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu), (int)AoiAi.side.up, Convert.ToInt32(img.Rows * or_vl), Convert.ToInt32(img.Rows * or_vu), Convert.ToInt32(img.Cols * dr_hu));
                    }
                }
                else
                {
                    if (oneSidePcb.currentCol == 0)
                    {
                        AoiAi.stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, (int)AoiAi.side.up, Convert.ToInt32(img.Cols * or_vl), Convert.ToInt32(img.Cols * or_vu), Convert.ToInt32(img.Rows * dr_hu));
                        //oneSidePcb.roi = oneSidePcb.roi0;
                    }
                    else
                    {
                        AoiAi.stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu), (int)AoiAi.side.up, Convert.ToInt32(img.Rows * or_vl), Convert.ToInt32(img.Rows * or_vu), Convert.ToInt32(img.Cols * dr_hu));
                    }
                }
                //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                AoiAi.copy_to(oneSidePcb.dst.Ptr, img.Ptr, oneSidePcb.roi);
                img.Dispose();
                currentFrame.Dispose();
                bitmap.Dispose();
                oneSidePcb.currentCol++;
                if (oneSidePcb.currentRow >= oneSidePcb.allRows - 1 && oneSidePcb.currentCol >= oneSidePcb.allCols)
                {
                    sw.Stop();
                    TimeSpan ts = sw.Elapsed;
                    Console.WriteLine("DateTime costed for Shuffle function is: {0}ms", ts.TotalMilliseconds);
                    this.BeginInvoke(done);
                    if (oneSidePcb.zTrajectory)
                    {
                        oneSidePcb.dst.Save(Path.Combine(savePath, "Front.jpg"));
                    }
                    else
                    {
                        oneSidePcb.dst.Save(Path.Combine(savePath, "Back.jpg"));
                    }
                }
                else if (oneSidePcb.currentCol >= oneSidePcb.allCols)
                {
                    oneSidePcb.currentCol = 0;
                    oneSidePcb.currentRow++;
                    if (oneSidePcb.trajectorySide == (int)AoiAi.side.left)
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.right;
                    }
                    else if (oneSidePcb.trajectorySide == (int)AoiAi.side.right)
                    {
                        oneSidePcb.trajectorySide = (int)AoiAi.side.left;
                    }
                }
            }
        }
Пример #3
0
        public void ComputeFeatures(Segment s)
        {
            //Add the relative size
            NamedFeature f = new NamedFeature("RelativeSize");

            f.values.Add(s.points.Count() / (double)(imageWidth * imageHeight));
            s.features.Add(f);

            // Relative centroid
            PointF c = NormalizedCentroid(s);

            s.features.Add(new NamedFeature("RelativeCentroid", new List <double> {
                c.X, c.Y
            }));

            // One interior point
            PointF np = RandomNormalizedInteriorPoint(s);

            s.features.Add(new NamedFeature("OneInteriorPoint", new List <double> {
                np.X, np.Y
            }));

            //Radial distance
            s.features.Add(new NamedFeature("RadialDistance", new List <double> {
                Math.Sqrt(c.X * c.X + c.Y * c.Y)
            }));


            //Normalized Discrete Compactness http://www.m-hikari.com/imf-password2009/25-28-2009/bribiescaIMF25-28-2009.pdf
            //Find the segment id
            Point sp   = s.points.First();
            int   sidx = assignments[sp.X, sp.Y];

            //count number of perimeter edges
            int perimeter = 0;

            foreach (Point p in s.points)
            {
                for (int i = -1; i <= 1; i++)
                {
                    for (int j = -1; j <= 1; j++)
                    {
                        if (Math.Abs(i) == Math.Abs(j))
                        {
                            continue;
                        }
                        if (Util.InBounds(p.X + i, p.Y + j, imageWidth, imageHeight) && assignments[p.X + i, p.Y + j] != sidx)
                        {
                            perimeter++;
                        }
                        else if (!Util.InBounds(p.X + i, p.Y + j, imageWidth, imageHeight)) //edge pixels should be considered perimeter too
                        {
                            perimeter++;
                        }
                    }
                }
            }
            int    n     = s.points.Count();
            double CD    = (4.0 * n - perimeter) / 2;
            double CDmin = n - 1;
            double CDmax = (4 * n - 4 * Math.Sqrt(n)) / 2;
            double CDN   = (CD - CDmin) / Math.Max(1, (CDmax - CDmin));

            s.features.Add(new NamedFeature("NormalizedDiscreteCompactness", new List <double> {
                CDN
            }));


            //Add elongation (width/length normalized between 0-square to 1-long http://hal.archives-ouvertes.fr/docs/00/44/60/37/PDF/ARS-Journal-SurveyPatternRecognition.pdf
            PointF[] points = s.points.Select <Point, PointF>(p => new PointF(p.X, p.Y)).ToArray <PointF>();
            Emgu.CV.Structure.MCvBox2D box = Emgu.CV.PointCollection.MinAreaRect(points);

            PointF[] vertices   = box.GetVertices();
            double   elongation = 1 - Math.Min(box.size.Width + 1, box.size.Height + 1) / Math.Max(box.size.Width + 1, box.size.Height + 1);

            s.features.Add(new NamedFeature("Elongation", new List <double> {
                elongation
            }));


            //Add Hu shape moments, invariant to translation, scale, and rotation (not sure what each measure refers to intuitively though, or if there is an intuitive analog)
            //They may also do badly on noisy data however. See: http://hal.archives-ouvertes.fr/docs/00/44/60/37/PDF/ARS-Journal-SurveyPatternRecognition.pdf (called Invariant Moments)

            Bitmap   regionBitmap = new Bitmap(imageWidth, imageHeight);
            Graphics g            = Graphics.FromImage(regionBitmap);

            g.FillRectangle(new SolidBrush(Color.Black), 0, 0, imageWidth, imageHeight);
            foreach (Point p in s.points)
            {
                regionBitmap.SetPixel(p.X, p.Y, Color.White);
            }

            Emgu.CV.Image <Gray, byte> region = new Emgu.CV.Image <Gray, byte>(regionBitmap);

            MCvMoments   moment = region.GetMoments(true);
            MCvHuMoments hu     = moment.GetHuMoment();

            s.features.Add(new NamedFeature("HuMoments", new List <double> {
                hu.hu1, hu.hu2, hu.hu3, hu.hu4, hu.hu5, hu.hu6, hu.hu7
            }));
            region.Dispose();
            regionBitmap.Dispose();
        }
Пример #4
0
        /// <summary>
        /// 拼图主函数
        /// </summary>
        /// <param name="oneSidePcb"></param>
        public static void StitchMain(OneStitchSidePcb oneSidePcb, Odin.StitchCallBack stitchCallBack)
        {
            bool       needSave = false;
            double     or_hl    = oneSidePcb.or_hl;
            double     or_hu    = oneSidePcb.or_hu;
            double     or_vl    = oneSidePcb.or_vl;
            double     or_vu    = oneSidePcb.or_vu;
            double     dr_hu    = oneSidePcb.dr_hu;
            double     dr_vu    = oneSidePcb.dr_vu;
            BitmapInfo bitmapInfo;

            lock (oneSidePcb.bitmaps)
            {
                bitmapInfo = oneSidePcb.bitmaps.Dequeue();
                Emgu.CV.Image <Bgr, Byte> currentFrame = new Emgu.CV.Image <Bgr, Byte>(bitmapInfo.bitmap);
                Mat imgOld = new Mat();
                CvInvoke.BitwiseAnd(currentFrame, currentFrame, imgOld);
                int edge = 3;
                Mat img  = new Mat(imgOld, new Rectangle(new Point(edge, edge), new Size(imgOld.Width - edge * 2, imgOld.Height - edge * 2)));

                //第一行
                if (oneSidePcb.currentRow == 0)
                {
                    if (oneSidePcb.currentCol == 0)
                    {
                        #region 判断s型还是z字形
                        if (oneSidePcb.zTrajectory) //Z形
                        {
                            oneSidePcb.trajectorySide = (int)side.left;
                            int x       = Convert.ToInt32(img.Cols * (oneSidePcb.allCols - 1) * dr_hu);
                            int y       = Convert.ToInt32(img.Rows * (oneSidePcb.allRows - 1) * dr_vu);
                            int dstRows = Convert.ToInt32(img.Rows * (oneSidePcb.allRows + (oneSidePcb.allRows - 1) * (dr_vu * 2 - or_vl)));
                            int dstCols = Convert.ToInt32(img.Cols * (oneSidePcb.allCols + (oneSidePcb.allCols - 1) * (dr_hu * 2 - or_hl)));
                            oneSidePcb.roi = new Rectangle(x, y, img.Cols, img.Rows);
                            oneSidePcb.dst = new Mat(dstRows, dstCols, img.Depth, 3); // 第一张图不要0,0 最好留一些像素
                        }
                        else // S型
                        {
                            oneSidePcb.trajectorySide = (int)side.right;

                            int dstRows = Convert.ToInt32(img.Rows * (oneSidePcb.allRows + (oneSidePcb.allRows - 1) * (dr_vu * 2 - or_vl)));
                            int dstCols = Convert.ToInt32(img.Cols * (oneSidePcb.allCols + (oneSidePcb.allCols - 1) * (dr_hu * 2 - or_hl)));
                            int x       = Convert.ToInt32((dstCols - img.Cols) * (1 - dr_hu));
                            int y       = Convert.ToInt32(img.Rows * (oneSidePcb.allRows - 1) * dr_vu);
                            oneSidePcb.roi = new Rectangle(x, y, img.Cols, img.Rows);
                            oneSidePcb.dst = new Mat(dstRows, dstCols, img.Depth, 3); // 第一张图不要0,0 最好留一些像素
                        }
                        #endregion
                    }
                    else
                    {
                        stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu));
                    }
                    //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                    copy_to(oneSidePcb.dst.Ptr, img.Ptr, oneSidePcb.roi);

                    //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");

                    oneSidePcb.currentCol++;
                    if (oneSidePcb.currentCol >= oneSidePcb.allCols)
                    {
                        oneSidePcb.currentCol = 0;
                        oneSidePcb.currentRow++;
                        if (oneSidePcb.trajectorySide == (int)side.left)
                        {
                            oneSidePcb.trajectorySide = (int)side.right;
                        }
                        else if (oneSidePcb.trajectorySide == (int)side.right)
                        {
                            oneSidePcb.trajectorySide = (int)side.left;
                        }
                    }
                    //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\row1.jpg");
                }
                else // 其他行
                {
                    if (Convert.ToBoolean(oneSidePcb.currentRow % 2)) //偶行
                    {
                        if (oneSidePcb.currentCol == 0)
                        {
                            stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, (int)side.up, Convert.ToInt32(img.Cols * or_vl), Convert.ToInt32(img.Cols * or_vu), Convert.ToInt32(img.Rows * dr_hu));
                            //oneSidePcb.roi = oneSidePcb.roi0;
                        }
                        else
                        {
                            stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu), (int)side.up, Convert.ToInt32(img.Rows * or_vl), Convert.ToInt32(img.Rows * or_vu), Convert.ToInt32(img.Cols * dr_hu));
                        }
                    }
                    else
                    {
                        if (oneSidePcb.currentCol == 0)
                        {
                            stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, (int)side.up, Convert.ToInt32(img.Cols * or_vl), Convert.ToInt32(img.Cols * or_vu), Convert.ToInt32(img.Rows * dr_hu));
                            //oneSidePcb.roi = oneSidePcb.roi0;
                        }
                        else
                        {
                            stitchv2(oneSidePcb.dst.Ptr, oneSidePcb.roi, img.Ptr, ref oneSidePcb.roi, oneSidePcb.trajectorySide, Convert.ToInt32(img.Cols * or_hl), Convert.ToInt32(img.Cols * or_hu), Convert.ToInt32(img.Rows * dr_vu), (int)side.up, Convert.ToInt32(img.Rows * or_vl), Convert.ToInt32(img.Rows * or_vu), Convert.ToInt32(img.Cols * dr_hu));
                        }
                    }
                    //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                    copy_to(oneSidePcb.dst.Ptr, img.Ptr, oneSidePcb.roi);
                    //oneSidePcb.dst.Save(@"C:\Users\Administrator\Desktop\suomi-test-img\" + oneSidePcb.currentRow + "-" + oneSidePcb.currentCol + ".jpg");
                    oneSidePcb.currentCol++;
                    if (oneSidePcb.currentRow >= oneSidePcb.allRows - 1 && oneSidePcb.currentCol >= oneSidePcb.allCols)
                    {
                        needSave = true;
                    }
                    else if (oneSidePcb.currentCol >= oneSidePcb.allCols)
                    {
                        oneSidePcb.currentCol = 0;
                        oneSidePcb.currentRow++;
                        if (oneSidePcb.trajectorySide == (int)side.left)
                        {
                            oneSidePcb.trajectorySide = (int)side.right;
                        }
                        else if (oneSidePcb.trajectorySide == (int)side.right)
                        {
                            oneSidePcb.trajectorySide = (int)side.left;
                        }
                    }
                }
                #region 实时更新采集框

                stitchCallBack(false, oneSidePcb, bitmapInfo, new RectangleF((float)(oneSidePcb.roi.Location.X * 0.25),
                                                                             (float)(oneSidePcb.roi.Location.Y * 0.25),
                                                                             (float)(oneSidePcb.roi.Size.Width * 0.25),
                                                                             (float)(oneSidePcb.roi.Size.Height * 0.25)));
                #endregion

                #region 释放资源
                img.Dispose();
                currentFrame.Dispose();
                //bitmap.Dispose();
                #endregion

                if (needSave) // 加这里主要是为了优化workingForm.imgBoxWorking最后一个框的显示问题
                {
                    stitchCallBack(true, oneSidePcb, bitmapInfo, new RectangleF());
                    Mat smallmat = new Mat();
                    CvInvoke.Resize(oneSidePcb.dst, smallmat, new Size(Convert.ToInt32(oneSidePcb.dst.Cols * oneSidePcb.scale), Convert.ToInt32(oneSidePcb.dst.Rows * oneSidePcb.scale)));
                    if (oneSidePcb.zTrajectory)
                    {
                        string saveFile = Path.Combine(oneSidePcb.savePath, "front.jpg");
                        smallmat.Save(saveFile);
                        //oneSidePcb.dst.Save(saveFile + "big.jpg");
                        Ftp.UpLoadFile(saveFile, Ftp.ftpPath + oneSidePcb.pcbId + "/front.jpg");
                    }
                    else
                    {
                        string saveFile = Path.Combine(oneSidePcb.savePath, "back.jpg");
                        smallmat.Save(saveFile);
                        //oneSidePcb.dst.Save(saveFile + "big.jpg");
                        Ftp.UpLoadFile(saveFile, Ftp.ftpPath + oneSidePcb.pcbId + "/back.jpg");
                    }
                    smallmat.Dispose();
                    oneSidePcb.dst.Dispose();
                }
            }
        }
Пример #5
0
        public void ComputeFeatures(Segment s)
        {
            //Add the relative size
            NamedFeature f = new NamedFeature("RelativeSize");
            f.values.Add(s.points.Count() / (double)(imageWidth * imageHeight));
            s.features.Add(f);

            // Relative centroid
            PointF c = NormalizedCentroid(s);
            s.features.Add(new NamedFeature("RelativeCentroid", new List<double>{c.X, c.Y}));

            // One interior point
            PointF np = RandomNormalizedInteriorPoint(s);
            s.features.Add(new NamedFeature("OneInteriorPoint", new List<double> { np.X, np.Y }));

            //Radial distance
            s.features.Add(new NamedFeature("RadialDistance", new List<double>{Math.Sqrt(c.X*c.X+c.Y*c.Y)}));

            //Normalized Discrete Compactness http://www.m-hikari.com/imf-password2009/25-28-2009/bribiescaIMF25-28-2009.pdf
            //Find the segment id
            Point sp = s.points.First();
            int sidx = assignments[sp.X, sp.Y];

            //count number of perimeter edges
            int perimeter = 0;
            foreach (Point p in s.points)
            {
                for (int i = -1; i <= 1; i++)
                {
                    for (int j = -1; j <= 1; j++)
                    {
                        if (Math.Abs(i) == Math.Abs(j))
                            continue;
                        if (Util.InBounds(p.X + i, p.Y + j, imageWidth, imageHeight) && assignments[p.X + i, p.Y + j] != sidx)
                            perimeter++;
                        else if (!Util.InBounds(p.X + i, p.Y + j, imageWidth, imageHeight)) //edge pixels should be considered perimeter too
                            perimeter++;
                    }
                }
            }
            int n = s.points.Count();
            double CD = (4.0 * n - perimeter) / 2;
            double CDmin = n - 1;
            double CDmax = (4 * n - 4 * Math.Sqrt(n)) / 2;
            double CDN = (CD - CDmin) / Math.Max(1,(CDmax - CDmin));
            s.features.Add(new NamedFeature("NormalizedDiscreteCompactness", new List<double> { CDN }));

            //Add elongation (width/length normalized between 0-square to 1-long http://hal.archives-ouvertes.fr/docs/00/44/60/37/PDF/ARS-Journal-SurveyPatternRecognition.pdf
            PointF[] points = s.points.Select<Point, PointF>(p => new PointF(p.X, p.Y)).ToArray<PointF>();
            Emgu.CV.Structure.MCvBox2D box = Emgu.CV.PointCollection.MinAreaRect(points);

            PointF[] vertices = box.GetVertices();
            double elongation = 1 - Math.Min(box.size.Width + 1, box.size.Height + 1) / Math.Max(box.size.Width + 1, box.size.Height + 1);
            s.features.Add(new NamedFeature("Elongation", new List<double>{elongation}));

            //Add Hu shape moments, invariant to translation, scale, and rotation (not sure what each measure refers to intuitively though, or if there is an intuitive analog)
            //They may also do badly on noisy data however. See: http://hal.archives-ouvertes.fr/docs/00/44/60/37/PDF/ARS-Journal-SurveyPatternRecognition.pdf (called Invariant Moments)

            Bitmap regionBitmap = new Bitmap(imageWidth, imageHeight);
            Graphics g = Graphics.FromImage(regionBitmap);
            g.FillRectangle(new SolidBrush(Color.Black), 0, 0, imageWidth, imageHeight);
            foreach (Point p in s.points)
            {
                regionBitmap.SetPixel(p.X, p.Y, Color.White);
            }

            Emgu.CV.Image<Gray, byte> region = new Emgu.CV.Image<Gray, byte>(regionBitmap);

            MCvMoments moment = region.GetMoments(true);
            MCvHuMoments hu = moment.GetHuMoment();
            s.features.Add(new NamedFeature("HuMoments", new List<double> {hu.hu1, hu.hu2, hu.hu3,hu.hu4,hu.hu5, hu.hu6, hu.hu7 }));
            region.Dispose();
            regionBitmap.Dispose();
        }