http://www.cnblogs.com/xrwang/archive/2010/02/28/ImageSegmentation.html?login=1

作者:王先荣
前言
    图像分割指的是将数字图像细分为多个图像子区域的过程,在OpenCv中实现了三种跟图像分割相关的算法,它们分别是:分水岭分割算法、金字塔分割算法以及均值漂移分割算法。它们的使用过程都很简单,下面的文章权且用于记录,并使该系列保持完整吧。
分水岭分割算法
    分水岭分割算法需要您或者先前算法提供标记,该标记用于指定哪些大致区域是目标,哪些大致区域是背景等等;分水岭分割算法的分割效果严重依赖于提供的标记。OpenCv中的函数cvWatershed实现了该算法,函数定义如下:


void cvWatershed(const CvArr * image, CvArr * markers)

其中:image为8为三通道的彩色图像;
      markers是单通道整型图像,它用不同的正整数来标记不同的区域,下面的代码演示了如果响应鼠标事件,并生成标记图像。

图像分割 Image Segmentation生成标记图像
图像分割 Image Segmentation

            //当鼠标按下并在源图像上移动时,在源图像上绘制分割线条        private void pbSource_MouseMove(object sender, MouseEventArgs e)        {            //如果按下了左键            if (e.Button == MouseButtons.Left)            {                if (previousMouseLocation.X >= 0 && previousMouseLocation.Y >= 0)                {                    Point p1 = new Point((int)(previousMouseLocation.X * xScale), (int)(previousMouseLocation.Y * yScale));                    Point p2 = new Point((int)(e.Location.X * xScale), (int)(e.Location.Y * yScale));                    LineSegment2D ls = new LineSegment2D(p1, p2);                    int thickness = (int)(LineWidth * xScale);                    imageSourceClone.Draw(ls, new Bgr(255d, 255d, 255d), thickness);                    pbSource.Image = imageSourceClone.Bitmap;                    imageMarkers.Draw(ls, new Gray(drawCount), thickness);                }                previousMouseLocation = e.Location;            }        }        //当松开鼠标左键时,将绘图的前一位置设置为(-1,-1)        private void pbSource_MouseUp(object sender, MouseEventArgs e)        {            previousMouseLocation = new Point(-1, -1);            drawCount++;        }
图像分割 Image Segmentation

 

 

        您可以用类似下面的方式来使用分水岭算法:

图像分割 Image Segmentation使用分水岭分割算法
图像分割 Image Segmentation

        /// <summary>        /// 分水岭算法图像分割        /// </summary>        /// <returns>返回用时</returns>        private string Watershed()        {            //分水岭算法分割            Image<Gray, Int32> imageMarkers2 = imageMarkers.Copy();            Stopwatch sw = new Stopwatch();            sw.Start();            CvInvoke.cvWatershed(imageSource.Ptr, imageMarkers2.Ptr);            sw.Stop();            //将分割的结果转换到256级灰度图像            pbResult.Image = imageMarkers2.Bitmap;            imageMarkers2.Dispose();            return string.Format("分水岭图像分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }
图像分割 Image Segmentation

 

 

金字塔分割算法
    金字塔分割算法由cvPrySegmentation所实现,该函数的使用很简单;需要注意的是图像的尺寸以及金字塔的层数,图像的宽度和高度必须能被2整除,能够被2整除的次数决定了金字塔的最大层数。下面的代码演示了如果校验金字塔层数:

图像分割 Image Segmentation校验金字塔分割的金字塔层数
图像分割 Image Segmentation

        /// <summary>        /// 当改变金字塔分割的参数“金字塔层数”时,对参数进行校验        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void txtPSLevel_TextChanged(object sender, EventArgs e)        {            int level = int.Parse(txtPSLevel.Text);            if (level < 1 || imageSource.Width % (int)(Math.Pow(2, level - 1)) != 0 || imageSource.Height % (int)(Math.Pow(2, level - 1)) != 0)                MessageBox.Show(this, "注意:您输入的金字塔层数不符合要求,计算结果可能会无效。", "金字塔层数错误");        }
图像分割 Image Segmentation

 

 

使用金字塔分割的示例代码如下:

图像分割 Image Segmentation使用金字塔分割算法
图像分割 Image Segmentation

        /// <summary>        /// 金字塔分割算法        /// </summary>        /// <returns></returns>        private string PrySegmentation()        {            //准备参数            Image<Bgr, Byte> imageDest = new Image<Bgr, byte>(imageSource.Size);            MemStorage storage = new MemStorage();            IntPtr ptrComp = IntPtr.Zero;            int level = int.Parse(txtPSLevel.Text);            double threshold1 = double.Parse(txtPSThreshold1.Text);            double threshold2 = double.Parse(txtPSThreshold2.Text);            //金字塔分割            Stopwatch sw = new Stopwatch();            sw.Start();            CvInvoke.cvPyrSegmentation(imageSource.Ptr, imageDest.Ptr, storage.Ptr, out ptrComp, level, threshold1, threshold2);            sw.Stop();            //显示结果            pbResult.Image = imageDest.Bitmap;            //释放资源            imageDest.Dispose();            storage.Dispose();            return string.Format("金字塔分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }
图像分割 Image Segmentation

 

 

均值漂移分割算法
    均值漂移分割算法由cvPryMeanShiftFiltering所实现,均值漂移分割的金字塔层数只能介于[1,7]之间,您可以用类似下面的代码来使用它:

图像分割 Image Segmentation使用均值漂移分割算法
图像分割 Image Segmentation

        /// <summary>        /// 均值漂移分割算法        /// </summary>        /// <returns></returns>        private string PryMeanShiftFiltering()        {            //准备参数            Image<Bgr, Byte> imageDest = new Image<Bgr, byte>(imageSource.Size);            double spatialRadius = double.Parse(txtPMSFSpatialRadius.Text);            double colorRadius = double.Parse(txtPMSFColorRadius.Text);            int maxLevel = int.Parse(txtPMSFNaxLevel.Text);            int maxIter = int.Parse(txtPMSFMaxIter.Text);            double epsilon = double.Parse(txtPMSFEpsilon.Text);            MCvTermCriteria termcrit = new MCvTermCriteria(maxIter, epsilon);            //均值漂移分割            Stopwatch sw = new Stopwatch();            sw.Start();            OpenCvInvoke.cvPyrMeanShiftFiltering(imageSource.Ptr, imageDest.Ptr, spatialRadius, colorRadius, maxLevel, termcrit);            sw.Stop();            //显示结果            pbResult.Image = imageDest.Bitmap;            //释放资源            imageDest.Dispose();            return string.Format("均值漂移分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }
图像分割 Image Segmentation

 

 

    函数cvPryMeanShiftFiltering在EmguCv中没有实现,我们可以用下面的方式来使用:

图像分割 Image Segmentation调用均值漂移分割

        //均值漂移分割        [DllImport("cv200.dll")]        public static extern void cvPyrMeanShiftFiltering(IntPtr src, IntPtr dst, double spatialRadius, double colorRadius, int max_level, MCvTermCriteria termcrit);

 

 

分割效果及性能对比
    上述三种分割算法的效果如何呢?下面我们以它们的默认参数,对一幅2272x1704大小的图像进行分割。得到的结果如下所示:

图像分割 Image Segmentation

图1 分水岭分割算法(左图白色的线条用于标记区域)

图像分割 Image Segmentation

图2 金字塔分割算法

图像分割 Image Segmentation

图3 均值漂移分割算法
    从上面我们可以看出:
    (1)分水岭分割算法的分割效果效果最好,均值漂移分割算法次之,而金字塔分割算法的效果最差;
    (2)均值漂移分割算法效率最高,分水岭分割算法接近于均值漂移算法,金字塔分割算法需要很长的时间。
    值得注意的是分水岭算法对标记很敏感,需要仔细而认真的绘制。

 

    本文的完整代码如下:

图像分割 Image Segmentation本文完整代码
图像分割 Image Segmentation

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Windows.Forms;using System.Diagnostics;using System.Runtime.InteropServices;using Emgu.CV;using Emgu.CV.CvEnum;using Emgu.CV.Structure;using Emgu.CV.UI;namespace ImageProcessLearn{    public partial class FormImageSegment : Form    {        //成员变量        private string sourceImageFileName = "wky_tms_2272x1704.jpg";//源图像文件名        private Image<Bgr, Byte> imageSource = null;                //源图像        private Image<Bgr, Byte> imageSourceClone = null;           //源图像的克隆        private Image<Gray, Int32> imageMarkers = null;              //标记图像        private double xScale = 1d;                                 //原始图像与PictureBox在x轴方向上的缩放        private double yScale = 1d;                                 //原始图像与PictureBox在y轴方向上的缩放        private Point previousMouseLocation = new Point(-1, -1);    //上次绘制线条时,鼠标所处的位置        private const int LineWidth = 5;                            //绘制线条的宽度        private int drawCount = 1;                                  //用户绘制的线条数目,用于指定线条的颜色                public FormImageSegment()        {            InitializeComponent();        }        //窗体加载时        private void FormImageSegment_Load(object sender, EventArgs e)        {            //设置提示            toolTip.SetToolTip(rbWatershed, "可以在源图像上用鼠标绘制大致分割区域线条,该线条用于分水岭算法");            toolTip.SetToolTip(txtPSLevel, "金字塔层数跟图像尺寸有关,该值只能是图像尺寸被2整除的次数,否则将得出错误结果");            toolTip.SetToolTip(txtPSThreshold1, "建立连接的错误阀值");            toolTip.SetToolTip(txtPSThreshold2, "分割簇的错误阀值");            toolTip.SetToolTip(txtPMSFSpatialRadius, "空间窗的半径");            toolTip.SetToolTip(txtPMSFColorRadius, "色彩窗的半径");            toolTip.SetToolTip(btnClearMarkers, "清除绘制在源图像上,用于分水岭算法的大致分割区域线条");            //加载图像            LoadImage();        }        //当窗体关闭时,释放资源        private void FormImageSegment_FormClosing(object sender, FormClosingEventArgs e)        {            if (imageSource != null)                imageSource.Dispose();            if (imageSourceClone != null)                imageSourceClone.Dispose();            if (imageMarkers != null)                imageMarkers.Dispose();        }        //加载源图像        private void btnLoadImage_Click(object sender, EventArgs e)        {            OpenFileDialog ofd = new OpenFileDialog();            ofd.CheckFileExists = true;            ofd.DefaultExt = "jpg";            ofd.Filter = "图片文件|*.jpg;*.png;*.bmp|所有文件|*.*";            if (ofd.ShowDialog(this) == DialogResult.OK)            {                if (ofd.FileName != "")                {                    sourceImageFileName = ofd.FileName;                    LoadImage();                }            }            ofd.Dispose();        }        //清除分割线条        private void btnClearMarkers_Click(object sender, EventArgs e)        {            if (imageSourceClone != null)                imageSourceClone.Dispose();            imageSourceClone = imageSource.Copy();            pbSource.Image = imageSourceClone.Bitmap;            imageMarkers.SetZero();            drawCount = 1;        }        //当鼠标按下并在源图像上移动时,在源图像上绘制分割线条        private void pbSource_MouseMove(object sender, MouseEventArgs e)        {            //如果按下了左键            if (e.Button == MouseButtons.Left)            {                if (previousMouseLocation.X >= 0 && previousMouseLocation.Y >= 0)                {                    Point p1 = new Point((int)(previousMouseLocation.X * xScale), (int)(previousMouseLocation.Y * yScale));                    Point p2 = new Point((int)(e.Location.X * xScale), (int)(e.Location.Y * yScale));                    LineSegment2D ls = new LineSegment2D(p1, p2);                    int thickness = (int)(LineWidth * xScale);                    imageSourceClone.Draw(ls, new Bgr(255d, 255d, 255d), thickness);                    pbSource.Image = imageSourceClone.Bitmap;                    imageMarkers.Draw(ls, new Gray(drawCount), thickness);                }                previousMouseLocation = e.Location;            }        }        //当松开鼠标左键时,将绘图的前一位置设置为(-1,-1)        private void pbSource_MouseUp(object sender, MouseEventArgs e)        {            previousMouseLocation = new Point(-1, -1);            drawCount++;        }        //加载源图像        private void LoadImage()        {            if (imageSource != null)                imageSource.Dispose();            imageSource = new Image<Bgr, byte>(sourceImageFileName);            if (imageSourceClone != null)                imageSourceClone.Dispose();            imageSourceClone = imageSource.Copy();            pbSource.Image = imageSourceClone.Bitmap;            if (imageMarkers != null)                imageMarkers.Dispose();            imageMarkers = new Image<Gray, Int32>(imageSource.Size);            imageMarkers.SetZero();            xScale = 1d * imageSource.Width / pbSource.Width;            yScale = 1d * imageSource.Height / pbSource.Height;            drawCount = 1;        }        //分割图像        private void btnImageSegment_Click(object sender, EventArgs e)        {            if (rbWatershed.Checked)                txtResult.Text += Watershed();            else if (rbPrySegmentation.Checked)                txtResult.Text += PrySegmentation();            else if (rbPryMeanShiftFiltering.Checked)                txtResult.Text += PryMeanShiftFiltering();        }        /// <summary>        /// 分水岭算法图像分割        /// </summary>        /// <returns>返回用时</returns>        private string Watershed()        {            //分水岭算法分割            Image<Gray, Int32> imageMarkers2 = imageMarkers.Copy();            Stopwatch sw = new Stopwatch();            sw.Start();            CvInvoke.cvWatershed(imageSource.Ptr, imageMarkers2.Ptr);            sw.Stop();            //将分割的结果转换到256级灰度图像            pbResult.Image = imageMarkers2.Bitmap;            imageMarkers2.Dispose();            return string.Format("分水岭图像分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }        /// <summary>        /// 金字塔分割算法        /// </summary>        /// <returns></returns>        private string PrySegmentation()        {            //准备参数            Image<Bgr, Byte> imageDest = new Image<Bgr, byte>(imageSource.Size);            MemStorage storage = new MemStorage();            IntPtr ptrComp = IntPtr.Zero;            int level = int.Parse(txtPSLevel.Text);            double threshold1 = double.Parse(txtPSThreshold1.Text);            double threshold2 = double.Parse(txtPSThreshold2.Text);            //金字塔分割            Stopwatch sw = new Stopwatch();            sw.Start();            CvInvoke.cvPyrSegmentation(imageSource.Ptr, imageDest.Ptr, storage.Ptr, out ptrComp, level, threshold1, threshold2);            sw.Stop();            //显示结果            pbResult.Image = imageDest.Bitmap;            //释放资源            imageDest.Dispose();            storage.Dispose();            return string.Format("金字塔分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }        /// <summary>        /// 均值漂移分割算法        /// </summary>        /// <returns></returns>        private string PryMeanShiftFiltering()        {            //准备参数            Image<Bgr, Byte> imageDest = new Image<Bgr, byte>(imageSource.Size);            double spatialRadius = double.Parse(txtPMSFSpatialRadius.Text);            double colorRadius = double.Parse(txtPMSFColorRadius.Text);            int maxLevel = int.Parse(txtPMSFNaxLevel.Text);            int maxIter = int.Parse(txtPMSFMaxIter.Text);            double epsilon = double.Parse(txtPMSFEpsilon.Text);            MCvTermCriteria termcrit = new MCvTermCriteria(maxIter, epsilon);            //均值漂移分割            Stopwatch sw = new Stopwatch();            sw.Start();            OpenCvInvoke.cvPyrMeanShiftFiltering(imageSource.Ptr, imageDest.Ptr, spatialRadius, colorRadius, maxLevel, termcrit);            sw.Stop();            //显示结果            pbResult.Image = imageDest.Bitmap;            //释放资源            imageDest.Dispose();            return string.Format("均值漂移分割,用时:{0:F05}毫秒。\r\n", sw.Elapsed.TotalMilliseconds);        }        /// <summary>        /// 当改变金字塔分割的参数“金字塔层数”时,对参数进行校验        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void txtPSLevel_TextChanged(object sender, EventArgs e)        {            int level = int.Parse(txtPSLevel.Text);            if (level < 1 || imageSource.Width % (int)(Math.Pow(2, level - 1)) != 0 || imageSource.Height % (int)(Math.Pow(2, level - 1)) != 0)                MessageBox.Show(this, "注意:您输入的金字塔层数不符合要求,计算结果可能会无效。", "金字塔层数错误");        }        /// <summary>        /// 当改变均值漂移分割的参数“金字塔层数”时,对参数进行校验        /// </summary>        /// <param name="sender"></param>        /// <param name="e"></param>        private void txtPMSFNaxLevel_TextChanged(object sender, EventArgs e)        {            int maxLevel = int.Parse(txtPMSFNaxLevel.Text);            if (maxLevel < 0 || maxLevel > 8)                MessageBox.Show(this, "注意:均值漂移分割的金字塔层数只能在0至8之间。", "金字塔层数错误");        }    }}
图像分割 Image Segmentation
           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

相关文章:

  • 2022-12-23
  • 2021-06-17
  • 2022-01-06
  • 2021-12-13
  • 2020-02-26
  • 2022-01-01
  • 2021-04-12
猜你喜欢
  • 2021-11-04
  • 2021-12-02
  • 2021-11-28
  • 2022-12-23
  • 2021-04-14
  • 2021-05-01
  • 2021-07-27
相关资源
相似解决方案