ID #5727

图像分割相关算法

前言

图像分割(Image Segmentation)指的是将数字图像细分为多个图像子区域的过程,在OpenCv中实现 了三种跟图像分割相关的算法,它们分别是:分水岭分割算法、金字塔分割算法 以及均值漂移分割算法。它们的使用过程都很简单,下面的文章权且用于记录, 并使该系列保持完整吧。

分水岭分割算法

分水岭分割算法需要您或者先前算法提供标记,该标记用于指定哪些大致区 域是目标,哪些大致区域是背景等等;分水岭分割算法的分割效果严重依赖于提 供的标记。OpenCv中的函数cvWatershed实现了该算法,函数定义如下:

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

其中:image为8为三通道的彩色图像;

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

生成标记图像

//当鼠标按下并在源图像上移动时,在源图像上绘制分割线条
     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++;
     }

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

使用分水岭分割算法

/// <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);
     }

金字塔分割算法

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

校验金字塔分割的金字塔层数

/// <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>
     /// <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);
     }

均值漂移分割算法

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

使用均值漂移分割算法

/// <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);
       //均值漂移分割---www.bianceng.cn
       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);
     }

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

调用均值漂移分割

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

分割效果及性能对比

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

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

图2 金字塔分割算法

图3 均值漂移分割算法

貫貧中厘断辛參心竃・

・1・蛍邦窓蛍護麻隈議蛍護丼惚丼惚恷挫・譲峙働卞蛍護麻隈肝岻・遇署忖 満蛍護麻隈議丼惚恷餓・

・2・譲峙働卞蛍護麻隈丼楕恷互・蛍邦窓蛍護麻隈俊除噐譲峙働卞麻隈・署 忖満蛍護麻隈俶勣載海議扮寂。

峙誼廣吭議頁蛍邦窓麻隈斤炎芝載樗湖・俶勣徙聾遇範寔議紙崙。

云猟議頼屁旗鷹泌和・

云猟頼屁旗鷹

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岻寂。", "署忖満蚊方危列");
     }
   }
}


2011-07-01 18:29
阅读:
I'm VC , Just U know Y
本站部分文章来源于互联网,版权归原作者所有。

延伸阅读:

单链表相关算法