Cv2.FloodFill(),水漫填充算法,是指用指定颜色填充与所选种子点像素的相连区域,常用于图像的分割,标记等。
函数原型:
//
// 摘要:
// Fills a connected component with the given color.
//
// 参数:
// image:
// Input/output 1- or 3-channel, 8-bit, or floating-point image. It is modified
// by the function unless the FLOODFILL_MASK_ONLY flag is set in the second variant
// of the function. See the details below.
//
// seedPoint:
// Starting point.
//
// newVal:
// New value of the repainted domain pixels.
//
// rect:
// Optional output parameter set by the function to the minimum bounding rectangle
// of the repainted domain.
//
// loDiff:
// Maximal lower brightness/color difference between the currently observed pixel
// and one of its neighbors belonging to the component, or a seed pixel being added
// to the component.
//
// upDiff:
// Maximal upper brightness/color difference between the currently observed pixel
// and one of its neighbors belonging to the component, or a seed pixel being added
// to the component.
//
// flags:
// Operation flags. Lower bits contain a connectivity value, 4 (default) or 8, used
// within the function. Connectivity determines which neighbors of a pixel are considered.
public static int FloodFill(InputOutputArray image, Point seedPoint, Scalar newVal, out Rect rect, Scalar? loDiff = default(Scalar?), Scalar? upDiff = default(Scalar?), FloodFillFlags flags = FloodFillFlags.Link4);
winform界面设计:
实现效果展示:
主要代码以及实现:
我的环境是 VS2015 Opencvsharp3
变量定义:
//在 yVars.ImgOptions 下
public struct FloodFill //水漫填充
{
// 水漫原图
public static string Image;
// 水漫填充颜色
public static OpenCvSharp.Scalar fillColor = new Scalar();
// 水漫种子点坐标
public static OpenCvSharp.Point pointLocation = new OpenCvSharp.Point();
// 线程启停信号
public static ManualResetEvent getLocationEvent = new ManualResetEvent(false);
// 种子点选取标志
public static bool IsSelectPoint = false;
public static int loDiff = 0; // 水漫负差最大值
public static int upDiff = 0; // 水漫正差最大值
//水漫后图片存取队列 用于撤销操作 水漫一次就往此list中添加一张结果图
public static List matList = new List();
// 更换原图标志位
public static bool IsChangePic = false;
}
主要方法:
public static void ImgFloodFill()
{
// 在水漫图片的队列的最后一张图片上进行操作
Mat pic = new Mat();
int index = yVars.ImgOptions.FloodFill.matList.Count;
pic = yVars.ImgOptions.FloodFill.matList[index - 1];
Mat picOut = new Mat();
// 水漫图像需要 BGR三通道图片
Cv2.CvtColor(pic, picOut, ColorConversionCodes.BGRA2BGR);
OpenCvSharp.Rect re = new Rect();
// 正负差最大值对应的颜色
Scalar loDiffColor = new OpenCvSharp.Scalar(yVars.ImgOptions.FloodFill.loDiff, yVars.ImgOptions.FloodFill.loDiff, yVars.ImgOptions.FloodFill.loDiff);
Scalar upDiffColor = new OpenCvSharp.Scalar(yVars.ImgOptions.FloodFill.upDiff, yVars.ImgOptions.FloodFill.upDiff, yVars.ImgOptions.FloodFill.upDiff);
// 将在图像种子点位置像素的正负差最大值之间的所有与种子点相连同的所有像素点设置成指定颜色
Cv2.FloodFill(picOut, yVars.ImgOptions.FloodFill.pointLocation, yVars.ImgOptions.FloodFill.fillColor, out re,
loDiffColor, upDiffColor, FloodFillFlags.Link8);
// 比较水漫前后两张图是否一样 如果一样则不需要添加到队列 方便撤销操作
if (!yOthers.matIsEqual(pic, picOut))
{
yVars.ImgOptions.FloodFill.matList.Add(picOut);
Form1.Instance.pbxFloodFill.Image = yImgConvert.MatToBitmap(picOut);
}
yVars.ImgOptions.FloodFill.IsSelectPoint = false;
Form1.Instance.btnSelectPoint.BackColor = Color.Transparent;
yVars.ImgOptions.FloodFill.pointLocation = new OpenCvSharp.Point(0, 0);
Form1.Instance.tbxFlood_x.Text = "0";
Form1.Instance.tbxFlood_y.Text = "0";
yVars.ImgOptions.FloodFill.getLocationEvent.Reset();
}
// 获取种子坐标线程
// 在点击选择点位之后 当鼠标进入picturebox后 启动下面线程
// 当在picturebox上点击鼠标时 暂停下面线程 并将此时鼠标点击位置设置成水漫种子点
public static Thread GetLocationTask = new Thread(() =>
{
while (true)
{
int MX = 0, MY = 0;
OpenCvSharp.Mat mm = new OpenCvSharp.Mat();
mm = yImgConvert.ImageToMat(Form1.Instance.pbxFloodFill.Image);
Form1.Instance.Invoke(new Action(() =>
{
// 鼠标相对于picturebox的位置
MX = Form1.Instance.pbxFloodFill.PointToClient(Control.MousePosition).X;
MY = Form1.Instance.pbxFloodFill.PointToClient(Control.MousePosition).Y;
if (MX <= Form1.Instance.pbxFloodFill.Width && MY <= Form1.Instance.pbxFloodFill.Height && MX > 0 && MY > 0)
{
Form1.Instance.tbxFlood_x.Text = MX.ToString();
Form1.Instance.tbxFlood_y.Text = MY.ToString();
// 图片是经过缩放后再放入picturebox中
// 大小并不一样 所以需要将相对于picturebox的坐标 转换成相对于对应图片坐标
double xx = MX * mm.Width / Form1.Instance.pbxFloodFill.Width;
double yy = MY * mm.Height / Form1.Instance.pbxFloodFill.Height;
yVars.ImgOptions.FloodFill.pointLocation = new OpenCvSharp.Point(xx, yy);
}
else
{
Form1.Instance.tbxFlood_x.Text = "0";
Form1.Instance.tbxFlood_y.Text = "0";
yVars.ImgOptions.FloodFill.pointLocation = new OpenCvSharp.Point(0, 0);
}
}));
yVars.ImgOptions.FloodFill.getLocationEvent.WaitOne();
Thread.Sleep(100);
}
});
// 判断两张多通道图片是否完全一样
public static bool matIsEqual(Mat mat1, Mat mat2)
{
if (mat1.Empty() && mat2.Empty())
{
return true;
}
if (mat1.Cols != mat2.Cols || mat1.Rows != mat2.Rows || mat1.Dims() != mat2.Dims())
{
return false;
}
if (mat1.Size() != mat2.Size() || mat1.Channels() != mat2.Channels() || mat1.Type() != mat2.Type())
{
return false;
}
if (mat1.Total() * mat1.ElemSize() != mat2.Total() * mat2.ElemSize())
{
return false;
}
Mat tempMat = new Mat();
Cv2.BitwiseXor(mat1, mat2, tempMat);
Mat[] mm = Cv2.Split(tempMat);
foreach (Mat m1 in mm)
{
if (Cv2.CountNonZero(m1) != 0)
return false;
}
return true;
}
主要控件事件:
// 选择水漫填充颜色
private void btnSelectFloodColor_Click(object sender, EventArgs e)
{
ColorDialog cc = new ColorDialog();
if (cc.ShowDialog() == DialogResult.OK)
{
yVars.ImgOptions.FloodFill.fillColor = OpenCvSharp.Scalar.FromRgb(cc.Color.R, cc.Color.G, cc.Color.B);
palFloodColor.BackColor = cc.Color;
}
}
// 选择点位点击事件
private void btnSelectPoint_Click(object sender, EventArgs e)
{
if (yVars.ImgOptions.FloodFill.Image == null)
{
YXH._01.yMessagebox.Show_CN("未选择图片");
return;
}
if (yVars.ImgOptions.FloodFill.IsSelectPoint == false)
{
yVars.ImgOptions.FloodFill.IsSelectPoint = true;
btnSelectPoint.BackColor = Color.Green;
yVars.ImgOptions.FloodFill.IsChangePic = false;
btnSelectFloodfill.Enabled = false;
}
else
{
yVars.ImgOptions.FloodFill.IsSelectPoint = false;
btnSelectPoint.BackColor = Color.Transparent;
}
}
// 点击选择点位按钮后 当鼠标进入picturebox后启动获取鼠标点位的线程
private void pbxFloodFill_MouseEnter(object sender, EventArgs e)
{
if (yVars.ImgOptions.FloodFill.IsSelectPoint == false)
return;
if (pbxFloodFill.Image == null)
return;
yVars.ImgOptions.FloodFill.getLocationEvent.Set();
if (!yImgOptions.GetLocationTask.IsAlive)
yImgOptions.GetLocationTask.Start();
}
// picturebo 点击时 暂停线程 画出选择点位
private void pbxFloodFill_Click(object sender, EventArgs e)
{
if (yVars.ImgOptions.FloodFill.pointLocation != null && yVars.ImgOptions.FloodFill.IsSelectPoint == true && yVars.ImgOptions.FloodFill.pointLocation != new OpenCvSharp.Point(0, 0))
{
OpenCvSharp.Mat spMat = new OpenCvSharp.Mat();
spMat = yImgConvert.ImageToMat(Form1.Instance.pbxFloodFill.Image);
OpenCvSharp.Cv2.Circle(spMat, yVars.ImgOptions.FloodFill.pointLocation, 0, OpenCvSharp.Scalar.Red, 2, LineTypes.AntiAlias);
pbxFloodFill.Image = yImgConvert.MatToBitmap(spMat);
btnSelectFloodfill.Enabled = true;
}
yVars.ImgOptions.FloodFill.getLocationEvent.Reset();
yVars.ImgOptions.FloodFill.IsSelectPoint = false;
btnSelectPoint.BackColor = Color.Transparent;
}
// 图像复原
private void btnResetPic_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(yVars.ImgOptions.FloodFill.Image))
pbxFloodFill.Image = new Bitmap(yVars.ImgOptions.FloodFill.Image);
}
// 撤销操作 从水漫图片队列中 选择倒数第二张显示 并删除最后一张
private void btnRepeal_Click(object sender, EventArgs e)
{
int index = 0;
index = yVars.ImgOptions.FloodFill.matList.Count;
if (index <= 1)
return;
else if (index > 1)
{
yVars.ImgOptions.FloodFill.matList.RemoveAt(index - 1);
index = yVars.ImgOptions.FloodFill.matList.Count;
Form1.Instance.pbxFloodFill.Image = yImgConvert.MatToBitmap(yVars.ImgOptions.FloodFill.matList[index - 1]);
}
}