#基于VR环境下的手指识别键盘输入#利用opencvforUnity实现透视变换

本文实现的效果是通过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<int> a, List<int> b)  
private void CullIllegalPoint(ref List<Vector2> corners,float minDis)  

完整代码:

using UnityEngine;  
using System.Collections.Generic;  
using OpenCVForUnity;  
  
public class PerspectiveTransform: MonoBehaviour {  
    private List<Vector2> corners = new List<Vector2>();  
    // 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<int> a = new List<int>();  
        List<int> b = new List<int>();  
        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<Renderer>().material.mainTexture = outputTexture;  
    }  
  
    // Update is called once per frame  
    void Update () {  
      
    }  
    private Vector2 ComputeIntersect(List<int> a, List<int> 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<Vector2> corners,float minDis)  
    {  
        Vector2 a = Vector2.zero;  
        Vector2 b = Vector2.zero;  
        List<Vector2> removeList = new List<Vector2>();  
        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<Vector2> corners, Vector2 center)  
    {  
        List<Vector2> top = new List<Vector2>();  
        List<Vector2> bot = new List<Vector2>();  
        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<Vector2> 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<Vector2> list)  
    {  
        Vector2 temp = list[0];  
        for (int i = 0; i < list.Count; i++)  
        {  
            if (list[i].y < temp.y)  
            {  
                temp = list[i];  
            }  
        }  
        return temp;  
    }  
}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值