C# 类似PS的魔棒工具(2)羽化

本文介绍了如何在图像处理中实现魔棒工具的边界羽化功能。通过理解羽化的概念及其与透明度的关系,利用模糊处理和卷积核对边界进行平滑过渡,最终达到图像融合的视觉效果。

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

        上一篇文章写了所谓的魔棒工具其实离可以使用的魔棒还差得远,不过已经可以找到边界和掩码图了,这篇文章讲讲魔棒的边界羽化功能。

1、羽化的概念

以前学习过PS,大概知道羽化的作用,却不知道什么原理。现在要实现这个功能,当然不能在像以前那样只知道使用用不知道原理了。百度百科上说,羽化使选中的图像边缘呈现半透明过渡的效果,这有利于在不同图像中合成效果。也就是说,羽化其实是一种透明效果。貌似PS里有个alpha通道,羽化应该就是靠这个alpha层进行运算得到的效果。

2、alpha与透明

上一篇文章讲了怎么获得一张掩码图,其实这张图可以当作羽化的alpha通道。alpha=0就是全透明,alpha=255就是不透明,之间是过度。所谓透明,就是前景与背景的加权混合。不透明就是前景权重为1背景权重为0,不透明就是反过来。半透就不用说了。

3、模糊

所谓模糊其实就是让某点的颜色受其周围颜色的不同程度的同化。

上篇文章得到的掩码图是二值的连通区域图,相邻不同值就是边界所在。将这二值转为0和255,然后对边界进行线性的模糊处理,二值变为0-255的渐变图,原来的边界变为有一定宽度的带,这个带的宽度就是羽化半径(或者直径,宽度,反正就是那个意思)。当然,对边界进行模糊处理在程序上恐怕不好操作,那就浪费点时间,对整幅图进行模糊处理。

4、卷积

对图像进行模糊处理,我知道的方法是使用卷积核与图像进行卷积,百度一下有很多博客介绍,这里不详细解释,简单来说就是某像素的颜色是周围颜色不同权重乘积的和。我开始是使用高斯模糊卷积核,如下

double [,] gaussianKn = new double[N, N];//
KernelSum = 0;for (i = 0; i < N; i++){ for (j = 0; j < N; j++) { gaussianKn[i, j] = Math.Exp(-((i - N / 2) * (i - N / 2) + (j - N / 2) * (j - N / 2)) / (2.0 * sigma * sigma)); KernelSum += gaussianKn[i, j]; }}
sigma调整正态分布的“高矮”。结果发现,还不如直接线性递增递减产生一个(不知道什么名字)卷积核,效果还不错,计算又简单快捷。如下

        /// <summary>
        /// 生成N*N的矩阵,作为模糊卷积核
        /// </summary>
        /// <param name="N">矩阵边长,必须为奇数,最小为1</param>
        /// <returns>目的矩阵</returns>
	private int[,] _createKernel(int N)
        {
            if (N <= 0) N = 1;
            int i, j;
            int[,] kn = new int[N, N];
            KernelSum = 0;
            for (i = 0; i < N; i++)
            {
                for (j = 0; j < N; j++)
                {
                    int g = (Math.Abs(i - N / 2) > Math.Abs(j - N / 2)) ? Math.Abs(i - N / 2) : Math.Abs(j - N / 2);
                    kn[i, j] = N/2 - g +1; 
                    KernelSum += kn[i, j];
                }
            }
            return kn;
        }   
KernelSum 是为了不使用浮点数加入的,就是相当于将浮点数的乘法变为乘一个整数(分子)再除一个整数(分母)。再后面程序中将卷积和除KernelSum,才能得到想要的结果
        //消耗大量时间,因为进行了没有优化的卷积运算
        //对二值图模糊处理,产生一张ALPHA渐变图
        private Bitmap blurred(Bitmap srcbmp)
        {
#if DEBUG_TSP2
            DateTime st = DateTime.Now;
#endif
            if (srcbmp == null) return null;
            int Width = srcbmp.Width, Height = srcbmp.Height;
            int[,] InputPicbuf = new int[Width,  Height];
            Color color = new Color();
            //只获取R通道
            for (int i = 0; i <  Width; i++)
            {
                for (int j = 0; j <  Height; j++)
                {
                    color = srcbmp.GetPixel(i, j);
                    InputPicbuf[ i, j] = color.R;
                }
            }


            Bitmap blurbmp = new Bitmap( Width,  Height);//创建新位图,保存模糊位图数据
            BitmapData blurbitmapdata = blurbmp.LockBits(new Rectangle(new Point(0, 0), blurbmp.Size), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            unsafe
            {
                int* dstbyte = (int*)(blurbitmapdata.Scan0.ToPointer());
                Color c;
                for (int j = 0; j < Height;j++ )
                for (int i = 0; i < Width ; i++)
                {
                    c = integrateColorPoint(i, j, BlurKn, InputPicbuf, Width, Height);
                    dstbyte[i + j * Width] =  c.ToArgb();
                }
            }
            blurbmp.UnlockBits(blurbitmapdata); 
#if DEBUG_TSP2
            lab_time.Text = "模糊处理时间:"+(DateTime.Now - st).TotalMilliseconds.ToString();
            lab_time.Refresh();
#endif
            return blurbmp;
        }
        //获取某点颜色积分
        private Color integrateColorPoint(int i, int j, int[,] kn, int[,] picbuf, int w, int h)
        {
            int cr=0 ;
            //每一个像素计算使用高斯模糊卷积核进行计算
            int KnSize = kn.GetLength(0);
            for (int r = 0; r < KnSize; r++)//循环卷积核的每一行
            {
                int row = i - KnSize / 2 + r;
                for (int f = 0; f < KnSize; f++)//循环卷积核的每一列
                {
                    int index = j - KnSize / 2 + f;
                    //当超出位图的大小范围时,选择最边缘的像素值作为该点的像素值
                    row = row < 0 ? 0 : row;
                    index = index < 0 ? 0 : index;
                    row = row >= w ? w - 1 : row;
                    index = index >= h ? h- 1 : index;
                    //像素值累加
                    cr +=   kn[r, f] * picbuf[ row, index]  ;
                }
            }
            cr /= KernelSum;
            cr = cr > 255 ? 255 : cr;


            return Color.FromArgb(cr,0,0,0);
        }


5、测试

代码太多就不一一贴出,拉几个控件测试一番

鼠标画一个区域

拖开,便于观察,这是模糊半径为0的效果

调整模糊半径,可以看到选取部分边缘柔化了,也可以看到模糊处理时间十分感人




与前一篇文章结合,魔棒工具倒是可以做出来,可惜性能实在马马虎虎。所以继续写第三篇(性能优化),感兴趣请留意下一篇文章。

本文章源码打包下载:http://download.youkuaiyun.com/download/wangzibigan/10186853




                
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值