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

本文介绍了一种改进的漫水填充算法,分别通过灰度值和ARGB四个分量进行颜色相似性判断,以实现更精准的图像填充效果。文中详细展示了算法的实现代码,并通过实例验证了算法的有效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

之前在魔棒工具的文章里写过一个漫水填充算法,实际使用效果并不好。这是因为算法将图片转为灰度再去与种子点的灰度比较。可以想象,这种做法是不合理的(虽然通常都是这么做)。比如两个视觉上相差很明显的颜色,其灰度值很有可能一样或者相差不大(无论你怎么修改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.测试

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


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


试一下透明区域


结果不错。



评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值