C# 魔棒工具-漫水填充算法优化

2023-11-11 18:11

本文主要是介绍C# 魔棒工具-漫水填充算法优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

之前在魔棒工具的文章里写过一个漫水填充算法,实际使用效果并不好。这是因为算法将图片转为灰度再去与种子点的灰度比较。可以想象,这种做法是不合理的(虽然通常都是这么做)。比如两个视觉上相差很明显的颜色,其灰度值很有可能一样或者相差不大(无论你怎么修改RGB三者权重,终归会遇到这种情况)。所以最好是对颜色的ARGB四个分值分开进行比较,如果速度允许,可以计算方差;如果希望速度快一点,就直接计算差值。

1.按灰度检测

        /// <summary>///  漫水填充 FloodFill()按灰度检测,FloodFill_argb()按四通道分量检测/// </summary>/// <param name="src">原图</param>/// <param name="location">检测点</param>/// <param name="fillColor">填充颜色</param>/// <param name="threshould">阈值</param>/// <returns>填充图,非填充部分为默认值</returns>unsafe public Bitmap FloodFill(Bitmap src, Point location, Color fillColor, int threshould){try{Bitmap srcbmp = src;Color backColor = srcbmp.GetPixel(location.X, location.Y);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 srcbmpData = 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);int stride = srcbmpData.Stride;byte* srcbuf = (byte*)srcbmpData.Scan0.ToPointer();byte* dstbuf = (byte*)dstbmpData.Scan0.ToPointer();int  cr = backColor.R,  cg = backColor.G, cb = backColor.B, ca = backColor.A;byte fcr = fillColor.R, fcg = fillColor.G, fcb = fillColor.B;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;dstbuf[3 * p.X + p.Y * stride] = fcb;dstbuf[3 * p.X + 1 + p.Y * stride] =  fcg;dstbuf[3 * p.X + 2 + p.Y * stride] =  fcr;if (p.X > 0 && (mask[p.X - 1, p.Y] != 1)&& Math.Abs(cb - srcbuf[3 * (p.X - 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[3 * (p.X - 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[3 * (p.X - 1) + 2 + p.Y * stride]) < threshould){dstbuf[3 * (p.X - 1) + p.Y * stride] = fcb;dstbuf[3 * (p.X - 1) + 1 + p.Y * stride] = fcg;dstbuf[3 * (p.X - 1) + 2 + p.Y * stride] = fcr;fillPoints.Push(new Point(p.X - 1, p.Y));mask[p.X - 1, p.Y] = 1;}if (p.X < w - 1 && (mask[p.X + 1, p.Y] != 1)&& Math.Abs(cb - srcbuf[3 * (p.X + 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[3 * (p.X + 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[3 * (p.X + 1) + 2+p.Y * stride]) < threshould ){dstbuf[3 * (p.X + 1) + p.Y * stride] = fcb;dstbuf[3 * (p.X + 1) + 1 + p.Y * stride] = fcg;dstbuf[3 * (p.X + 1) + 2 + p.Y * stride] = fcr;fillPoints.Push(new Point(p.X + 1, p.Y));mask[p.X + 1, p.Y] = 1;}if (p.Y > 0 && (mask[p.X, p.Y - 1] != 1)&& Math.Abs(cb - srcbuf[3 * p.X + (p.Y - 1) * stride]) + Math.Abs(cg - srcbuf[3 * p.X + 1 + (p.Y - 1) * stride]) + Math.Abs(cr - srcbuf[3 * p.X +2+ (p.Y - 1) * stride]) < threshould){dstbuf[3 * p.X + (p.Y - 1) * stride] = fcb;dstbuf[3 * p.X + 1 + (p.Y - 1) * stride] = fcg;dstbuf[3 * p.X + 2 + (p.Y - 1) * stride] = fcr;fillPoints.Push(new Point(p.X, p.Y - 1));mask[p.X, p.Y - 1] = 1;}if (p.Y < h - 1 && (mask[p.X, p.Y + 1] != 1)&& Math.Abs(cb - srcbuf[3 * p.X + (p.Y + 1) * stride]) + Math.Abs(cg - srcbuf[3 * p.X +1+ (p.Y + 1) * stride]) + Math.Abs(cr - srcbuf[3 * p.X +2+ (p.Y + 1) * stride]) < threshould){dstbuf[3 * p.X + (p.Y + 1) * stride] = fcb;dstbuf[3 * p.X + 1 + (p.Y + 1) * stride] = fcg;dstbuf[3 * p.X + 2 + (p.Y + 1) * stride] = fcr;fillPoints.Push(new Point(p.X, p.Y + 1));mask[p.X, p.Y + 1] = 1;}}fillPoints.Clear();srcbmp.UnlockBits(srcbmpData);dstbmp.UnlockBits(dstbmpData);return dstbmp;}catch (Exception exp){System.Windows.MessageBox.Show(exp.Message);return null;}}
        public int GetGray(byte r, byte g, byte b){return (int)(r * 77 + g * 151 + b * 28) >> 8;//按权重计算灰度值}


2.按颜色ARGB四分量检测

这里直接计算差值儿没有计算方差,其实效果已经足够。

        unsafe public Bitmap FloodFill_argb(Bitmap src, Point location, Color fillColor, int threshould){try{Bitmap srcbmp = src;Color backColor = srcbmp.GetPixel(location.X, location.Y);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.Format32bppArgb);System.Drawing.Imaging.BitmapData dstbmpData = dstbmp.LockBits(new Rectangle(0, 0, dstbmp.Width, dstbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadWrite, System.Drawing.Imaging.PixelFormat.Format32bppArgb);int stride = bmpData.Stride;int stridedst = dstbmpData.Stride;byte* srcbuf = (byte*)bmpData.Scan0.ToPointer();int* dstbuf = (int*)dstbmpData.Scan0.ToPointer();int cr = backColor.R, cg = backColor.G, cb = backColor.B, ca = backColor.A;byte fcr = fillColor.R, fcg = fillColor.G, fcb = fillColor.B;int fc = fillColor.ToArgb();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;dstbuf[  p.X + p.Y * w] = fc;if (p.X > 0 && (mask[p.X - 1, p.Y] != 1)&& Math.Abs(cb - srcbuf[4 * (p.X - 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[4 * (p.X - 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[4 * (p.X - 1) + 2 + p.Y * stride]) < threshould&& Math.Abs(ca - srcbuf[4 * (p.X - 1) +3+ p.Y * stride]) < threshould/2){dstbuf[  (p.X - 1) + p.Y * w] = fc;fillPoints.Push(new Point(p.X - 1, p.Y));mask[p.X - 1, p.Y] = 1;}if (p.X < w - 1 && (mask[p.X + 1, p.Y] != 1)&& Math.Abs(cb - srcbuf[4 * (p.X + 1) + p.Y * stride]) + Math.Abs(cg - srcbuf[4 * (p.X + 1) + 1 + p.Y * stride]) + Math.Abs(cr - srcbuf[4 * (p.X + 1) + 2 + p.Y * stride]) < threshould&& Math.Abs(ca - srcbuf[4 * (p.X + 1) + 3 + p.Y * stride]) < threshould / 2){dstbuf[  (p.X + 1) + p.Y * w] = fc;fillPoints.Push(new Point(p.X + 1, p.Y));mask[p.X + 1, p.Y] = 1;}if (p.Y > 0 && (mask[p.X, p.Y - 1] != 1)&& Math.Abs(cb - srcbuf[4 * p.X + (p.Y - 1) * stride]) + Math.Abs(cg - srcbuf[4 * p.X + 1 + (p.Y - 1) * stride]) + Math.Abs(cr - srcbuf[4 * p.X + 2 + (p.Y - 1) * stride]) < threshould&& Math.Abs(ca - srcbuf[4 * p.X +3+ (p.Y - 1) * stride]) < threshould / 2){dstbuf[  p.X + (p.Y - 1) * w] = fc ;fillPoints.Push(new Point(p.X, p.Y - 1));mask[p.X, p.Y - 1] = 1;}if (p.Y < h - 1 && (mask[p.X, p.Y + 1] != 1)&& Math.Abs(cb - srcbuf[4 * p.X + (p.Y + 1) * stride]) + Math.Abs(cg - srcbuf[4 * p.X + 1 + (p.Y + 1) * stride]) + Math.Abs(cr - srcbuf[4 * p.X + 2 + (p.Y + 1) * stride]) < threshould&& Math.Abs(ca - srcbuf[4 * p.X +3+ (p.Y + 1) * stride]) < threshould /2){dstbuf[  p.X + (p.Y + 1) *w] = fc;fillPoints.Push(new Point(p.X, p.Y + 1));mask[p.X, p.Y + 1] = 1;}}fillPoints.Clear();srcbmp.UnlockBits(bmpData);dstbmp.UnlockBits(dstbmpData);return dstbmp;}catch (Exception exp){System.Windows.MessageBox.Show(exp.Message);return null;}}

3.测试

是驴是马,拉出来遛一遛
下图中要处理的图片,向日葵的花心与花瓣,分别为橙色与黄色,灰度值相差很小,右上角有个透明区域,里面有个黑色方框。
使用


调整阈值,为了观察,把透明度也调整一下,点选花瓣


试一下透明区域


结果不错。



这篇关于C# 魔棒工具-漫水填充算法优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/391938

相关文章

在.NET平台使用C#为PDF添加各种类型的表单域的方法

《在.NET平台使用C#为PDF添加各种类型的表单域的方法》在日常办公系统开发中,涉及PDF处理相关的开发时,生成可填写的PDF表单是一种常见需求,与静态PDF不同,带有**表单域的文档支持用户直接在... 目录引言使用 PdfTextBoxField 添加文本输入域使用 PdfComboBoxField

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

Java中的工具类命名方法

《Java中的工具类命名方法》:本文主要介绍Java中的工具类究竟如何命名,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java中的工具类究竟如何命名?先来几个例子几种命名方式的比较到底如何命名 ?总结Java中的工具类究竟如何命名?先来几个例子JD

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

C# Where 泛型约束的实现

《C#Where泛型约束的实现》本文主要介绍了C#Where泛型约束的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录使用的对象约束分类where T : structwhere T : classwhere T : ne

MySQL索引的优化之LIKE模糊查询功能实现

《MySQL索引的优化之LIKE模糊查询功能实现》:本文主要介绍MySQL索引的优化之LIKE模糊查询功能实现,本文通过示例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录一、前缀匹配优化二、后缀匹配优化三、中间匹配优化四、覆盖索引优化五、减少查询范围六、避免通配符开头七、使用外部搜索引擎八、分