本文实现的效果是通过OpenCVForUnity实现透视图片校正,主要参考文章是点击打开链接
效果如下:
OpenCVForUnity有封装好的计算透视变换矩阵的方法:
public static Mat getPerspectiveTransform (Mat src, Mat dst)
和通过透视变换矩阵实现透视变换的方法:
public static void warpPerspective (Mat src, Mat dst, Mat M, Size dsize)
灰度化图片
public static void cvtColor (Mat src, Mat dst, int code)
边缘处理
public static void Canny (Mat image, Mat edges, double threshold1, double threshold2)
霍夫曼线
public static void HoughLinesP (Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)
第一个参数为输入图像,应该为灰度图,
第二个参数为输出的检测到的直线的容器
第三个参数为以参数极径单位的分辨率
第四个是以弧度为单位的分辨率
第五个为一条直线所需最少的的曲线交点
计算霍夫曼线交叉点 并剔除不合理的点(这个方法根据实际计算出来的点位置自行调整)
private Vector2 ComputeIntersect(List a, List b)
private void CullIllegalPoint(ref List corners,float minDis)
完整代码:
using UnityEngine;
using System.Collections.Generic;
using OpenCVForUnity;
public class PerspectiveTransform: MonoBehaviour {
private List corners = new List();
// Use this for initialization
void Start () {
Texture2D inputTexture = Resources.Load("inputTexture") as Texture2D;
Mat inputMat = new Mat(inputTexture.height, inputTexture.width, CvType.CV_8UC4);
Mat outputMat = new Mat(inputTexture.height, inputTexture.width, CvType.CV_8UC4);
Utils.texture2DToMat(inputTexture, inputMat);
Imgproc.cvtColor(inputMat, outputMat, Imgproc.COLOR_RGB2GRAY);
Imgproc.Canny(outputMat, outputMat, 100, 150);
Mat lines = new Mat();
//第五个参数是阈值,通过调整阈值大小可以过滤掉一些干扰线
Imgproc.HoughLinesP(outputMat, lines, 1, Mathf.PI / 180, 60, 50, 10);
//计算霍夫曼线外围的交叉点
int[] linesArray = new int[lines.cols() * lines.rows() * lines.channels()];
lines.get(0, 0, linesArray);
Debug.Log("length of lineArray " + linesArray.Length);
List a = new List();
List b = new List();
for (int i = 0; i < linesArray.Length -4; i = i + 4)
{
Imgproc.line(inputMat, new Point(linesArray[i + 0], linesArray[i + 1]), new Point(linesArray[i + 2], linesArray[i + 3]), new Scalar(255, 0, 0), 2);
}
for (int i = 0; i < linesArray.Length; i = i+4)
{
a.Add(linesArray[i + 0]);
a.Add(linesArray[i + 1]);
a.Add(linesArray[i + 2]);
a.Add(linesArray[i + 3]);
for (int j = i+4; j < linesArray.Length; j= j + 4)
{
b.Add(linesArray[j + 0]);
b.Add(linesArray[j + 1]);
b.Add(linesArray[j + 2]);
b.Add(linesArray[j + 3]);
Vector2 temp = ComputeIntersect(a, b);
b.Clear();
if (temp.x > 0 && temp.y > 0 && temp.x < 1000 && temp.y < 1000)
{
corners.Add(temp);
}
}
a.Clear();
}
//剔除重合的点和不合理的点
CullIllegalPoint(ref corners,20);
if (corners.Count != 4)
{
Debug.Log("The object is not quadrilateral " + corners.Count);
}
Vector2 center = Vector2.zero;
for (int i = 0; i < corners.Count; i++)
{
center += corners[i];
}
center *= 0.25f;
SortCorners(ref corners, center);
//计算转换矩阵
Vector2 tl = corners[0];
Vector2 tr = corners[1];
Vector2 br = corners[2];
Vector2 bl = corners[3];
Mat srcRectMat = new Mat(4, 1, CvType.CV_32FC2);
Mat dstRectMat = new Mat(4, 1, CvType.CV_32FC2);
srcRectMat.put(0, 0, tl.x, tl.y, tr.x, tr.y, bl.x, bl.y, br.x, br.y);
dstRectMat.put(0, 0, 0.0, inputMat.rows(), inputMat.cols(), inputMat.rows(), 0.0, 0.0, inputMat.rows(), 0);
Mat perspectiveTransform = Imgproc.getPerspectiveTransform(srcRectMat, dstRectMat);
Mat outputMat0 = inputMat.clone();
//圈出四个顶点
//Point t = new Point(tl.x,tl.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(tr.x, tr.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(bl.x, bl.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//t = new Point(br.x, br.y);
//Imgproc.circle(outputMat0, t, 6, new Scalar(0, 0, 255, 255), 2);
//进行透视转换
Imgproc.warpPerspective(inputMat, outputMat0, perspectiveTransform, new Size(inputMat.rows(), inputMat.cols()));
Texture2D outputTexture = new Texture2D(outputMat0.cols(), outputMat0.rows(), TextureFormat.RGBA32, false);
Utils.matToTexture2D(outputMat0, outputTexture);
gameObject.GetComponent().material.mainTexture = outputTexture;
}
// Update is called once per frame
void Update () {
}
private Vector2 ComputeIntersect(List a, List b)
{
int x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3];
int x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3];
float d = ((float)(x1 - x2) * (y3 - y4)) - (x3 - x4) * (y1 - y2);
Vector2 temp = Vector2.zero;
if (d == 0)
{
temp.x = -1;
temp.y = -1;
}
else
{
temp.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
temp.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;
}
return temp;
}
private void CullIllegalPoint(ref List corners,float minDis)
{
Vector2 a = Vector2.zero;
Vector2 b = Vector2.zero;
List removeList = new List();
for (int i = 0; i < corners.Count; i++)
{
a = corners[i];
for (int j = i + 1; j < corners.Count; j++)
{
b = corners[j];
if (Vector2.Distance(a, b) < minDis)
{
removeList.Add(b);
}
}
}
for (int i = 0; i < removeList.Count; i++)
{
corners.Remove(removeList[i]);
}
}
private void SortCorners( ref List corners, Vector2 center)
{
List top = new List();
List bot = new List();
for (int i = 0; i < corners.Count; i++)
{
if (corners[i].y > center.y)
top.Add(corners[i]);
else
bot.Add(corners[i]);
}
if (top.Count < 2)
{
Vector2 temp = GetMaxFromList(bot);
top.Add(temp);
bot.Remove(temp);
}
if (top.Count > 2)
{
Vector2 temp = GetMinFromList(top);
top.Remove(temp);
bot.Add(temp);
}
Vector2 tl = top[0].x > top[1].x ? top[1]: top[0];
Vector2 tr = top[0].x > top[1].x ? top[0] : top[1];
Vector2 bl = bot[0].x > bot[1].x ? bot[1] : bot[0];
Vector2 br = bot[0].x > bot[1].x ? bot[0] : bot[1];
corners.Clear();
corners.Add(tl);
corners.Add(tr);
corners.Add(br);
corners.Add(bl);
}
private Vector2 GetMaxFromList(List list)
{
Vector2 temp = list[0];
for (int i = 0; i < list.Count; i++)
{
if (list[i].y > temp.y)
{
temp = list[i];
}
}
return temp;
}
private Vector2 GetMinFromList(List list)
{
Vector2 temp = list[0];
for (int i = 0; i < list.Count; i++)
{
if (list[i].y < temp.y)
{
temp = list[i];
}
}
return temp;
}
}