2017-05-07 45 views
7

스택에 대한 첫 번째 게시물이므로 내 불만을 미리 드러내며 미안합니다. 어쨌든 내 질문을 개선 할 수 있는지 알려 주시기 바랍니다.Unity를위한 OpenCV : 4 포인트 보정/재 투사

내가 유니티 fo를 OpenCV의를 사용하여 레이저 포인터로 내 Unity3d 프리젠 테이션을 조작하려고 :

내가 (장기적으로)를 달성하려면 무엇 ►.

나는 한 장의 사진이 천 마디 말보다 더 가치가있다 생각, 그래서 이것은 가장 말해야한다 :

enter image description here

는 문제가 무엇 ► :

나는 간단한을 만들려고 카메라 뷰 (사다리꼴의 일종)에서 평면 공간으로의 4 포인트 보정 (투영).

나는 매우 기본적인 것이 될 것이라고 생각했지만 OpenCV에 대한 경험이 없기 때문에 작동하지 않을 수 있습니다.

► 샘플 :

나는 어떤 레이저 탐지 및 다른 모든 것들없이, 훨씬 덜 복잡 예를했다. 비행기 공간으로 재 투척하려고하는 4 점 사다리꼴. 전체 샘플 프로젝트에

링크 : https://1drv.ms/u/s!AiDsGecSyzmuujXGQUapcYrIvP7b

내 예제의 핵심 스크립트

using OpenCVForUnity; 
using System.Collections; 
using System.Collections.Generic; 
using UnityEngine; 
using UnityEngine.UI; 
using System; 

public class TestCalib : MonoBehaviour 
{ 
    public RawImage displayDummy; 
    public RectTransform[] handlers; 
    public RectTransform dummyCross; 
    public RectTransform dummyResult; 

    public Vector2 webcamSize = new Vector2(640, 480); 
    public Vector2 objectSize = new Vector2(1024, 768); 

    private Texture2D texture; 

    Mat cameraMatrix; 
    MatOfDouble distCoeffs; 

    MatOfPoint3f objectPoints; 
    MatOfPoint2f imagePoints; 

    Mat rvec; 
    Mat tvec; 
    Mat rotationMatrix; 
    Mat imgMat; 


    void Start() 
    { 
     texture = new Texture2D((int)webcamSize.x, (int)webcamSize.y, TextureFormat.RGB24, false); 
     if (displayDummy) displayDummy.texture = texture; 
     imgMat = new Mat(texture.height, texture.width, CvType.CV_8UC3); 
    } 


    void Update() 
    { 
     imgMat = new Mat(texture.height, texture.width, CvType.CV_8UC3); 
     Test(); 
     DrawImagePoints(); 
     Utils.matToTexture2D(imgMat, texture); 
    } 

    void DrawImagePoints() 
    { 
     Point[] pointsArray = imagePoints.toArray(); 
     for (int i = 0; i < pointsArray.Length; i++) 
     { 
      Point p0 = pointsArray[i]; 
      int j = (i < pointsArray.Length - 1) ? i + 1 : 0; 
      Point p1 = pointsArray[j]; 

      Imgproc.circle(imgMat, p0, 5, new Scalar(0, 255, 0, 150), 1); 
      Imgproc.line(imgMat, p0, p1, new Scalar(255, 255, 0, 150), 1); 
     } 
    } 


    private void DrawResults(MatOfPoint2f resultPoints) 
    { 
     Point[] pointsArray = resultPoints.toArray(); 
     for (int i = 0; i < pointsArray.Length; i++) 
     { 
      Point p = pointsArray[i]; 
      Imgproc.circle(imgMat, p, 5, new Scalar(255, 155, 0, 150), 1); 
     } 
    } 

    public void Test() 
    { 
     float w2 = objectSize.x/2F; 
     float h2 = objectSize.y/2F; 

     /* 
     objectPoints = new MatOfPoint3f(
      new Point3(-w2, -h2, 0), 
      new Point3(w2, -h2, 0), 
      new Point3(-w2, h2, 0), 
      new Point3(w2, h2, 0) 
     ); 
     */ 

     objectPoints = new MatOfPoint3f(
      new Point3(0, 0, 0), 
      new Point3(objectSize.x, 0, 0), 
      new Point3(objectSize.x, objectSize.y, 0), 
      new Point3(0, objectSize.y, 0) 
     ); 

     imagePoints = GetImagePointsFromHandlers(); 

     rvec = new Mat(1, 3, CvType.CV_64FC1); 
     tvec = new Mat(1, 3, CvType.CV_64FC1); 
     rotationMatrix = new Mat(3, 3, CvType.CV_64FC1); 


     double fx = webcamSize.x/objectSize.x; 
     double fy = webcamSize.y/objectSize.y; 
     double cx = 0; // webcamSize.x/2.0f; 
     double cy = 0; // webcamSize.y/2.0f; 
     cameraMatrix = new Mat(3, 3, CvType.CV_64FC1); 
     cameraMatrix.put(0, 0, fx); 
     cameraMatrix.put(0, 1, 0); 
     cameraMatrix.put(0, 2, cx); 
     cameraMatrix.put(1, 0, 0); 
     cameraMatrix.put(1, 1, fy); 
     cameraMatrix.put(1, 2, cy); 
     cameraMatrix.put(2, 0, 0); 
     cameraMatrix.put(2, 1, 0); 
     cameraMatrix.put(2, 2, 1.0f); 

     distCoeffs = new MatOfDouble(0, 0, 0, 0); 
     Calib3d.solvePnP(objectPoints, imagePoints, cameraMatrix, distCoeffs, rvec, tvec); 

     Mat uv = new Mat(3, 1, CvType.CV_64FC1); 
     uv.put(0, 0, dummyCross.anchoredPosition.x); 
     uv.put(1, 0, dummyCross.anchoredPosition.y); 
     uv.put(2, 0, 0); 

     Calib3d.Rodrigues(rvec, rotationMatrix); 
     Mat P = rotationMatrix.inv() * (cameraMatrix.inv() * uv - tvec); 

     Vector2 v = new Vector2((float)P.get(0, 0)[0], (float)P.get(1, 0)[0]); 
     dummyResult.anchoredPosition = v; 
    } 

    private MatOfPoint2f GetImagePointsFromHandlers() 
    { 
     MatOfPoint2f m = new MatOfPoint2f(); 
     List<Point> points = new List<Point>(); 
     foreach (RectTransform handler in handlers) 
     { 
      Point p = new Point(handler.anchoredPosition.x, handler.anchoredPosition.y); 
      points.Add(p); 
     } 

     m.fromList(points); 
     return m; 
    } 
} 

감사합니다 사전에 어떤 도움.

답변

1

이 질문은 opencv에만 국한된 것이 아니라 많이 수학에 기반을두고 있으며 컴퓨터 그래픽의 영역에서 자주 볼 수 있습니다. 당신이 찾고있는 것은 Projective Transformation입니다.

투영 변환은 일련의 좌표를 취해이를 무언가에 투영합니다. 귀하의 경우에는 카메라 뷰의 2D 점을 평면상의 2D 점으로 투영하려고합니다.

그래서 우리는 2D 공간을위한 투영 변환을 원합니다. 투영 변환을 수행하려면 적용 할 변환을위한 투영 행렬을 찾아야합니다. 이 경우 평평한면과 관련하여 카메라의 투영 변형을 표현하는 행렬이 필요합니다.

투영을 사용하려면 먼저 포인트를 homogeneous coordinates으로 변환해야합니다. 이렇게하려면 값 1의 벡터에 새 구성 요소를 추가하면됩니다. 따라서 (x,y)(x,y,1)이됩니다. 그리고 우리는 우리가 사용할 수있는 5 가지 요소를 모두 사용하여이를 수행 할 것입니다.

이제 실제 수학으로 시작합니다. 첫 번째 정의 : 카메라의 시점과 각 좌표는 camera space이고, 평면에 대한 좌표는 flat space입니다. c₁ ~ c₄을 균질 벡터로 카메라 공간과 관련하여 평면의 코너 점이라고합시다. p을 우리가 카메라 공간에서 발견 한 포인트라고하고, p'을 평면 공간에서 찾고자하는 점을 모두 균질 벡터로 다시 보자.

수학적으로 말하자면, p을 부여하여 p'을 계산할 수있는 행렬 C을 찾고 있습니다.

p' = C * p 

이제 우리는 분명히 C을 찾아야합니다. 2 차원의 투영 행렬을 찾으려면, 우리는 네 개의 점 (아주 편리 ..) 나는 c₁(0,0)에 갈 것이라고 생각합니다 필요 c₂(1,1)(1,0)c₄(0,1), c₃로 이동합니다. 예를 들어, 다음을 사용하여 두 행렬 방정식을 풀어야합니다. 가우시안 행 제거 또는 LR 분해 알고리즘 OpenCV에는 이러한 작업을 수행하는 기능이 있어야하지만 매트릭스 조절과 사용 가능한 솔루션에 미치는 영향을 알고 있어야합니다.

이제 행렬로 돌아갑니다. 두 개의 기본 변경 행렬을 호출 할 때이를 계산해야합니다. 좌표의 좌표계를 변경하는 데 사용됩니다. 첫 번째 행렬은 좌표를 3 차원 기본 벡터로 변환하고 두 번째 행렬은 2D 평면을 3 차원 기본 벡터로 변환합니다. 당신은 다음 식에 λ, μr을 계산해야합니다 하나의 좌표를 들어

:

⌈ c₁.x c₂.x c₃.x ⌉  ⌈ λ ⌉ ⌈ c₄.x ⌉ 
    c₁.y c₂.y c₃.y * μ = c₄.y 
⌊ 1  1  1 ⌋  ⌊ r ⌋ ⌊ 1 ⌋ 

이 첫 번째 행렬에 당신을 이끌 것입니다, A

⌈ λ*c₁.x μ*c₂.x r*c₃.x ⌉ 
A = λ*c₁.y μ*c₂.y r*c₃.y 
    ⌊ λ   μ  r ⌋ 

유언장 이제 c₁c₄에 기초 좌표 (1,0,0), (0,1,0), (0,0,1) 및로 매핑하십시오.. 우리 비행기에서도 똑같은 일을합니다. 먼저

⌈ 0 0 1 ⌉  ⌈ λ ⌉ ⌈ 1 ⌉ 
    0 1 0 * μ = 1 
⌊ 1 1 1 ⌋  ⌊ r ⌋ ⌊ 1 ⌋ 

를 해결하고 B

⌈ 0 0 r ⌉ 
B = 0 μ 0 
    ⌊ λ μ r ⌋ 

AB 이제 각각의 공간으로 그 세 차원 기저 벡터에서 매핑됩니다 얻는다. 하지만 그건 우리가 원하는 것이 아닙니다. 우리는 camera space -> basis -> flat space을 원하기 때문에 행렬 B 만 올바른 방향으로 조작합니다. 그러나 그것은 A을 뒤집어서 쉽게 고칠 수 있습니다. 그러면 매트릭스 C = B * A⁻¹ (BA⁻¹의 순서를 지켜 볼 수 있습니다. 이로 인해 p'p 중에서 계산하는 수식이 남습니다.

p' = C * p 
p' = B * A⁻¹ * p 

왼쪽에서 오른쪽으로 다음과 같이 읽으십시오 : 카메라 공간에서 기본 공간으로 p 변환하고 평면 공간으로 변환하십시오.

정확하게 기억하면 p'에는 여전히 세 가지 구성 요소가 있으므로 우리가 사용하기 전에 p'을 먼저 무해 화해야합니다. 이

x' = p'.x/p'.z 
y' = p'.y/p'.z 

를 얻을 것이며, 비올라, 우리는 성공적으로 종이의 평평한 부분에 카메라 뷰에서 레이저 포인트를 전환했다. 지나치게 복잡하지 않으므로 ...

+0

고마워요. 정확히 답이 아니지만 무엇을 찾아야하는지 이해하는 데 도움이되었습니다. :) – IronWolf