圖像輪廓的提取


對圖像邊緣提取,常見的方式是先對圖片進行灰度處理,然后再利用圖像梯度算法提取出邊框。我們先來看效果圖

 

經過處理后的前后對比,可以看到,圖形的輪廓已經大致提取了。現在來看實現的代碼

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

轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM