最近一段时间在开发一个画图软件,其中需要实现魔棒功能。网上浏览了一圈,没有找到。苦思之后,终于开窍了:。思路是:先用漫水填充算法, 获得一张连通区域的二值图。然后对这幅图进行边缘检测,获取边缘。如果使用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库)

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

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

C# 类似PS的魔棒工具(1)相关推荐

  1. 带节点的曲线,可以鼠标拖动节点,类似PS

    转自:http://topic.csdn.net/u/20120626/22/D778EDE8-CC97-4F13-AC5D-DA65BFB94E50.html 先来PS的 再来刚做的,极其简单. 思 ...

  2. 【canvas系列】用canvas实现一个colorpicker(类似PS的颜色选择器)

    每个浏览器都有自己的特点,比如今天要做的colorpicker就是,一千个浏览器,一千个哈姆雷特,一千个colorpicker.今天canvas系列就用canvas做一个colorpicker. ** ...

  3. 用CSS的perspective和transform将图片扭曲成类似PS的透视效果

    最终达到以下透视效果: 个人感想:这种效果在PS中用变形扭曲很快就实现,然而在CSS中做这个效果很麻烦,需要不断地微调.其实是可以有[简单粗暴]的方法,那就是直接在CSS中直接定位四点的坐标,当然CS ...

  4. 类似PS的蒙版?可以实现,LVGL『Object mask对象蒙版控件』介绍

    一. LVGL GUI对象蒙版控件的概念 绘制其子级时,对象蒙版能够向图形添加一些蒙版 二. LVGL GUI对象蒙版小部件和样式 对象蒙版只有一个主要部分 LV_OBJMASK_PART_BG ,它 ...

  5. html5水墨效果,HTML5 Canvas 类似PS中水墨喷溅晕染的喷笔效果

    JavaScript 语言: JaveScriptBabelCoffeeScript 确定 var stage = document.getElementById("stage") ...

  6. java制作透明窗体(类似PS欢迎界面 )

    首先得准备一张png格式图片,因为png支持透明效果,图片如下 将Jframe设置为没有边框和标题的形式,代码如下: setUndecorated(true); 重写Jframe的paint方法,并将 ...

  7. [canvas入坑3] 类似ps中魔术棒或者画图中油漆桶的功能

    查看效果请到 http://philippica.github.io/  点击fill 这功能其实实现很low,最早高一看黑书的时候看到了floodfill算法感觉好神奇,转念一想这不就是bfs么!! ...

  8. ps滤镜之旋转扭曲算法实现

    最近捕鱼达人3的发布 ,人气比较高,由于前2部作品已经有一大群"粉丝",所以此次3代再发布就受到很多关注.就画面3代是3d的效果,加上一些特殊效果,在画面质量上有了一定的提升.本文 ...

  9. Ps算法Python实现:图层混合模式-色相

    1.相关文章 最近想要实现Photoshop图层混合中的色相模式,在网上查阅了众多关于混合模式实现的文章: Photoshop图层混合模式详解 PS中混合模式是什么意思? photoshop图层混合模 ...

最新文章

  1. 深入理解 C 指针阅读笔记 -- 第六章
  2. 多边形填充算法-有序边表法(扫描线算法) 计算机图形学
  3. 转载-项目经理与部门经理之间的关系
  4. LeetCode 101. 对称二叉树 思考分析
  5. mysql不支持emoji表情的问题的解决方法
  6. C# GDI绘制波形图
  7. Ambari--主机管理
  8. 如何提升应用程序启动权限
  9. 南宁二中三中高考2021成绩查询,2020年南宁二中三中录取总成绩不低于A
  10. pb实现简单计算器的思想_人教版初中数学七年级下册 用计算器求算数平方根、用有理数估计算数平方根的大小公开课优质课课件教案视频...
  11. springboot整合mybatis-plus多数据源分别连接mysql和oracle
  12. 关于盒子模型的应用(制作三角形)
  13. Excel里怎么冻结某一行某一列
  14. python提取图片中的数字_用Python-OpenCV从车牌图像中提取数字和字母
  15. Python数据分析学习 二
  16. Linux perf 1.1、perf_event内核框架
  17. MFC下载—时间记录
  18. java操纵excel文件2
  19. 火狐浏览器打不开淘宝首页的解决办法
  20. js判断true和false

热门文章

  1. (转载)WebAssembly,Web的新时代
  2. 【论文翻译】KDD19 -HeGAN: Adversarial Learning on Heterogeneous Information Networks 异构信息网络中的对抗学习
  3. 使用HTML5和CSS制作抖音动态首页
  4. 懂AI的医生一定会替代不懂AI的医生
  5. ROS机器人程序设计(原书第2版).
  6. 实验7-4 身份证号码最后一位
  7. AI实战:上海垃圾分类系列(一)之快速搭建垃圾分类模型
  8. 杂学第八篇:最近成功实现用applescript检测手机号码是否注册imessage,有需要的带价联系
  9. 从国外的网站上下载包很慢的解决办法
  10. 利用vbox安装服务器操作系统,在Linux服务器上使用Vbox安装虚拟机(示例代码)