【问题标题】:Combining Images in CameraView with Overlay. (Swift 3)?将 CameraView 中的图像与 Overlay 相结合。 (斯威夫特 3)?
【发布时间】:2017-07-23 21:18:20
【问题描述】:

我刚刚解决了这个问题。感谢一些出色的帮助让我走上正轨。 这是我现在的代码。

基本上,我现在可以从绘制的叠加层和 cameraPreview 中制作图像。但还不能将它们结合起来。我能找到的有用代码似乎很少。

所以重要的部分是顶部的扩展块,以及添加到 func saveToCamera() 靠近代码底部。 简而言之,我想我现在有了我需要的两张图片。 myImage 的快照出现在白色背景上 - 所以不确定这是否自然 - 或不是。这就是它在模拟器上的显示方式。所以这可能只是自然的。

图片 1. 屏幕截图。

Image 2. 根据说明保存的 myImage 图像。

import UIKit
import AVFoundation
import Foundation

// extension must be outside class
extension UIImage {
convenience init(view: UIView) {
    UIGraphicsBeginImageContext(view.frame.size)
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    self.init(cgImage: (image?.cgImage)!)
}
}




class ViewController: UIViewController {

@IBOutlet weak var navigationBar: UINavigationBar!
@IBOutlet weak var imgOverlay: UIImageView!
@IBOutlet weak var btnCapture: UIButton!

@IBOutlet weak var shapeLayer: UIView!


let captureSession = AVCaptureSession()
let stillImageOutput = AVCaptureStillImageOutput()
var previewLayer : AVCaptureVideoPreviewLayer?

//var shapeLayer : CALayer?

// If we find a device we'll store it here for later use
var captureDevice : AVCaptureDevice?




override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    //=======================

    let midX = self.view.bounds.midX
    let midY = self.view.bounds.midY

    for index in 1...10 {
    let circlePath = UIBezierPath(arcCenter: CGPoint(x: midX,y: midY), radius: CGFloat((index * 10)), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    let shapeLayerPath = CAShapeLayer()

    shapeLayerPath.path = circlePath.cgPath
    //change the fill color
    shapeLayerPath.fillColor = UIColor.clear.cgColor
    //you can change the stroke color
    shapeLayerPath.strokeColor = UIColor.blue.cgColor
    //you can change the line width
    shapeLayerPath.lineWidth = 0.5

        // add the blue-circle layer to the shapeLayer ImageView
        shapeLayer.layer.addSublayer(shapeLayerPath)

    }
    print("Shape layer drawn")
    //=====================



    captureSession.sessionPreset = AVCaptureSessionPresetHigh

    if let devices = AVCaptureDevice.devices() as? [AVCaptureDevice] {
        // Loop through all the capture devices on this phone
        for device in devices {
            // Make sure this particular device supports video
            if (device.hasMediaType(AVMediaTypeVideo)) {
                // Finally check the position and confirm we've got the back camera
                if(device.position == AVCaptureDevicePosition.back) {
                    captureDevice = device
                    if captureDevice != nil {
                        print("Capture device found")
                        beginSession()
                    }
                }
            }
        }
    }
}

@IBAction func actionCameraCapture(_ sender: AnyObject) {

    print("Camera button pressed")
    saveToCamera()
}

func beginSession() {

    do {
        try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
        stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]

        if captureSession.canAddOutput(stillImageOutput) {
            captureSession.addOutput(stillImageOutput)
        }

    }
    catch {
        print("error: \(error.localizedDescription)")
    }

    guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {
        print("no preview layer")
        return
    }
    // this is what displays the camera view. But - it's on TOP of the drawn view, and under the overview. ??
    self.view.layer.addSublayer(previewLayer)
    previewLayer.frame = self.view.layer.frame



    captureSession.startRunning()
    print("Capture session running")


    self.view.addSubview(navigationBar)
    //self.view.addSubview(imgOverlay)
    self.view.addSubview(btnCapture)

    // shapeLayer ImageView is already a subview created in IB
    // but this will bring it to the front
    self.view.addSubview(shapeLayer)

        }

func saveToCamera() {

    if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
          stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in

            if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
                if let cameraImage = UIImage(data: imageData) {
                    // cameraImage is the camera preview image.

                    // I need to combine/merge it with the myImage that is actually the blue circles.

                    // This converts the UIView of the bllue circles to an image. Uses 'extension' at top of code.
                    let myImage = UIImage(view: self.shapeLayer)
                    print("converting myImage to an image")


                   UIImageWriteToSavedPhotosAlbum(cameraImage, nil, nil, nil)

                }
            }
        })
    }
}






override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
}

【问题讨论】:

    标签: ios swift3 camera-overlay


    【解决方案1】:

    试一试...而不是合并您的叠加视图,它绘制圆圈并合并输出:

    import UIKit
    import AVFoundation
    import Foundation
    
    class CameraWithTargetViewController: UIViewController {
    
        @IBOutlet weak var navigationBar: UINavigationBar!
        @IBOutlet weak var imgOverlay: UIImageView!
        @IBOutlet weak var btnCapture: UIButton!
    
        @IBOutlet weak var shapeLayer: UIView!
    
        let captureSession = AVCaptureSession()
        let stillImageOutput = AVCaptureStillImageOutput()
        var previewLayer : AVCaptureVideoPreviewLayer?
    
        //var shapeLayer : CALayer?
    
        // If we find a device we'll store it here for later use
        var captureDevice : AVCaptureDevice?
    
        override func viewDidLoad() {
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
            //=======================
    
            captureSession.sessionPreset = AVCaptureSessionPresetHigh
    
            if let devices = AVCaptureDevice.devices() as? [AVCaptureDevice] {
                // Loop through all the capture devices on this phone
                for device in devices {
                    // Make sure this particular device supports video
                    if (device.hasMediaType(AVMediaTypeVideo)) {
                        // Finally check the position and confirm we've got the back camera
                        if(device.position == AVCaptureDevicePosition.back) {
                            captureDevice = device
                            if captureDevice != nil {
                                print("Capture device found")
                                beginSession()
                            }
                        }
                    }
                }
            }
        }
    
        @IBAction func actionCameraCapture(_ sender: AnyObject) {
    
            print("Camera button pressed")
            saveToCamera()
        }
    
        func beginSession() {
    
            do {
                try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
                stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]
    
                if captureSession.canAddOutput(stillImageOutput) {
                    captureSession.addOutput(stillImageOutput)
                }
    
            }
            catch {
                print("error: \(error.localizedDescription)")
            }
    
            guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {
                print("no preview layer")
                return
            }
            // this is what displays the camera view. But - it's on TOP of the drawn view, and under the overview. ??
            self.view.layer.addSublayer(previewLayer)
            previewLayer.frame = self.view.layer.frame
    
    
            imgOverlay.frame = self.view.frame
            imgOverlay.image = self.drawCirclesOnImage(fromImage: nil, targetSize: imgOverlay.bounds.size)
    
            self.view.bringSubview(toFront: navigationBar)
            self.view.bringSubview(toFront: imgOverlay)
            self.view.bringSubview(toFront: btnCapture)
            // don't use shapeLayer anymore...
            //      self.view.bringSubview(toFront: shapeLayer)
    
    
            captureSession.startRunning()
            print("Capture session running")
    
        }
    
        func getImageWithColor(color: UIColor, size: CGSize) -> UIImage {
            let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size.width, height: size.height))
            UIGraphicsBeginImageContextWithOptions(size, false, 0)
            color.setFill()
            UIRectFill(rect)
            let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
            UIGraphicsEndImageContext()
            return image
        }
    
        func drawCirclesOnImage(fromImage: UIImage? = nil, targetSize: CGSize? = CGSize.zero) -> UIImage? {
    
            if fromImage == nil && targetSize == CGSize.zero {
                return nil
            }
    
            var tmpimg: UIImage?
    
            if targetSize == CGSize.zero {
    
                tmpimg = fromImage
    
            } else {
    
                tmpimg = getImageWithColor(color: UIColor.clear, size: targetSize!)
    
            }
    
            guard let img = tmpimg else {
                return nil
            }
    
            let imageSize = img.size
            let scale: CGFloat = 0
            UIGraphicsBeginImageContextWithOptions(imageSize, false, scale)
    
            img.draw(at: CGPoint.zero)
    
            let w = imageSize.width
    
            let midX = imageSize.width / 2
            let midY = imageSize.height / 2
    
            // red circles - radius in %
            let circleRads = [ 0.07, 0.13, 0.17, 0.22, 0.29, 0.36, 0.40, 0.48, 0.60, 0.75 ]
    
            // center "dot" - radius is 1.5%
            var circlePath = UIBezierPath(arcCenter: CGPoint(x: midX,y: midY), radius: CGFloat(w * 0.015), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    
            UIColor.red.setFill()
            circlePath.stroke()
            circlePath.fill()
    
            // blue circle is between first and second red circles
            circlePath = UIBezierPath(arcCenter: CGPoint(x: midX,y: midY), radius: w * CGFloat((circleRads[0] + circleRads[1]) / 2.0), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    
            UIColor.blue.setStroke()
            circlePath.lineWidth = 2.5
            circlePath.stroke()
    
            UIColor.red.setStroke()
    
            for pct in circleRads {
    
                let rad = w * CGFloat(pct)
    
                circlePath = UIBezierPath(arcCenter: CGPoint(x: midX, y: midY), radius: CGFloat(rad), startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
    
                circlePath.lineWidth = 2.5
                circlePath.stroke()
    
            }
    
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
    
            UIGraphicsEndImageContext()
    
            return newImage
        }
    
        func saveToCamera() {
    
            if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
                stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
    
                    if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
                        if let cameraImage = UIImage(data: imageData) {
    
                            if let nImage = self.drawCirclesOnImage(fromImage: cameraImage, targetSize: CGSize.zero) {
                                UIImageWriteToSavedPhotosAlbum(nImage, nil, nil, nil)
                            }
    
                        }
                    }
                })
            }
        }
    
    
    
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
            // Dispose of any resources that can be recreated.
        }
    }
    

    【讨论】:

    • 有趣。感谢那。现在我有两种方法可以达到几乎相同的结果。我一开始就被类声明抓住了 - class CameraWithTargetViewController: UIViewController { - 仅此而已。圆圈法师位于导航栏的顶部 - 但这没问题。我今天需要学习这个,它也可以满足我的所有需求。这个“func drawCirclesOnImage(”真的很有趣。
    【解决方案2】:

    好的,基本解决了。重要的代码在这里。生成的图像有点歪斜,但我会解决这个问题并修复它,除非有人能找到一个好的修复方法。

        func saveToCamera() {
    
        if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
              stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
    
                if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
                    if let cameraImage = UIImage(data: imageData) {
                        // cameraImage is the camera preview image.
    
                        // I need to combine/merge it with the myImage that is actually the blue circles.
    
                        // This converts the UIView of the bllue circles to an image. Uses 'extension' at top of code.
                        let myImage = UIImage(view: self.shapeLayer)
                        print("converting myImage to an image")
    
                        let newImage = self.composite(image:cameraImage, overlay:(myImage), scaleOverlay:true)
                       UIImageWriteToSavedPhotosAlbum(newImage!, nil, nil, nil)
    
                    }
                }
            })
        }
    }
    
    func composite(image:UIImage, overlay:(UIImage), scaleOverlay: Bool = false)->UIImage?{
        UIGraphicsBeginImageContext(image.size)
        var rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height)
        image.draw(in: rect)
        if scaleOverlay == false {
            rect = CGRect(x: 0, y: 0, width: overlay.size.width, height: overlay.size.height)
        }
        overlay.draw(in: rect)
        return UIGraphicsGetImageFromCurrentImageContext()
    }
    

    生成的保存图像。

    【讨论】:

    • 我刚刚注意到 iPad (mini) 的显示和图像尺寸不正确,可能是任何 iPad。视觉和保存的图像尺寸为 1080x1920 - iPhone 6S Plus 尺寸。部署信息设置为通用。
    • 因此可能是圆圈的覆盖大小已固定到位。现在我在哪里可以找到它。
    猜你喜欢
    • 2017-03-24
    • 1970-01-01
    • 1970-01-01
    • 2017-02-15
    • 1970-01-01
    • 1970-01-01
    • 2017-07-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多