美文网首页
iOS 一个简单的滤镜相机

iOS 一个简单的滤镜相机

作者: 一天天的啊哈哈 | 来源:发表于2023-04-05 14:18 被阅读0次

CIImage做滤镜,Metal显示。
格式不太好,当做记录吧...
整体代码如下:

// capture
var captureSession : AVCaptureSession!
var backCamera : AVCaptureDevice!
var backInput : AVCaptureInput!
var videoOutput : AVCaptureVideoDataOutput!
var photoOutput : AVCapturePhotoOutput!
var captureImage : UIImage!

// metal
var metalDevice : MTLDevice!
var metalCommandQueue : MTLCommandQueue!
    
// core image
var ciContext : CIContext!
var currentCIImage : CIImage?
let transferFilter = CIFilter(name: "CIPhotoEffectTransfer")

// view
let captureImageButton : UIButton = {
    let button = UIButton()
    button.backgroundColor = .white
    button.tintColor = .white
    button.layer.cornerRadius = 25
    button.translatesAutoresizingMaskIntoConstraints = false
    return button
}()
    
let mtkView = MTKView()

override func viewDidLoad() {
    super.viewDidLoad()
    setupView()
    checkPermissions()
    checkAlbum()
    setupMetal()
    setupCoreImage()
    setupAndStartCaptureSession()
}

界面初始化

func setupView() {
    view.backgroundColor = .black
    mtkView.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(mtkView)
    view.addSubview(captureImageButton)
        
    NSLayoutConstraint.activate([
        captureImageButton.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
        captureImageButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
        captureImageButton.widthAnchor.constraint(equalToConstant: 50),
        captureImageButton.heightAnchor.constraint(equalToConstant: 50),
            
        mtkView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        mtkView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
        mtkView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
        mtkView.topAnchor.constraint(equalTo: view.topAnchor)
    ])
        
    captureImageButton.addTarget(self, action: #selector(captureImageClick(_:)), for: .touchUpInside)
}

相机权限

// 相机权限
func checkPermissions() {
    let cameraAuthStatus = AVCaptureDevice.authorizationStatus(for: AVMediaType.video)
    switch cameraAuthStatus {
        case .notDetermined:
        AVCaptureDevice.requestAccess(for: AVMediaType.video) { (authorized) in
            if (!authorized) {
                abort()
            }
        }
        case .restricted:
            abort()
        case .denied:
            abort()
        case .authorized:
            return
        @unknown default:
            fatalError()
    }
}

相册权限

// 相册权限
func checkAlbum() {
    let authorStatus:PHAuthorizationStatus? = PHPhotoLibrary.authorizationStatus(for: PHAccessLevel.readWrite)
    switch authorStatus {
    case .notDetermined:
        PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
                
        }
        break
    case .authorized:
            
        break
    case .restricted:

        break
    case .denied:

        break
    case .limited:
            
        break
    default:
        break
    }
}

Metal初始化

// metal
func setupMetal() {
    metalDevice = MTLCreateSystemDefaultDevice()
    mtkView.device = metalDevice
    mtkView.isPaused = true
    mtkView.delegate = self
    mtkView.framebufferOnly = false
        
    metalCommandQueue = metalDevice.makeCommandQueue()
}

CoreImage

// core image
func setupCoreImage() {
    ciContext = CIContext(mtlDevice: metalDevice)
}

func applyFilters(inputImage image: CIImage) -> CIImage? {
    var filteredImage : CIImage?
        
    transferFilter?.setValue(image, forKeyPath: kCIInputImageKey)
    filteredImage = transferFilter?.outputImage
        
    return filteredImage
}

CaptureSession

// camera
func setupAndStartCaptureSession() {
    DispatchQueue.global(qos: .userInitiated).async {
        self.captureSession = AVCaptureSession()
        self.captureSession.beginConfiguration()
        if self.captureSession.canSetSessionPreset(.photo) {
            self.captureSession.sessionPreset = .photo
        }
        self.captureSession.automaticallyConfiguresCaptureDeviceForWideColor = true
            
        self.setupInputs()
        self.setupOutput()
            
        self.captureSession.commitConfiguration()
        self.captureSession.startRunning()
    }
}

func setupInputs() {
    if let device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) {
        backCamera = device
    } else {
        fatalError("no back camera")
    }
        
    guard let bInput = try? AVCaptureDeviceInput(device: backCamera) else {
        fatalError()
    }
        
    backInput = bInput
    if !captureSession.canAddInput(backInput) {
        fatalError()
    }
        
    captureSession.addInput(backInput)
}

func setupOutput() {
    videoOutput = AVCaptureVideoDataOutput()
    let videoQueue = DispatchQueue(label: "videoQueue")
    videoOutput.setSampleBufferDelegate(self, queue: videoQueue)
        
    if captureSession.canAddOutput(videoOutput) {
        captureSession.addOutput(videoOutput)
    } else {
        fatalError()
    }
        
    videoOutput.connections.first?.videoOrientation = .portrait
    photoOutput = AVCapturePhotoOutput()
        
    if captureSession.canAddOutput(photoOutput) {
        captureSession.addOutput(photoOutput)
    }
}

画面渲染

extension ViewController : AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        guard let cvBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
            return
        }
        
        let ciImage = CIImage(cvImageBuffer: cvBuffer)
        
        guard let filteredCIImage = applyFilters(inputImage: ciImage) else {
            return
        }
        self.currentCIImage = filteredCIImage
        
        mtkView.draw()
    }
}

extension ViewController : MTKViewDelegate {
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    }
    
    func draw(in view: MTKView) {
        guard let commandBuffer = metalCommandQueue.makeCommandBuffer() else {
            return
        }
        
        guard let ciImage = currentCIImage else {
            return
        }
        
        guard let currentDrawable = view.currentDrawable else {
            return
        }
        
        let heightOfciImage = ciImage.extent.height
        let heightOfDrawable = view.drawableSize.height
        let yOffsetFromBottom = (heightOfDrawable - heightOfciImage)/2
        
        self.ciContext.render(ciImage, to: currentDrawable.texture, commandBuffer: commandBuffer, bounds: CGRect(origin: CGPoint(x: 0, y: -yOffsetFromBottom), size: view.drawableSize), colorSpace: CGColorSpaceCreateDeviceRGB())
        
        commandBuffer.present(currentDrawable)
        commandBuffer.commit()
    }
}

图片输出

extension ViewController : AVCapturePhotoCaptureDelegate {
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        // you can do something
        guard let photoData = photo.fileDataRepresentation() else {
            return
        }
        
        guard let ciImage = CIImage(data: photoData) else {
            return
        }
        
        guard let filteredCIImage = applyFilters(inputImage: ciImage) else {
            return
        }
        
        self.captureImage = UIImage(ciImage: filteredCIImage)
    }
    
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) {
        if self.captureImage == nil {
            return
        }
        // save
    }
}

Click

@objc func captureImageClick(_ sender: UIButton?) {
    let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey:AVVideoCodecType.jpeg])
    settings.flashMode = .off
    photoOutput.capturePhoto(with: settings, delegate: self)
}

相关文章

  • 猫儿相机

    猫儿相机是一款简单高效的滤镜相机,拥有多款免费滤镜,实时拍照多款优质滤镜任您选择,让您找到属于自己的颜色!猫儿相机...

  • 收藏博客

    IOS开发 相机滤镜,OPENGL,美图秀秀Demo: https://www.cnblogs.com/salam...

  • Camera2 教程 3:实现简单相机预览

    在 相机滤镜2:图片动态切换滤镜章节,我们实现了通过OPENGL ES实现图片显示和简单滤镜效果;本章节,将复用上...

  • GPUImage学习

    前言 一些自带滤镜 支持滤镜摄像,滤镜相机,滤镜图片,滤镜视频 一.使用滤镜生成UIImage对象 创建一个滤镜类...

  • iOS 相机实时滤镜效果

    项目Demo,实现了实时滤镜、拍照、录像功能。 前言 最近玩了哈实时滤镜,学到挺多东西的。笔者长得丑,看看有没有机...

  • ios 三方库(收集)

    常用的ios三方库: HYFileManager 简单实用的iOS文件工具类 GPUImage 一个滤镜摄像头效...

  • 直播美颜功能GPUImage

    GPUImage是一个基于GPU图像和视频处理的开源iOS框架,提供各种各样的图像处理滤镜,并且支持照相机和...

  • GPUImage给相机丶视频丶图片添加滤镜

    使用GPUImage实现简单的视频丶相机的滤镜是非常简单的,实现的基本原理是GPUImageStillCamera...

  • Android CameraX结合LibYUV和GPUImage

    目录 前言 之前使用Camera实现了一个自定义相机滤镜(Android自定义相机滤镜[https://www.j...

  • 少女心野餐必备好物,和野餐拍照小技巧分享。

    和男朋友野餐的一天,简单、浪漫、知足。? 图片滤镜:奶油ll(好喜欢这个滤镜) 修图工具:黄油相机 ?少女心野餐必...

网友评论

      本文标题:iOS 一个简单的滤镜相机

      本文链接:https://www.haomeiwen.com/subject/blveddtx.html