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;
namespace NewFeaturesOfOpenCV2._1
{
public partial class FormGrabCut : Form
{
//常量
private static readonly Bgr Blue = new Bgr(255d, 0d, 0d); //蓝色,用于绘制矩形
private static readonly Bgr Green = new Bgr(0d, 255d, 0d); //绿色,用于绘制前景曲线
private static readonly Bgr Red = new Bgr(0d, 0d, 255d); //红色,用于绘制背景曲线
private const int LineWidth = 5; //绘制线条的宽度
private const int GC_BGD = 0; //背景标志
private const int GC_FGD = 1; //前景标志
private const int GC_PR_BGD = 2; //可能的背景标志
private const int GC_PR_FGD = 3; //可能的前景标志
//成员变量
private string sourceImageFileName = "wky_tms_2272x1704.jpg";//源图像文件名
private Image<Bgr, Byte> imageSource = null; //源图像
private Image<Bgr, Byte> imageSourceClone = null; //源图像的克隆
private Image<Gray, Byte> imageMask = null; //掩码图像:保存初始化之后的掩码信息及用户绘制的信息
private Matrix<Single> foregroundModel = null; //前景模型
private Matrix<Single> backgroundModel = null; //背景模型
private double xScale = 1d; //原始图像与PictureBox在x轴方向上的缩放
private double yScale = 1d; //原始图像与PictureBox在y轴方向上的缩放
private Point previousMouseLocation = new Point(-1, -1); //上次绘制线条时,鼠标所处的位置
private Rectangle rect; //初始化矩形窗口
private bool initialized = false; //是否已经初始化过GrabCut
public FormGrabCut()
{
InitializeComponent();
}
//加载窗体时
private void FormGrabCut_Load(object sender, EventArgs e)
{
//设置提示
toolTip.SetToolTip(rbRect, "使用鼠标在源图像绘制矩形窗口,在图像分割之前使用矩形窗口所在的区域进行初始化。");
toolTip.SetToolTip(rbMask, "使用鼠标在源图像绘制掩码,左键绘制前景掩码,邮件绘制背景掩码,在图像分割之前使用掩码图像进行初始化。");
//初始化前景模型和背景模型
foregroundModel = new Matrix<float>(1, 13 * 5);
backgroundModel = new Matrix<float>(1, 13 * 5);
//加载默认图像
LoadImage();
}
//关闭窗体前,释放资源
private void FormGrabCut_FormClosing(object sender, FormClosingEventArgs e)
{
if (imageSource != null)
imageSource.Dispose();
if (imageSourceClone != null)
imageSourceClone.Dispose();
if (imageMask != null)
imageMask.Dispose();
if (foregroundModel != null)
foregroundModel.Dispose();
if (backgroundModel != null)
backgroundModel.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 btnReload_Click(object sender, EventArgs e)
{
LoadImage();
}
//加载源图像
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 (imageMask != null)
imageMask.Dispose();
imageMask = new Image<Gray, byte>(imageSource.Size);
imageMask.SetZero();
xScale = 1d * imageSource.Width / pbSource.Width;
yScale = 1d * imageSource.Height / pbSource.Height;
rect = new Rectangle(-1, -1, 1, 1);
initialized = false;
}
//鼠标在源图像上按下时
private void pbSource_MouseDown(object sender, MouseEventArgs e)
{
if (rbRect.Checked)
rect = new Rectangle((int)(e.X * xScale), (int)(e.Y * yScale), 1, 1);
else
previousMouseLocation = new Point((int)(e.X * xScale), (int)(e.Y * yScale));
}
//鼠标在源图像上移动时
private void pbSource_MouseMove(object sender, MouseEventArgs e)
{
//绘制矩形
if (rbRect.Checked && e.Button != MouseButtons.None)
{
rect = new Rectangle(rect.Left, rect.Top, (int)(e.X * xScale - rect.Left), (int)(e.Y * yScale - rect.Top));
imageSourceClone.Dispose();
imageSourceClone = imageSource.Clone();
imageSourceClone.Draw(rect, Blue, LineWidth);
pbSource.Image = imageSourceClone.Bitmap;
return;
}
//绘制线条,用于手工标记前景或者背景
if (rbMask.Checked && (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right))
{
if (previousMouseLocation.X == -1 && previousMouseLocation.Y == -1)
{
previousMouseLocation.X = (int)(e.X * xScale);
previousMouseLocation.Y = (int)(e.Y * yScale);
}
else
{
LineSegment2D line = new LineSegment2D(previousMouseLocation, new Point((int)(e.X * xScale), (int)(e.Y * yScale)));
if (e.Button == MouseButtons.Left)
{
imageMask.Draw(line, new Gray((double)GC_FGD), LineWidth);
imageSourceClone.Draw(line, Green, LineWidth);
}
else
{
imageMask.Draw(line, new Gray((double)GC_BGD), LineWidth);
imageSourceClone.Draw(line, Red, LineWidth);
}
pbSource.Image = imageSourceClone.Bitmap;
previousMouseLocation = line.P2;
}
}
}
//鼠标在源图像上松开时
private void pbSource_MouseUp(object sender, MouseEventArgs e)
{
if (rbRect.Checked && e.Button != MouseButtons.None)
{
rect = new Rectangle(rect.Left, rect.Top, (int)(e.X * xScale - rect.Left), (int)(e.Y * yScale - rect.Top));
imageSourceClone.Dispose();
imageSourceClone = imageSource.Clone();
imageSourceClone.Draw(rect, Blue, LineWidth);
pbSource.Image = imageSourceClone.Bitmap;
//绘制矩形结束之后,初始化掩码图像
imageMask.SetZero();
imageMask.Draw(rect, new Gray((double)GC_PR_FGD), 0);
return;
}
if (rbMask.Checked)
previousMouseLocation = new Point(-1, -1);
}
//开始图像分割
private void btnStartSegment_Click(object sender, EventArgs e)
{
if (rect != new Rectangle(-1, -1, 1, 1)) //必须指定矩形窗
{
Stopwatch sw = new Stopwatch();
Image<Gray, Byte> mask = null;
if (rbRect.Checked)
{
//用矩形窗初始化
sw.Reset();
sw.Start();
mask = imageSource.GrabCut(rect, (int)nudIterCount.Value); //注:Image.GrabCut等价于先用矩形初始化CvGrabCut(....,GRABCUT_INIT_TYPE.INIT_WITH_RECT),然后再计算CvGrabCut(....,GRABCUT_INIT_TYPE.INIT_WITH_EVAL)
sw.Stop();
imageMask = mask.Clone();
initialized = true;
ShowResult("用矩形窗初始化GrabCut并计算", sw.ElapsedMilliseconds);
}
else
{
//用掩码初始化
mask = imageMask.Clone();
if (!initialized)
{
sw.Reset();
sw.Start();
CvInvoke.CvGrabCut(imageSource.Ptr, mask.Ptr, ref rect, backgroundModel.Ptr, foregroundModel.Ptr, 1, GRABCUT_INIT_TYPE.INIT_WITH_MASK);
sw.Stop();
initialized = true;
ShowResult("用掩码初始化GrabCut", sw.ElapsedMilliseconds);
}
sw.Reset();
sw.Start();
CvInvoke.CvGrabCut(imageSource.Ptr, mask.Ptr, ref rect, backgroundModel.Ptr, foregroundModel.Ptr, (int)nudIterCount.Value, GRABCUT_INIT_TYPE.EVAL);
sw.Stop();
ShowResult("计算GrabCut", sw.ElapsedMilliseconds);
}
CvInvoke.cvAndS(mask.Ptr, new MCvScalar(1d), mask.Ptr, IntPtr.Zero); //将掩码图像和1进行按位“与”操作,这样背景及可能的背景将变为0;而前景及可能的前景将变成1
Image<Bgr, Byte> result = imageSource.Copy(mask);
pbResult.Image = result.Bitmap;
mask.Dispose();
//result.Dispose();
}
else
MessageBox.Show(this, "在开始分割之前,请在源图像上绘制一个矩形窗口。", "缺少矩形窗", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
/// <summary>
/// 显示结果
/// </summary>
/// <param name="prompt">提示</param>
/// <param name="elapsedMilliseconds">耗时</param>
private void ShowResult(string prompt, double elapsedMilliseconds)
{
txtResult.Text += string.Format("{0},耗时:{1:F04}毫秒,参数(矩形窗起点:{2},大小:{3}X{4},迭代次数:{5})。\r\n",
prompt, elapsedMilliseconds, rect.Location, rect.Width, rect.Height, nudIterCount.Value);
}
}
}