C# 类似PS的魔棒工具(1)

来源:互联网 发布:在线字体生成网站源码 编辑:程序博客网 时间:2024/05/21 08:14

最近一段时间在开发一个画图软件,其中需要实现魔棒功能。网上浏览了一圈,没有找到。苦思之后,终于开窍了:。思路是:先用漫水填充算法, 获得一张连通区域的二值图。然后对这幅图进行边缘检测,获取边缘。如果使用emgucv或者opencv,可以直接使用函数floodFill()获得区域,再函数Canny()与FindContours()函数获得边界。

1.漫水填充

这里我不适用emgucv的方法,使用的是一个网友算法,改了一点点。

public Bitmap FloodFill(Bitmap src, Point location, Color fillColor, int threshould)        {            try            {                Bitmap srcbmp = src;                Bitmap dstbmp = new Bitmap(src.Width,src.Height);                int w = srcbmp.Width;                int h = srcbmp.Height;                Stack<Point> fillPoints = new Stack<Point>(w * h);                System.Drawing.Imaging.BitmapData bmpData = srcbmp.LockBits(new Rectangle(0, 0, srcbmp.Width, srcbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);                System.Drawing.Imaging.BitmapData dstbmpData = dstbmp.LockBits(new Rectangle(0, 0, dstbmp.Width, dstbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format24bppRgb);                           IntPtr ptr = bmpData.Scan0;                int stride = bmpData.Stride;                int bytes = bmpData.Stride * srcbmp.Height;                byte[] grayValues = new byte[bytes];                System.Runtime.InteropServices.Marshal.Copy(ptr, grayValues, 0, bytes);                Color backColor = Color.FromArgb(grayValues[location.X * 3 + 2 + location.Y * stride], grayValues[location.X * 3 + 1 + location.Y * stride], grayValues[location.X * 3 + location.Y * stride]);                IntPtr dstptr = dstbmpData.Scan0;                byte[] temp = new byte[bytes];                System.Runtime.InteropServices.Marshal.Copy(dstptr, temp, 0, bytes);                int gray = (int)((backColor.R + backColor.G + backColor.B) / 3);                if (location.X < 0 || location.X >= w || location.Y < 0 || location.Y >= h) return null;                fillPoints.Push(new Point(location.X, location.Y));                int[,] mask = new int[w, h];                                while (fillPoints.Count > 0)                {                                        Point p = fillPoints.Pop();                    mask[p.X, p.Y] = 1;                    temp[3 * p.X + p.Y * stride] = (byte)fillColor.B;                    temp[3 * p.X + 1 + p.Y * stride] = (byte)fillColor.G;                    temp[3 * p.X + 2 + p.Y * stride] = (byte)fillColor.R;                    if (p.X > 0 && (Math.Abs(gray - (int)((grayValues[3 * (p.X - 1) + p.Y * stride] + grayValues[3 * (p.X - 1) + 1 + p.Y * stride] + grayValues[3 * (p.X - 1) + 2 + p.Y * stride]) / 3)) < threshould) && (mask[p.X - 1, p.Y] != 1))                    {                        temp[3 * (p.X - 1) + p.Y * stride] = (byte)fillColor.B;                        temp[3 * (p.X - 1) + 1 + p.Y * stride] = (byte)fillColor.G;                        temp[3 * (p.X - 1) + 2 + p.Y * stride] = (byte)fillColor.R;                        fillPoints.Push(new Point(p.X - 1, p.Y));                        mask[p.X - 1, p.Y] = 1;                    }                    if (p.X < w - 1 && (Math.Abs(gray - (int)((grayValues[3 * (p.X + 1) + p.Y * stride] + grayValues[3 * (p.X + 1) + 1 + p.Y * stride] + grayValues[3 * (p.X + 1) + 2 + p.Y * stride]) / 3)) < threshould) && (mask[p.X + 1, p.Y] != 1))                    {                        temp[3 * (p.X + 1) + p.Y * stride] = (byte)fillColor.B;                        temp[3 * (p.X + 1) + 1 + p.Y * stride] = (byte)fillColor.G;                        temp[3 * (p.X + 1) + 2 + p.Y * stride] = (byte)fillColor.R;                        fillPoints.Push(new Point(p.X + 1, p.Y));                        mask[p.X + 1, p.Y] = 1;                    }                    if (p.Y > 0 && (Math.Abs(gray - (int)((grayValues[3 * p.X + (p.Y - 1) * stride] + grayValues[3 * p.X + 1 + (p.Y - 1) * stride] + grayValues[3 * p.X + 2 + (p.Y - 1) * stride]) / 3)) < threshould) && (mask[p.X, p.Y - 1] != 1))                    {                        temp[3 * p.X + (p.Y - 1) * stride] = (byte)fillColor.B;                        temp[3 * p.X + 1 + (p.Y - 1) * stride] = (byte)fillColor.G;                        temp[3 * p.X + 2 + (p.Y - 1) * stride] = (byte)fillColor.R;                        fillPoints.Push(new Point(p.X, p.Y - 1));                        mask[p.X, p.Y - 1] = 1;                    }                    if (p.Y < h - 1 && (Math.Abs(gray - (int)((grayValues[3 * p.X + (p.Y + 1) * stride] + grayValues[3 * p.X + 1 + (p.Y + 1) * stride] + grayValues[3 * p.X + 2 + (p.Y + 1) * stride]) / 3)) < threshould) && (mask[p.X, p.Y + 1] != 1))                    {                        temp[3 * p.X + (p.Y + 1) * stride] = (byte)fillColor.B;                        temp[3 * p.X + 1 + (p.Y + 1) * stride] = (byte)fillColor.G;                        temp[3 * p.X + 2 + (p.Y + 1) * stride] = (byte)fillColor.R;                        fillPoints.Push(new Point(p.X, p.Y + 1));                        mask[p.X, p.Y + 1] = 1;                    }                }                fillPoints.Clear();                System.Runtime.InteropServices.Marshal.Copy(temp, 0, dstptr, bytes);                srcbmp.UnlockBits(bmpData);                dstbmp.UnlockBits(dstbmpData);                return dstbmp;            }            catch (Exception exp)            {                MessageBox.Show(exp.Message);                return null;            }        }
实际上这个函数时有缺陷的,转换位图数据时用了
System.Drawing.Imaging.PixelFormat.Format24bppRgb
这是不可取的。应该是32位argb。否则无法处理透明与黑色。这里灰度值使用平均值,我觉得应该使用PS开源程序的加权方法。

这里获得了一个连通的区域,实际上相当于一张掩码图。利用这张图,一是方便追踪边界,而是对原图进行掩码操作,进行分离等。

2.边缘追踪

使用边缘追踪算法,可以真正将边缘寻找出来,这样可以得到有序的点集合,可惜我不太理解EmguCV的CvInvoke.FindContours()获得的Emgu.CV.Util.VectorOfVectorOfPoint

是怎么个有序排列发,只好粗暴地显示出来算数。

最可靠的还那张掩码图,而不是这些莫名其妙的边界点。

private bool FloodFillTOcanny()        {            Image<Bgr, Byte> srcimg = new Image<Bgr, Byte>((Bitmap)pictureBox2.Image);            //转成灰度图            Image<Gray, Byte> grayimg =  srcimg.Convert<Gray, Byte>() ;           // CvInvoke.BitwiseNot(grayimg, grayimg);            //Canny 边缘检测            Image<Gray, Byte> cannyGrayimg  = grayimg.Canny((int)numericUD_FloodFill.Value, (int)numericUD_FloodFill.Value);            Gray bkGrayWhite = new Gray(255);            Emgu.CV.IOutputArray hierarchy = new Image<Gray, Byte>(srcimg.Width, srcimg.Height, bkGrayWhite);            Image<Rgb, Byte> imgresult = new Image<Rgb, Byte>(srcimg.Width, srcimg.Height, new Rgb(255, 255, 255));            CvInvoke.FindContours(  cannyGrayimg,                                    contours,                                    (Emgu.CV.IOutputArray)hierarchy,                                    Emgu.CV.CvEnum.RetrType.Ccomp,                                    Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone//保存为                                     );            GraphicsPath myGraphicsPath = new GraphicsPath();            Region myRegion = new Region();            myGraphicsPath.Reset();                                  int areaMax = 0, idx = 0 ;            for (int ii = 0; ii < contours.Size ; ii++)            {                int area = (int)CvInvoke.ContourArea(contours[ii]);                if (area > areaMax) { areaMax = area; idx = ii;}                if (area < 1) continue;                CvInvoke.DrawContours(imgresult, contours, ii, new MCvScalar(0, 0, 0), 1, Emgu.CV.CvEnum.LineType.EightConnected, (Emgu.CV.IInputArray)null, 2147483647);                imageBox1.Image = imgresult;                try                {                    myGraphicsPath.AddPolygon( contours[ii].ToArray() );                }                catch                  {                    //MessageBox.Show(e.Message);                }            }            myRegion.MakeEmpty();            myRegion.Union(myGraphicsPath);            pictureBox1.Refresh();            Pen pen = new Pen(Color.Red, 1);            pen.DashStyle = DashStyle.Dot;            Graphics gs = pictureBox1.CreateGraphics();             gs.DrawPath(pen, myGraphicsPath);             if (myRegion.IsVisible(lastPoint) )            {                //gs.DrawPolygon(pen, respts);            }           else             {               //gs.DrawRectangle(pen, new Rectangle(0,0,pictureBox1.Image.Width, pictureBox1.Image.Height));           }             gs.Dispose();            return true;        }
这个函数也是不可取的,我只做一个演示,其中
CvInvoke.FindContours(  cannyGrayimg,                                    contours,                                    (Emgu.CV.IOutputArray)hierarchy,                                    Emgu.CV.CvEnum.RetrType.Ccomp,                                    Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxNone//保存方式                                     );
函数最后的一个参数影响还是挺大的,使用是可以试试不同枚举参数;

3.测试

拉了几个控件,测试一番

我的鼠标猥琐地点了大腿那里,第二图显示漫水算法得到掩码图,最后一张图是边缘获取的结果,并且加到了原图上面。


源码:http://download.csdn.net/download/wangzibigan/10172940

(不知道怎么设成免费;文件28m其实大部分是emgucv库)

我的这篇文章其实没有实现魔棒功能,只是一个边缘的获取显示,所有我又写了第二篇。

我项目中的软件已经实现了魔棒功能,目前正在认真完善”羽化“功能。


阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 日韩第2页 日韩网站 亚州日韩 手机日韩 日韩女装 日韩女 日韩吧 日韩套图 日韩亚谢吧 日韩色片 日韩90 日韩p 日韩动态图 日韩影音 日韩干x网 日韩成 日韩一 日韩第3页 日韩成人 日韩明星 日韩少女 日韩va 日韩图区 日韩影片2 日韩一二三 日韩涩 日韩v 日韩在线涩 日韩做情 日韩一区 日韩网综合 日韩人体 日韩亚性 日韩大图 日韩成狼网 第一页日韩 日韩婷婷 日韩1区 日韩日韩 综合日韩 日韩xx