C#+OpenCV实战(一)_图片简易角度矫正
/// <summary>
/// 图像角度矫正-纸张
/// </summary>
/// <param name="src">图片</param>
/// <returns>结果图片</returns>
/// <exception cref="Exception"></exception>
public static Mat ImageAngle_Correction(Mat src)
{
Mat src2 = new Mat();
src.CopyTo(src2);
InputArray kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
Cv2.MorphologyEx(src, src, MorphTypes.Close, kernel, new Point(-1, -1), 3);
// 高斯滤波
Cv2.GaussianBlur(src, src, new Size(11, 11), 2, 2);
// Canny边缘检测
Mat canny_Image = new Mat();
Cv2.Canny(src, canny_Image, 10, 30, 3, false);
// 找轮廓
/*
* 找轮廓(输入图像,out 轮廓集合,out 级别,轮廓检索模式,近似法,偏移量)
* 输入图像:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
* 轮廓集合:contours
* 历史轮廓:hierarchy:0:后一个轮廓,1:前一个轮廓,2:父轮廓,3:内嵌轮廓
* 轮廓检索模式:轮廓的检索模式
* 取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
* 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
* 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
* 取值四:CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
* 近似方法:轮廓的近似方法
* 取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
* 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
* 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
* 偏移量:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
*/
Cv2.FindContours(canny_Image, out Point[][] contours, out HierarchyIndex[] hierarchly,
RetrievalModes.External,
ContourApproximationModes.ApproxSimple,
new Point(0, 0));
if (contours.Length == 0)
{
throw new Exception("边缘检测失败");
}
Random rnd = new Random();
Scalar color;
color = new Scalar(0, 255, 0);
for (int i = 0; i < contours.Length; i++)
{
color = new Scalar(rnd.Next(0, 255), rnd.Next(0, 255), rnd.Next(0, 255));
Cv2.DrawContours(src, contours, i, color, 2, LineTypes.Link4);
}
//Cv2.ImShow("contours", src);
//求出面积最大的轮廓
double max_area = 0.0;
double currentArea = 0.0;
Point[] max_contour = null;
for (int i = 0; i < contours.Length; i++)
{
currentArea = Cv2.ContourArea(contours[i]);
if (currentArea > max_area)
{
max_area = currentArea;
max_contour = contours[i];
}
}
//多边形拟合凸包的四个顶点
Point[] hull = Cv2.ConvexHull(max_contour);
double epsilon = 0.02 * Cv2.ArcLength(max_contour, true);
Point[] approx = Cv2.ApproxPolyDP(hull, epsilon, true);
if (approx.Length != 4)
{
throw new Exception("拟合凸包的四个顶点失败");
}
Scalar scalar2 = new Scalar(0, 255, 255);
Cv2.Line(src, approx[0], approx[1], scalar2, 1, LineTypes.Link4);
Cv2.Line(src, approx[1], approx[2], scalar2, 1, LineTypes.Link4);
Cv2.Line(src, approx[2], approx[3], scalar2, 1, LineTypes.Link4);
Cv2.Line(src, approx[3], approx[0], scalar2, 1, LineTypes.Link4);
//排序
Array.Sort(approx, (cs1, cs2) =>
{
if (cs1.Y > cs2.Y)
{
return 1;
}
else if (cs1.Y == cs2.Y)
{
return cs1.X < cs2.X ? 1 : -1;
}
else
{
return -1;
}
});
//算法找出的角点
Point2f[] srcPt = [approx[0], approx[1], approx[3], approx[2]];
//最小外接矩形
RotatedRect rect = Cv2.MinAreaRect(srcPt);
Rect box = rect.BoundingRect();
Point2f[] dstPt = new Point2f[4];
dstPt[0].X = box.X;
dstPt[0].Y = box.Y;
dstPt[1].X = box.X + box.Width;
dstPt[1].Y = box.Y;
dstPt[2].X = box.X + box.Width;
dstPt[2].Y = box.Y + box.Height;
dstPt[3].X = box.X;
dstPt[3].Y = box.Y + box.Height;
Mat final = new Mat();
Mat warpmatrix = Cv2.GetPerspectiveTransform(srcPt, dstPt); // 获得变换矩阵
Cv2.WarpPerspective(src2, final, warpmatrix, src.Size()); // 投射变换,将结果赋给final
return final;
}
本文来自博客园,作者:꧁执笔小白꧂,转载请注明原文链接:https://www.cnblogs.com/qq2806933146xiaobai/p/18295821