如何使用CAShapeLayer和UIBezeirPath在实时摄像机源上绘制检测到的矩形路径

时间:2016-06-10 12:16:13

标签: ios swift uibezierpath cashapelayer cidetector

我正在开发一个应用程序来检测实时摄像机输入中的矩形并突出显示检测到的矩形。我使用AVFoundation做了相机,并使用下面的方法来检测并突出显示检测到的矩形。

var detector: CIDetector?;

override func viewDidLoad() {
    super.viewDidLoad();

    detector = self.prepareRectangleDetector();
}

func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) { // re check this method

    // Need to shimmy this through type-hell
    let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)

    // Force the type change - pass through opaque buffer
    let opaqueBuffer = Unmanaged<CVImageBuffer>.passUnretained(imageBuffer!).toOpaque()
    let pixelBuffer = Unmanaged<CVPixelBuffer>.fromOpaque(opaqueBuffer).takeUnretainedValue()

    let sourceImage = CIImage(CVPixelBuffer: pixelBuffer, options: nil)

    // Do some detection on the image
    self.performRectangleDetection(sourceImage);

    var outputImage = sourceImage

    // Do some clipping
    var drawFrame = outputImage.extent
    let imageAR = drawFrame.width / drawFrame.height
    let viewAR = videoDisplayViewBounds.width / videoDisplayViewBounds.height

    if imageAR > viewAR {
        drawFrame.origin.x += (drawFrame.width - drawFrame.height * viewAR) / 2.0
        drawFrame.size.width = drawFrame.height / viewAR
    } else {
        drawFrame.origin.y += (drawFrame.height - drawFrame.width / viewAR) / 2.0
        drawFrame.size.height = drawFrame.width / viewAR
    }

    //videoDisplayView is a GLKView which is used to display camera feed
    videoDisplayView.bindDrawable()
    if videoDisplayView.context != EAGLContext.currentContext() {
        EAGLContext.setCurrentContext(videoDisplayView.context)
    }

    // clear eagl view to grey
    glClearColor(0.5, 0.5, 0.5, 1.0);
    glClear(0x00004000)

    // set the blend mode to "source over" so that CI will use that
    glEnable(0x0BE2);
    glBlendFunc(1, 0x0303);

    renderContext.drawImage(outputImage, inRect: videoDisplayViewBounds, fromRect: drawFrame);

    videoDisplayView.display();

}

func prepareRectangleDetector() -> CIDetector {

    let options: [String: AnyObject] = [CIDetectorAccuracy: CIDetectorAccuracyHigh];
    return CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: options);
}

func performRectangleDetection(image: CIImage){

    let resultImage: CIImage? = nil;

    if let detector = detector {

        // Get the detections
        let features = detector.featuresInImage(image, options: [CIDetectorAspectRatio:NSNumber(float:1.43)]);


        if features.count != 0{ // feature found

            for feature in features as! [CIRectangleFeature] {

                self.previewImageView.layer.sublayers = nil;

                let line: CAShapeLayer = CAShapeLayer();
                line.frame = self.videoDisplayView.bounds;
                let linePath: UIBezierPath = UIBezierPath();

                linePath.moveToPoint(feature.topLeft);
                linePath.addLineToPoint(feature.topRight);
                linePath.addLineToPoint(feature.bottomRight);
                linePath.addLineToPoint(feature.bottomLeft);
                linePath.addLineToPoint(feature.topLeft);
                linePath.closePath();

                line.lineWidth = 5.0;
                line.path = linePath.CGPath;
                line.fillColor = UIColor.clearColor().CGColor;
                line.strokeColor = UIColor(netHex: 0x3399CC, alpha: 1.0).CGColor;

                // videoDisplayParentView is the parent of videoDisplayView and they both have same bounds
                self.videoDisplayParentView.layer.addSublayer(line);
             }    
         }                    
     }
}

我使用CAShapeLayerUIBezierPath绘制矩形。这非常慢。几分钟后路径就可见了。

有人可以帮我弄清楚它为什么慢,或者让我知道我在这里做错了什么。任何帮助都将受到高度赞赏。

或者,如果有一些方法比这更容易,我也想知道。

1 个答案:

答案 0 :(得分:0)

如果您开始向GLKView添加子图层,那将会很慢。这里的GLKView每秒刷新多次(就像在captureOutput:didOutputSampleBuffer:..方法中一样),每次创建和添加子层的过程都无法跟上。

更好的方法是使用CoreImage绘制路径并在resultImage上合成它。