示例#1
0
        public void DidOutputMetadataObjects(AVCaptureMetadataOutput captureOutput,
                                             AVMetadataObject[] metadataObjects, AVCaptureConnection connection)
        {
            var lostFaces = _faceLayers.Keys.ToList();

            foreach (var metadata in metadataObjects.OfType <AVMetadataFaceObject>())
            {
                var transformed = VideoPreviewLayer.GetTransformedMetadataObject(metadata);
                var face        = transformed as AVMetadataFaceObject;
                var bounds      = transformed.Bounds;

                if (lostFaces.Contains(face.FaceID))
                {
                    lostFaces.Remove(face.FaceID);
                }

                CALayer faceLayer;
                if (!_faceLayers.TryGetValue(face.FaceID, out faceLayer))
                {
                    faceLayer = CreateFaceLayer();
                    _overlayLayer.AddSublayer(faceLayer);
                    _faceLayers.Add(face.FaceID, faceLayer);
                }

                faceLayer.Transform = CATransform3D.Identity;
                faceLayer.Frame     = bounds;

                if (face.HasRollAngle)
                {
                    var transform = RollTransform(face.RollAngle);
                    faceLayer.Transform = faceLayer.Transform.Concat(transform);
                }

                if (face.HasYawAngle)
                {
                    var transform = YawTransform(face.YawAngle);
                    faceLayer.Transform = faceLayer.Transform.Concat(transform);
                }
            }

            RemoveLostFaces(lostFaces);
        }
示例#2
0
        // Updates the region of interest with a proposed region of interest ensuring
        // the new region of interest is within the bounds of the video preview. When
        // a new region of interest is set, the region of interest is redrawn.
        public void SetRegionOfInterestWithProposedRegionOfInterest(CGRect proposedRegionOfInterest)
        {
            // We standardize to ensure we have positive widths and heights with an origin at the top left.
            var videoPreviewRect = VideoPreviewLayer.MapToLayerCoordinates(new CGRect(0, 0, 1, 1)).Standardize();

            // Intersect the video preview view with the view's frame to only get
            // the visible portions of the video preview view.
            var visibleVideoPreviewRect = CGRect.Intersect(videoPreviewRect, Frame);
            var oldRegion = RegionOfInterest;
            var newRegion = proposedRegionOfInterest.Standardize();

            // Move the region of interest in bounds.
            if (currentControlCorner == ControlCorner.None)
            {
                nfloat xOffset = 0;
                nfloat yOffset = 0;

                if (!visibleVideoPreviewRect.Contains(newRegion.CornerTopLeft()))
                {
                    xOffset = NMath.Max(visibleVideoPreviewRect.GetMinX() - newRegion.GetMinX(), 0);
                    yOffset = NMath.Max(visibleVideoPreviewRect.GetMinY() - newRegion.GetMinY(), 0);
                }

                if (!visibleVideoPreviewRect.Contains(visibleVideoPreviewRect.CornerBottomRight()))
                {
                    xOffset = NMath.Min(visibleVideoPreviewRect.GetMaxX() - newRegion.GetMaxX(), xOffset);
                    yOffset = NMath.Min(visibleVideoPreviewRect.GetMaxY() - newRegion.GetMaxY(), yOffset);
                }

                newRegion.Offset(xOffset, yOffset);
            }

            // Clamp the size when the region of interest is being resized.
            visibleVideoPreviewRect.Intersect(newRegion);
            newRegion = visibleVideoPreviewRect;

            // Fix a minimum width of the region of interest.
            if (proposedRegionOfInterest.Size.Width < MinimumRegionOfInterestSize)
            {
                switch (currentControlCorner)
                {
                case ControlCorner.TopLeft:
                case ControlCorner.BottomLeft:
                    var x = oldRegion.Location.X + oldRegion.Size.Width - MinimumRegionOfInterestSize;
                    newRegion = newRegion.WithX(x).WithWidth(MinimumRegionOfInterestSize);
                    break;

                case ControlCorner.TopRight:
                    newRegion = newRegion.WithX(oldRegion.Location.X)
                                .WithWidth(MinimumRegionOfInterestSize);
                    break;

                default:
                    newRegion = newRegion.WithLocation(oldRegion.Location)
                                .WithWidth(MinimumRegionOfInterestSize);
                    break;
                }
            }

            // Fix a minimum height of the region of interest.
            if (proposedRegionOfInterest.Height < MinimumRegionOfInterestSize)
            {
                switch (currentControlCorner)
                {
                case ControlCorner.TopLeft:
                case ControlCorner.TopRight:
                    newRegion = newRegion.WithY(oldRegion.Y + oldRegion.Height - MinimumRegionOfInterestSize)
                                .WithHeight(MinimumRegionOfInterestSize);
                    break;

                case ControlCorner.BottomLeft:
                    newRegion = newRegion.WithY(oldRegion.Y)
                                .WithHeight(MinimumRegionOfInterestSize);
                    break;

                default:
                    newRegion = newRegion.WithLocation(oldRegion.Location)
                                .WithHeight(MinimumRegionOfInterestSize);
                    break;
                }
            }

            RegionOfInterest = newRegion;
            SetNeedsLayout();
        }
示例#3
0
        void ResizeRegionOfInterestWithGestureRecognizer(UIPanGestureRecognizer pan)
        {
            var touchLocation       = pan.LocationInView(pan.View);
            var oldRegionOfInterest = RegionOfInterest;

            switch (pan.State)
            {
            case UIGestureRecognizerState.Began:
                // When the gesture begins, save the corner that is closes to
                // the resize region of interest gesture recognizer's touch location.
                currentControlCorner = CornerOfRect(oldRegionOfInterest, touchLocation);
                break;


            case UIGestureRecognizerState.Changed:
                var newRegionOfInterest = oldRegionOfInterest;

                switch (currentControlCorner)
                {
                case ControlCorner.None:
                    // Update the new region of interest with the gesture recognizer's translation.
                    var translation = pan.TranslationInView(pan.View);
                    // Move the region of interest with the gesture recognizer's translation.
                    if (RegionOfInterest.Contains(touchLocation))
                    {
                        newRegionOfInterest.X += translation.X;
                        newRegionOfInterest.Y += translation.Y;
                    }

                    // If the touch location goes outside the preview layer,
                    // we will only translate the region of interest in the
                    // plane that is not out of bounds.
                    var normalizedRect = new CGRect(0, 0, 1, 1);
                    if (!normalizedRect.Contains(VideoPreviewLayer.PointForCaptureDevicePointOfInterest(touchLocation)))
                    {
                        if (touchLocation.X < RegionOfInterest.GetMinX() || touchLocation.X > RegionOfInterest.GetMaxX())
                        {
                            newRegionOfInterest.Y += translation.Y;
                        }
                        else if (touchLocation.Y < RegionOfInterest.GetMinY() || touchLocation.Y > RegionOfInterest.GetMaxY())
                        {
                            newRegionOfInterest.X += translation.X;
                        }
                    }

                    // Set the translation to be zero so that the new gesture
                    // recognizer's translation is in respect to the region of
                    // interest's new position.
                    pan.SetTranslation(CGPoint.Empty, pan.View);
                    break;

                case ControlCorner.TopLeft:
                    newRegionOfInterest = new CGRect(touchLocation.X, touchLocation.Y,
                                                     oldRegionOfInterest.Width + oldRegionOfInterest.X - touchLocation.X,
                                                     oldRegionOfInterest.Height + oldRegionOfInterest.Y - touchLocation.Y);
                    break;

                case ControlCorner.TopRight:
                    newRegionOfInterest = new CGRect(newRegionOfInterest.X,
                                                     touchLocation.Y,
                                                     touchLocation.X - newRegionOfInterest.X,
                                                     oldRegionOfInterest.Height + newRegionOfInterest.Y - touchLocation.Y);
                    break;


                case ControlCorner.BottomLeft:
                    newRegionOfInterest = new CGRect(touchLocation.X, oldRegionOfInterest.Y,
                                                     oldRegionOfInterest.Width + oldRegionOfInterest.X - touchLocation.X,
                                                     touchLocation.Y - oldRegionOfInterest.Y);
                    break;

                case ControlCorner.BottomRight:
                    newRegionOfInterest = new CGRect(oldRegionOfInterest.X, oldRegionOfInterest.Y,
                                                     touchLocation.X - oldRegionOfInterest.X,
                                                     touchLocation.Y - oldRegionOfInterest.Y);
                    break;
                }

                // Update the region of intresest with a valid CGRect.
                SetRegionOfInterestWithProposedRegionOfInterest(newRegionOfInterest);
                break;

            case UIGestureRecognizerState.Ended:
                RegionOfInterestDidChange?.Invoke(this, EventArgs.Empty);

                // Reset the current corner reference to none now that the resize.
                // gesture recognizer has ended.
                currentControlCorner = ControlCorner.None;
                break;

            default:
                return;
            }
        }