这篇文章目的是使用opencvsharp里面的方法对一副比较有明显轮廓的图像进行抠图旋转;
接下来我们会对一副人名币进行旋转矫正,最后得到我们想要获取的特征图像:
先使用二值化对图像进行处理,然后在寻找轮廓,因为这张图比较简单,所以我们寻找起来也比较方便,二值化的阈值设置为50即可完美扣下图形;
Mat src = new Mat(@"D:\BaiduNetdiskDownload\人名币.png");
//Cv2.ImShow("src", src);
Mat gray = new Mat();
Mat binary=new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, binary, 50, 255, ThresholdTypes.Binary);//转换为二值图像
//Cv2.ImShow("bin", binary);
//然后寻找轮廓,只需要寻找外围轮廓即可
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
得到轮廓之后,我们可以使用ApproxPolyDP方法获得凸包(可以省略),再使用MinAreaRect方法获得最小外接矩形,这个时候我们得到的其实就是一个围着这张人民币的一个带角度的矩形,当然可能会有一些噪音信息,我们接下来有办法过滤
RotatedRect[] rotateRect = new RotatedRect[contours.Length];
Point[][] contours_poly = new Point[contours.Length][];
for (int i = 0; i < contours.Length; i++)
{
contours_poly[i] = Cv2.ApproxPolyDP(contours[i], 30, true);//返回凸包,单线长大于30过滤
rotateRect[i] = Cv2.MinAreaRect(contours_poly[i]);//最小外接矩形集合
}
我们可以在rotateRect[i].Angle中找到每个被检测出来矩形的信息,根据这些信息进行过滤即可
for (int i = 0; i < rotateRect.Length; i++)
{
float angle = rotateRect[i].Angle;//矩形角度
pot = rotateRect[i].Points();//矩形的4个角
double line1 = Math.Sqrt((pot[0].X - pot[1].X) * (pot[0].X - pot[1].X) + (pot[0].Y - pot[1].Y) * (pot[0].Y - pot[1].Y));
double line2= Math.Sqrt((pot[0].X - pot[3].X) * (pot[0].X - pot[3].X) + (pot[0].Y - pot[3].Y) * (pot[0].Y - pot[3].Y));
if (line1 * line2 < 1000)//过滤,太小的矩形直接pass
{
continue;
}
if (line1>line2)//依据实际情况进行判断
{
angle += 90;
}
}
接下来通过WarpAffine函数旋转,将得到的图扣下来放正即可,旋转角度已经在上面代码通过 rotateRect[i].Angle得到
Mat Roi = new Mat(src.Size(), MatType.CV_8UC3);
Roi.SetTo(0);//全黑
Cv2.DrawContours(binary, contours,-1, Scalar.White, -1);//在二值图像中圈出轮廓区域并染白
Cv2.ImShow("bin", binary);
//这里可以对binary做一些开操作过滤噪声,这里就不演示了
src.CopyTo(Roi, binary);//将原图通过mask抠图到Roi
Cv2.ImShow("Roi", Roi);
Mat afterRotato = new Mat(src.Size(), MatType.CV_8UC3);
afterRotato.SetTo(0);
Point2f center = rotateRect[i].Center;
Mat M = Cv2.GetRotationMatrix2D(center, angle, 1);//计算变换矩阵
Cv2.WarpAffine(Roi, afterRotato, M, Roi.Size(), InterpolationFlags.Linear, BorderTypes.Constant);//得到变换后的图像,滤除其他信息
Cv2.ImShow("旋转后", afterRotato);
这里因为binary没做开操作导致有些白线,但是不影响我们接下来的操作,接下来再重新寻找旋转后的轮廓和抠图就能得到我们想要的结果了
下面贴附我调试过程的原码:
Mat src = new Mat(@"D:\BaiduNetdiskDownload\人名币.png");
Cv2.ImShow("src", src);
#region
//Mat rotateImage = new Mat(src.Rows, src.Cols, MatType.CV_8UC3);
//rotateImage.SetTo(0);
//Mat M= Cv2.GetRotationMatrix2D(new Point2f(300, 300), 30, 0.5);
//Cv2.WarpAffine(src, rotateImage, M, src.Size(), InterpolationFlags.WarpInverseMap);//flags: 插值算法标识符,有默认值INTER_LINEAR
//Cv2.ImShow("11", rotateImage);
//Cv2.WaitKey();
#endregion
Mat gray = new Mat();
Mat binary=new Mat();
Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(gray, binary, 50, 255, ThresholdTypes.Binary);//转换为二值图像
Cv2.ImShow("bin", binary);
//Cv2.WaitKey();
//建立轮廓接受数组
Point[][] contours;
HierarchyIndex[] hierarchy;
Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
//最小外接矩形接收数组
RotatedRect[] rotateRect = new RotatedRect[contours.Length];
Point[][] contours_poly = new Point[contours.Length][];
for (int i = 0; i < contours.Length; i++)
{
contours_poly[i] = Cv2.ApproxPolyDP(contours[i], 30, true);//返回凸包,单线长大于30过滤
rotateRect[i] = Cv2.MinAreaRect(contours_poly[i]);//最小外接矩形集合
//}
Console.WriteLine(rotateRect.Length);
Point2f[] pot = new Point2f[4];//新建点集合接收点集合
//for (int i = 0; i < rotateRect.Length; i++)
//{
float angle = rotateRect[i].Angle;//矩形角度
pot = rotateRect[i].Points();//矩形的4个角
double line1 = Math.Sqrt((pot[0].X - pot[1].X) * (pot[0].X - pot[1].X) + (pot[0].Y - pot[1].Y) * (pot[0].Y - pot[1].Y));
double line2= Math.Sqrt((pot[0].X - pot[3].X) * (pot[0].X - pot[3].X) + (pot[0].Y - pot[3].Y) * (pot[0].Y - pot[3].Y));
if (line1 * line2 < 1000)//过滤,太小的矩形直接pass
{
continue;
}
if (line1>line2)//依据实际情况进行判断
{
angle += 90;
}
Console.WriteLine(line1 );
Console.WriteLine( line2);
Mat Roi = new Mat(src.Size(), MatType.CV_8UC3);
Roi.SetTo(0);//全黑
Cv2.DrawContours(binary, contours,-1, Scalar.White, -1);//在二值图像中圈出轮廓区域并染白
Cv2.ImShow("bin", binary);
src.CopyTo(Roi, binary);//将原图通过mask抠图到Roi
Cv2.ImShow("Roi", Roi);
Mat afterRotato = new Mat(src.Size(), MatType.CV_8UC3);
afterRotato.SetTo(0);
Point2f center = rotateRect[i].Center;
Mat M = Cv2.GetRotationMatrix2D(center, angle, 1);//计算变换矩阵
Cv2.WarpAffine(Roi, afterRotato, M, Roi.Size(), InterpolationFlags.Linear, BorderTypes.Constant);//得到变换后的图像,滤除其他信息
Cv2.ImShow("旋转后", afterRotato);
Mat bin2 = new Mat();
Cv2.ImShow("after", afterRotato);
Cv2.CvtColor(afterRotato, bin2, ColorConversionCodes.BGR2GRAY);
Cv2.Threshold(bin2, bin2, 20, 255, ThresholdTypes.Binary);
Point[][] con;
HierarchyIndex[] temp;//接收矫正后的轮廓信息
Cv2.FindContours(bin2, out con,out temp, RetrievalModes.External, ContourApproximationModes.ApproxNone);
for (int j = 0; j < con.Length; j++)
{
Rect rect = Cv2.BoundingRect(con[j]);//直接使用矫正矩形,因为矫正后不需要再旋转
if (rect.Height*rect.Width<8000)//过滤干扰信息
{
continue;
}
Mat dstImg =new Mat(afterRotato,rect);
//string name = "dst" + i;//主要看调试的时候有几个结果
Cv2.ImShow("dst", dstImg);
}
}
Cv2.WaitKey();
Console.ReadLine();