版权声明:本文为博主原创文章,转载请在显著位置标明本文出处以及作者网名,未经作者允许不得用于商业目的。
EmguCV是一个基于OpenCV的开源免费的跨平台计算机视觉库,它向C#和VB.NET开发者提供了OpenCV库的大部分功能。
教程VB.net版本请访问:EmguCV学习笔记 VB.Net 目录-CSDN博客
教程C#版本请访问:EmguCV学习笔记 C# 目录-CSDN博客
笔者的博客网址:https://blog.csdn.net/uruseibest
教程配套文件及相关说明以及如何获得pdf教程和代码,请移步:EmguCV学习笔记
学习VB.Net知识,请移步: vb.net 教程 目录_vb中如何用datagridview-CSDN博客
学习C#知识,请移步:C# 教程 目录_c#教程目录-CSDN博客
11.6 图像分割
11.6.1 语义分割Fcn
FCN(Fully Convolutional Network)是一种流行的语义分割算法,它通过将传统的卷积神经网络转化为全卷积网络来实现像素级别的语义分割,将输入图像传入网络中,得到每个像素的标签,并根据标签来分割出不同的物体。FCN在语义分割任务中表现优秀,可以实现像素级别的精细分割,被广泛应用于自动驾驶、智能监控等领域。
【代码位置:frmChapter11】Button5_Click、getColorTable、showColorTable
//语义分割fcn
private void Button5_Click(object sender, EventArgs e)
{
//对象分类,object_detection_classes_pascal_voc.txt文件提供了21类对象(含background)
string[] classnames = System.IO.File.ReadAllLines("C:\\learnEmgucv\\fcn\\object_detection_classes_pascal_voc.txt");
//获得输出颜色表
Bgr[] colorTable = getColorTable();
//显示颜色表及对应对象名称
showColorTable(classnames, colorTable);
//需要测试的图像文件
Mat m = new Mat("C:\\learnEmgucv\\dnntest1.jpg", ImreadModes.Color);
Single hm = m.Height;
Single wm = m.Width;
Net net;
net = DnnInvoke.ReadNetFromCaffe("C:\\learnEmgucv\\fcn\\fcn8s-heavy-pascal.prototxt",
"C:\\learnEmgucv\\fcn\\fcn8s-heavy-pascal.caffemodel"
);
Mat mcopy = new Mat();
CvInvoke.Resize(m, mcopy, new Size(500, 500));
Mat blob = DnnInvoke.BlobFromImage(mcopy, 1, new Size(500, 500),
new MCvScalar(0,0,0), false, false);
net.SetInput(blob);
Mat mout = new Mat();
mout = net.Forward();
Single[,,,] fout;
fout = (Single[,,,])mout.GetData();
//通道数=21,即21类对象
int chan = fout.GetLength(1);
//高度=500
int row = fout.GetLength(2);
//宽度=500
int col = fout.GetLength(3);
//记录21个通道对应坐标点的最大值
Matrix<Single> matrMaxValue = new Matrix<Single>(new Size(row, col));
matrMaxValue.SetZero();
记录对应通道号
//Matrix<Single> matrMaxChan = new Matrix<Single>(new Size(row, col));
//matrMaxChan.SetZero();
//记录对应颜色
Image<Bgr, byte> imgOut = new Image<Bgr, byte>(col, row);
imgOut.SetZero();
//遍历21个通道
for (int c = 0; c < chan; c++)
for (int h = 0; h < row; h++)
//遍历高度和宽度,获得对应坐标的值
for (int w = 0; w < col; w++)
//比较最大值
if (fout[0, c, h, w] > matrMaxValue[h, w])
{
//取得最大值
matrMaxValue[h, w] = fout[0, c, h, w];
取得通道号
//matrMaxChan[h, w] = c;
//最重要的是获取通道对应颜色表的值
imgOut[h, w] = colorTable[c];
}
//显示输出的图像
//ImageBox1.Image = imgOut.Mat;
//设置掩膜
Mat mask = new Mat();
mask = imgOut.Mat;
//掩膜大小必须和源图像一致
CvInvoke.Resize(mask, mask, m.Size);
Mat mFinalOut = new Mat();
CvInvoke.AddWeighted(m, 0.3, mask, 0.7, 0, mFinalOut);
ImageBox1.Image = mFinalOut;
}
//为了更好地观察,这里没有使用随机颜色
private Bgr[] getColorTable()
{
Bgr[] newColors = new Bgr[21];
newColors[0] = new Bgr(0, 0, 0); //background
newColors[1] = new Bgr(128, 0, 0); //aeroplane
newColors[2] = new Bgr(0, 128, 0); //bicycle
newColors[3] = new Bgr(128, 128, 0); //bird
newColors[4] = new Bgr(0, 0, 128); //boat
newColors[5] = new Bgr(128, 0, 128); //bottle
newColors[6] = new Bgr(0, 128, 128); //bus
newColors[7] = new Bgr(128, 128, 128); //car
newColors[8] = new Bgr(255, 0, 0); //cat
newColors[9] = new Bgr(0, 255, 0); //chair
newColors[10] = new Bgr(0, 0, 255); //cow
newColors[11] = new Bgr(255, 255, 0); //diningtable
newColors[12] = new Bgr(64, 0, 128); //dog
newColors[13] = new Bgr(192, 0, 128); //horse
newColors[14] = new Bgr(64, 128, 128); //motorbike
newColors[15] = new Bgr(192, 128, 128); //person
newColors[16] = new Bgr(0, 64, 0); //pottedplant
newColors[17] = new Bgr(128, 64, 64); //sheep
newColors[18] = new Bgr(0, 192, 0); //sofa
newColors[19] = new Bgr(128, 192, 0); //train
newColors[20] = new Bgr(0, 64, 128); //tvmonitor
return newColors;
}
//显示颜色表及对象名称
//参数1:对象名称的字符串数组
//参数2:bgr颜色数组
private void showColorTable(string[] names, Bgr[] colors)
{
List<Mat> lstmoutV = new List<Mat>();
for (int i = 0; i <= 20; i++)
{
Mat moutV = new Mat(40, 200, DepthType.Cv8U, 3);
moutV.SetTo(new MCvScalar(colors[i].Blue, colors[i].Green, colors[i].Red));
CvInvoke.PutText(moutV, names[i], new Point(20, 25), FontFace.HersheyTriplex, 0.4, new MCvScalar(255, 255, 255));
lstmoutV.Add(moutV);
}
Mat mout = new Mat();
//垂直方向拼接
CvInvoke.VConcat(lstmoutV.ToArray(), mout);
CvInvoke.Imshow("colortable", mout);
}
输出结果如下图所示:
图11-4 FCN分割获得不同对象区域
11.6.2 实例分割 MASK RCNN
Mask RCNN是一种基于Faster R-CNN的实例分割算法,它是一种联合目标检测和语义分割的方法,能够同时检测图像中的对象并得到每个对象的位置、类别和像素级别的分割结果。Mask RCNN在实例分割任务中表现优秀,是目前最先进的实例分割算法之一,被广泛应用于自动驾驶、智能监控等领域。
【代码位置:frmChapter11】Button6_Click、getRadomColor
//实例分割 mask rcnn
private void Button6_Click(object sender, EventArgs e)
{
//对象分类,object_detection_classes_coco.txt文件提供了90类对象(含background)
string[] classnames;
classnames = System.IO.File.ReadAllLines("C:\\learnEmgucv\\maskrcnn\\object_detection_classes_coco.txt");
//需要测试的图像文件
Mat m = new Mat("C:\\learnEmgucv\\dnntest.jpg", ImreadModes.Color);
Single hm = m.Height;
Single wm = m.Width;
Net net;
net = DnnInvoke.ReadNetFromTensorflow("C:\\learnEmgucv\\maskrcnn\\frozen_inference_graph.pb",
"C:\\learnEmgucv\\maskrcnn\\mask_rcnn_inception_v2_coco_2018_01_28.pbtxt"
);
Mat mcopy = new Mat();
mcopy = m.Clone();
Mat blob;
blob = DnnInvoke.BlobFromImage(mcopy, 1, new Size(500, 500),
new MCvScalar(0, 0, 0), false, false);
net.SetInput(blob);
string[] names = new string[2];
names[0] = "detection_out_final";
names[1] = "detection_masks";
//返回的mout包含两个mat
VectorOfMat mout = new VectorOfMat();
net.Forward(mout, names);
//第一个mat标识返回的置信度候选框,是一个四维数组
Mat moutBox = new Mat();
moutBox = mout[0];
//返回维度:
//第1维:1,
//第2维:1,
//第3维:100,100个候选置信矩形框
//第4维:7,0:?;1:对应类别;2:置信度;3-6:候选框位置(源图像百分比)
Single[,,,] foutBox;
foutBox = (Single[,,,])moutBox.GetData();
//第二个mat标识返回的掩膜,是一个四维数组
Mat moutMask = new Mat();
moutMask = mout[1];
//返回维度:
//第1维:100,100个对象对应的100个掩膜
//第2维:90,对象的置信度
//第3维:15,掩膜高度
//第4维:15,掩膜宽度
Single[,,,] foutMask;
foutMask = (Single[,,,])moutMask.GetData();
int maskH = 15; //foutMask.GetLength(2)
int maskW = 15; //foutMask.GetLength(3)
//新建Mat,用来在这上面绘制掩膜
Mat mbg = new Mat(new Size(m.Width, m.Height), DepthType.Cv8U, 3);
mbg.SetTo(new MCvScalar(0, 0, 0));
for (int i = 0; i < 100; i++)
{
//置信度
Single conf = foutBox[0, 0, i, 2];
//当置信度满足时
if (conf > 0.53)
{
//对应检测对象的序号
int objID = (int)foutBox[0, 0, i, 1];
Single ltX = foutBox[0, 0, i, 3] * wm; //左上角X
Single ltY = foutBox[0, 0, i, 4] * hm; //左上角Y
Single rbX = foutBox[0, 0, i, 5] * wm; //右下角X
Single rbY = foutBox[0, 0, i, 6] * hm; //右下角Y
Single w = rbX - ltX; //宽度
Single h = rbY - ltY; //高度
//绘制包围矩形框
CvInvoke.Rectangle(m, new Rectangle((int)ltX, (int)ltY,(int) w, (int)h), new MCvScalar(0, 0, 255), 1);
//绘制对象名称
CvInvoke.PutText(m, classnames[objID], new Point((int)ltX, (int)ltY - 10), FontFace.HersheyTriplex, 0.3, new MCvScalar(255, 0, 0));
//开始处理掩膜
Mat mmask = new Mat();
//掩膜大小为15*15,对应foutMask最后两个维度
Single[,] bmask = new Single[15, 15];
for (int j = 0; j < 15; j++)
for (int k = 0; k < 15; k++)
bmask[j, k] = foutMask[i, objID, j, k];
//将数组转为Mat
Matrix<Single> matrmask = new Matrix<Single>(bmask);
mmask = matrmask.Mat;
//大小放大与对应包围矩形框一致
CvInvoke.Resize(mmask, mmask, new Size((int)w, (int)h));
//二值化
CvInvoke.Threshold(mmask, mmask, 0.3, 255, ThresholdType.Binary);
//由于本身是CV32F,需要处理为CV8U,才能使用FindContours
mmask.ConvertTo(mmask, DepthType.Cv8U);
//定义关注区域,当修改关注区域时,也就修改了源图像
Mat mRoi = new Mat(mbg, new Rectangle((int)ltX, (int)ltY, (int)w, (int)h));
//查找轮廓
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
CvInvoke.FindContours(mmask, contours, null, RetrType.External, ChainApproxMethod.ChainApproxSimple);
//填充轮廓
CvInvoke.DrawContours(mRoi, contours, -1, getRadomColor(), -1);
}
}
Mat mresult = new Mat();
//加法
CvInvoke.AddWeighted(m, 1, mbg, 0.4, 0, mresult);
ImageBox1.Image = mresult;
}
//获得随机颜色
private MCvScalar getRadomColor()
{
Random rd = new Random(DateTime.Now.Millisecond);
byte r, g, b;
b = (byte)rd.Next(256);
g = (byte)rd.Next(256);
r = (byte)rd.Next(256);
return new MCvScalar(b, g, r);
}
输出结果如下图所示:
图11-5 Mask RCNN 分割得到对象区域和类别