OpenCVForUnity 透视校正,透视变换

本文实现的效果是通过OpenCVForUnity实现透视图片校正,主要参考文章是点击打开链接

效果如下:

OpenCVForUnity 透视校正,透视变换_第1张图片

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)

OpenCVForUnity 透视校正,透视变换_第2张图片

边缘处理

public static void Canny (Mat image, Mat edges, double threshold1, double threshold2)
OpenCVForUnity 透视校正,透视变换_第3张图片


霍夫曼线

public static void HoughLinesP (Mat image, Mat lines, double rho, double theta, int threshold, double minLineLength, double maxLineGap)

第一个参数为输入图像,应该为灰度图, 
第二个参数为输出的检测到的直线的容器 
第三个参数为以参数极径单位的分辨率 
第四个是以弧度为单位的分辨率 
第五个为一条直线所需最少的的曲线交点

OpenCVForUnity 透视校正,透视变换_第4张图片

计算霍夫曼线交叉点 并剔除不合理的点(这个方法根据实际计算出来的点位置自行调整)

 private Vector2 ComputeIntersect(List a, List b)

 private void CullIllegalPoint(ref List corners,float minDis)


OpenCVForUnity 透视校正,透视变换_第5张图片

完整代码:

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;
    }
}




你可能感兴趣的:(OpenCVForUnity 透视校正,透视变换)