Exemple #1
0
    protected virtual void CheckIfDetectedMarkers()
    {
        if (ids == null)
        {
            return;
        }

        int count = 0;

        if (ids.Length > 0 && OnMarkersDetected != null)
        {
            for (int i = 0; i < ids.Length; i++)
            {
                if (!MarkerManager.IsMarkerRegistered(ids[i]))
                {
                    count++;
                }
            }

            if (count == ids.Length)
            {
                return;
            }

            OnMarkersDetected.Invoke(ids);
        }
    }
Exemple #2
0
    /// <summary>
    /// Looks for markers in the most recent ZED image, and updates all registered MarkerObjects accordingly.
    /// </summary>
    private void ImageUpdated(Camera cam, Mat camMat, Mat iamgeMat)
    {
        Dictionary predict = Aruco.getPredefinedDictionary((int)markerDictionary); //Load the selected pre-defined dictionary.

        //Create empty structures to hold the output of Aruco.detectMarkers.
        List <Mat>         corners        = new List <Mat>();
        Mat                ids            = new Mat();
        DetectorParameters detectparams   = DetectorParameters.create();
        List <Mat>         rejectedpoints = new List <Mat>(); //There is no overload for detectMarkers that will take camMat without this also.

        //Call OpenCV to tell us which markers were detected, and give their 2D positions.
        Aruco.detectMarkers(iamgeMat, predict, corners, ids, detectparams, rejectedpoints, camMat);

        //Make matrices to hold the output rotations and translations of each detected marker.
        Mat rotvectors   = new Mat();
        Mat transvectors = new Mat();

        //Convert the 2D corner positions into a 3D pose for each detected marker.
        Aruco.estimatePoseSingleMarkers(corners, markerWidthMeters, camMat, new Mat(), rotvectors, transvectors);

        //Now we have ids, rotvectors and transvectors, which are all vertical arrays holding info about each detection:
        // - ids: An Nx1 array (N = number of markers detected) where each slot is the ID of a detected marker in the dictionary.
        // - rotvectors: An Nx3 array where each row is for an individual detection: The first row is the rotation of the marker
        //    listed in the first row of ids, etc. The columns are the X, Y and Z angles of that marker's rotation, BUT they are not
        //    directly usable in Unity because they're calculated very differetly. We'll deal with that soon.
        // - transvectors: An Nx1 array like rotvectors with each row corresponding to a detected marker, with a double[3] array with the X, Y and Z positions.
        //    positions. These three values are usable in Unity - they're just relative to the camera, not the world, which is easy to fix.

        //Convert matrix of IDs into a List, to simply things for those not familiar with using Matrices.
        List <int> detectedIDs = new List <int>();

        for (int i = 0; i < ids.height(); i++)
        {
            int id = (int)ids.get(i, 0)[0];
            if (!detectedIDs.Contains(id))
            {
                detectedIDs.Add(id);
            }
        }

        //We'll go through every ID that's been registered into registered Markers, and see if we found any markers in the scene with that ID.
        Dictionary <int, List <sl.Pose> > detectedWorldPoses = new Dictionary <int, List <sl.Pose> >(); //Key is marker ID, value is world space poses.

        //foreach (int id in registeredMarkers.Keys)
        for (int index = 0; index < transvectors.rows(); index++)
        {
            int id = (int)ids.get(index, 0)[0];
            if (!registeredMarkers.ContainsKey(id) || registeredMarkers[id].Count == 0)
            {
                continue;                 //Don't waste time if the list is empty. Can happen if markers are added, then removed.
            }
            if (detectedIDs.Contains(id)) //At least one MarkerObject needs to be updated. Convert pose to world space and call MarkerDetected() on it.
            {
                //Translation is just pose relative to camera. But we need to flip Y because of OpenCV's different coordinate system.
                Vector3 localpos;
                localpos.x = (float)transvectors.get(index, 0)[0];
                localpos.y = -(float)transvectors.get(index, 0)[1];
                localpos.z = (float)transvectors.get(index, 0)[2];

                Vector3 worldpos = cam.transform.TransformPoint(localpos); //Convert from local to world space.

                //Because of different coordinate frame, we need to flip the Y direction, which is pointing down instead of up.
                //We need to do this before we calculate the 3x3 rotation matrix soon, as that makes it muuuch harder to work with.
                double[] flip = rotvectors.get(index, 0);
                flip[1] = -flip[1];
                rotvectors.put(index, 0, flip);

                //Convert this rotation vector to a 3x3 matrix, which will hold values we can use in Unity.
                Mat rotmatrix = new Mat();
                Calib3d.Rodrigues(rotvectors.row(index), rotmatrix);

                //This new 3x3 matrix holds a vector pointing right in the first column, a vector pointing up in the second,
                //and a vector pointing forward in the third column. Rows 0, 1 and 2 are the X, Y and Z values of each vector.
                //We'll grab the forward and up vectors, which we can put into Quaternion.LookRotation() to get a representative Quaternion.
                Vector3 forward;
                forward.x = (float)rotmatrix.get(2, 0)[0];
                forward.y = (float)rotmatrix.get(2, 1)[0];
                forward.z = (float)rotmatrix.get(2, 2)[0];

                Vector3 up;
                up.x = (float)rotmatrix.get(1, 0)[0];
                up.y = (float)rotmatrix.get(1, 1)[0];
                up.z = (float)rotmatrix.get(1, 2)[0];

                Quaternion rot = Quaternion.LookRotation(forward, up);

                //Compensate for flip on Z axis.
                rot *= Quaternion.Euler(0, 0, 180);

                //Convert from local space to world space by multiplying the camera's world rotation with it.
                Quaternion worldrot = cam.transform.rotation * rot;

                if (!detectedWorldPoses.ContainsKey(id))
                {
                    detectedWorldPoses.Add(id, new List <sl.Pose>());
                }
                detectedWorldPoses[id].Add(new sl.Pose()
                {
                    translation = worldpos, rotation = worldrot
                });

                foreach (MarkerObject marker in registeredMarkers[id])
                {
                    marker.MarkerDetectedSingle(worldpos, worldrot);
                }
            }
        }

        //Call the event that gives all marker world poses, if any listeners.
        if (OnMarkersDetected != null)
        {
            OnMarkersDetected.Invoke(detectedWorldPoses);
        }

        //foreach (int detectedid in detectedWorldPoses.Keys)
        foreach (int key in registeredMarkers.Keys)
        {
            if (detectedWorldPoses.ContainsKey(key))
            {
                foreach (MarkerObject marker in registeredMarkers[key])
                {
                    marker.MarkerDetectedAll(detectedWorldPoses[key]);
                }
            }
            else
            {
                foreach (MarkerObject marker in registeredMarkers[key])
                {
                    marker.MarkerNotDetected();
                }
            }
        }
    }