图像轮廓的提取

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xxdddail/article/details/77989990

对图像边缘提取,常见的方式是先对图片进行灰度处理,然后再利用图像梯度算法提取出边框。我们先来看效果图

 

经过处理后的前后对比,可以看到,图形的轮廓已经大致提取了。现在来看实现的代码

using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;

namespace BitmapOutlineSimulate
{
    public class BitmapOutline
    {
        #region 内部变量
        private Bitmap _bitmap = null;
        #endregion

        public BitmapOutline(Bitmap bitmap)
        {
            _bitmap = bitmap;
        }

        #region Calculate
        public Bitmap Calculate(GradientTypeEnum gradientType, int threshold)
        {
            Bitmap grayBitmap = new Bitmap(_bitmap.Width, _bitmap.Height);
            for (int i = 0; i < _bitmap.Width; i++)
            {
                for (int j = 0; j < _bitmap.Height; j++)
                {
                    //得到像素的原始色彩        
                    var oColor = _bitmap.GetPixel(i, j);
                    //得到该色彩的亮度
                    var brightness = oColor.GetBrightness();
                    //用该亮度计算灰度
                    var gRGB = (int)(brightness * 255);
                    //组成灰度色彩
                    var gColor = Color.FromArgb(gRGB, gRGB, gRGB);
                    //最后将该灰度色彩赋予该像素
                    grayBitmap.SetPixel(i, j, gColor);
                }
            }

            var gradientTemplate = Gradient.GetGradientTemplate(gradientType);


            var destBitmap = EdgeDectect(grayBitmap, gradientTemplate, threshold);
            return destBitmap;
        }

        //template为模板,nThreshold是一个阈值,
        //用来将模板游历的结果(也就是梯度)进行划分。
        //大于阈值的和小于阈值的分别赋予两种颜色,白或黑来标志边界和背景
        private Bitmap EdgeDectect(Bitmap grayBitmap, int[,] template, int nThreshold)
        {
            var destBitmap = new Bitmap(grayBitmap.Width, grayBitmap.Height);
            //取出和模板等大的原图中的区域
            int[,] gRGB = new int[3, 3];
            //模板值结果,梯度
            int templateValue = 0;
            //遍历灰度图中每个像素            
            for (int i = 1; i < grayBitmap.Width - 1; i++)
            {
                for (int j = 1; j < grayBitmap.Height - 1; j++)
                {
                    //取得模板下区域的颜色,即灰度
                    gRGB[0, 0] = grayBitmap.GetPixel(i - 1, j - 1).R;
                    gRGB[0, 1] = grayBitmap.GetPixel(i - 1, j).R;
                    gRGB[0, 2] = grayBitmap.GetPixel(i - 1, j + 1).R;
                    gRGB[1, 0] = grayBitmap.GetPixel(i, j - 1).R;
                    gRGB[1, 1] = grayBitmap.GetPixel(i, j).R;
                    gRGB[1, 2] = grayBitmap.GetPixel(i, j + 1).R;
                    gRGB[2, 0] = grayBitmap.GetPixel(i + 1, j - 1).R;
                    gRGB[2, 1] = grayBitmap.GetPixel(i + 1, j).R;
                    gRGB[2, 2] = grayBitmap.GetPixel(i + 1, j + 1).R;
                    //按模板计算
                    for (int m = 0; m < 3; m++)
                    {
                        for (int n = 0; n < 3; n++)
                        {
                            templateValue += template[m, n] * gRGB[m, n];
                        }
                    }
                    //将梯度之按阈值分类,并赋予不同的颜色
                    if (templateValue > nThreshold)
                    {
                        destBitmap.SetPixel(i, j, Color.FromArgb(255, 255, 255)); //白
                    }
                    else
                    {
                        destBitmap.SetPixel(i, j, Color.FromArgb(0, 0, 0)); //黑
                    }
                    templateValue = 0;
                }
            }
            return destBitmap;
        }
        #endregion
    }
}
在Calculate函数中,先对图片进行了灰度处理,其原理就是获取图片的亮度然后与RGB色运算得到。

在灰度处理后,使用图像梯度和阈值对图像进行处理。选择不同的梯度和阈值会处理出不同的结果,上图是使用Lapacian梯度和阈值125计算的结果。下面梯度的代码

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitmapOutlineSimulate
{
    /// <summary>
    /// 梯度
    /// </summary>
    public class Gradient
    {
        #region 常量
        public static int[,] Lapacian =
        {
            { 1,1,1},
            { 1,-8,1},
            { 1,1,1}
        };

        /// <summary>
        /// Sobel算子-水平
        /// </summary>
        public static int[,] Sobel_Horizontal =
        {
             { -1,-2,-1},
            { 0,0,0},
            { 1,2,1}
        };

        /// <summary>
        /// Sobel算子-垂直
        /// </summary>
        public static int[,] Sobel_Vertical =
       {
             { -1,0,1},
            { -2,0,2},
            { -1,0,1}
        };

        /// <summary>
        /// Sobel算子-对角线1
        /// </summary>
        public static int[,] Sobel_Diagonal1 =
        {
             { 0,1,2},
            { -1,0,1},
            { -2,-1,0}
        };

        /// <summary>
        /// Sobel算子-对角线2
        /// </summary>
        public static int[,] Sobel_Diagonal2 =
        {
            { -2,-1,0},
            { -1,0,1},
            { 0,1,2}
        };

        /// <summary>
        /// Prewitt算子-水平
        /// </summary>
        public static int[,] Prewitt_Horizontal =
        {
            { -1,-1,-1},
            { 0,0,0},
            { 1,1,1}
        };

        /// <summary>
        /// Prewitt算子-垂直
        /// </summary>
        public static int[,] Prewitt_Vertical =
        {
            { -1,0,1},
            { -1,0,1},
            { -1,0,1}
        };


        /// <summary>
        /// Prewitt算子-对角线1
        /// </summary>
        public static int[,] Prewitt_Diagonal1 =
        {
            { 0,1,1},
            { -1,0,1},
            { -1,-1,0}
        };

        /// <summary>
        /// Prewitt算子-对角线2
        /// </summary>
        public static int[,] Prewitt_Diagonal2 =
        {
            { -1,-1,0},
            { -1,0,1},
            { 0,1,1}
        };
        #endregion

        public static int[,] GetGradientTemplate(GradientTypeEnum gradientType)
        {
            int[,] gradientTemplate = null;
            switch (gradientType)
            {
                case GradientTypeEnum.Lapacian:
                    {
                        gradientTemplate = Lapacian;
                        break;
                    }
                case GradientTypeEnum.Sobel_Horizontal:
                    {
                        gradientTemplate = Sobel_Horizontal;
                        break;
                    }
                case GradientTypeEnum.Sobel_Vertical:
                    {
                        gradientTemplate = Sobel_Vertical;
                        break;
                    }
                case GradientTypeEnum.Sobel_Diagonal1:
                    {
                        gradientTemplate = Sobel_Diagonal1;
                        break;
                    }
                case GradientTypeEnum.Sobel_Diagonal2:
                    {
                        gradientTemplate = Sobel_Diagonal2;
                        break;
                    }
                case GradientTypeEnum.Prewitt_Horizontal:
                    {
                        gradientTemplate = Prewitt_Horizontal;
                        break;
                    }
                case GradientTypeEnum.Prewitt_Vertical:
                    {
                        gradientTemplate = Prewitt_Vertical;
                        break;
                    }
                case GradientTypeEnum.Prewitt_Diagonal1:
                    {
                        gradientTemplate = Prewitt_Diagonal1;
                        break;
                    }
                case GradientTypeEnum.Prewitt_Diagonal2:
                    {
                        gradientTemplate = Prewitt_Diagonal2;
                        break;
                    }
                default:
                    break;
            }
            return gradientTemplate;
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitmapOutlineSimulate
{
    public enum GradientTypeEnum
    {
        Lapacian,

        /// <summary>
        /// Sobel算子-水平
        /// </summary>
        Sobel_Horizontal,

        /// <summary>
        /// Sobel算子-垂直
        /// </summary>
        Sobel_Vertical,

        /// <summary>
        /// Sobel算子-对角线1
        /// </summary>
        Sobel_Diagonal1,

        /// <summary>
        /// Sobel算子-对角线2
        /// </summary>
        Sobel_Diagonal2,

        /// <summary>
        /// Prewitt算子-水平
        /// </summary>
        Prewitt_Horizontal,

        /// <summary>
        /// Prewitt算子-垂直
        /// </summary>
        Prewitt_Vertical,

        /// <summary>
        /// Prewitt算子-对角线1
        /// </summary>
        Prewitt_Diagonal1,

        /// <summary>
        /// Prewitt算子-对角线2
        /// </summary>
        Prewitt_Diagonal2
    }
}
灰度算法参考文章http://blog.csdn.net/chinaxhb/article/details/4038050
图像梯度参考文章http://blog.csdn.net/swj110119/article/details/51777422

转载请注明出处。

猜你喜欢

转载自blog.csdn.net/xxdddail/article/details/77989990