首页 > 解决方案 > 如何快速从选定区域裁剪视频帧大小?

问题描述

我正在开发视频编辑应用程序。我想通过提供选定区域来裁剪视频,但找不到任何解决方案。

我现在要做的是旋转、裁剪和切换视频。我设法旋转和切换,但我被困在裁剪中。我发现的那段代码只从顶部中心和底部裁剪。我希望将我的纹理视图拖动到视频帧上的任何位置,以获取坐标映射到像素和裁剪。

请参考任何方法或解决方案。谢谢
在此处输入图像描述

标签: swiftvideoavfoundationcropavcapturesession

解决方案


我知道已经有一段时间了,但我会回复以防它可能对其他人有帮助。

您可以使用AVMutableVideoComposition(.., applyingCIFiltersWithHandler)核心图像过滤器CICropCILanczosScaleTransform裁剪视频资产,然后将生成的合成导出为新的视频文件

让我给你一个示例代码:

typealias CropTaskCompletion = (Result<URL, Error>) -> Void

// Calculate video frame (center it)
func cropVideoWithGivenSize(asset: AVAsset, baseSize cropSize: CGSize, completionHandler: @escaping CropTaskCompletion) {
    
    // Create your context and filter
    // I'll use metal context and CIFilter
    if let device = MTLCreateSystemDefaultDevice(),
       let filterCrop = CIFilter(name: "CICrop") {
        context = CIContext(mtlDevice: device, options: [.workingColorSpace : NSNull()])
        cropFilter = filterCrop
    }
    
    // Try getting first video track to get size and transform
    guard let videoTrack = asset.tracks(withMediaType: .video).first else {
        print("No video track to crop.")
        completionHandler(.failure(NSError()))
        return
    }
    // Original video size
    let videoSize = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
    
    // Create a mutable video composition configured to apply Core Image filters to each video frame of the specified asset.
    let composition = AVMutableVideoComposition(asset: asset, applyingCIFiltersWithHandler: { [weak self] request in
        
        guard let `self` = self else { return }
                    
        // Compute scale and corrective aspect ratio
        let scaleX = cropSize.width / videoSize.width
        let scaleY = cropSize.height / videoSize.height
        let rate = max(scaleX, scaleY)
        let width = videoSize.width * rate
        let height = videoSize.height * rate
        let targetSize = CGSize(width: width, height: height)

        // Handle video frame (CIImage)
        let outputImage = request.sourceImage
        
        // Define your crop rect here
        // I would like to crop video from it's center
        let cropRect = CGRect(
            x: (targetSize.width - cropSize.width) / 2,
            y: (targetSize.height - cropSize.height) / 2,
            width: cropSize.width,
            height: cropSize.height
        )
        
        // Add the .sourceImage (a CIImage) from the request to the filter.
        self.cropFilter.setValue(outputImage, forKey: kCIInputImageKey)
        // Specify cropping rectangle with converting it to CIVector
        self.cropFilter.setValue(CIVector(cgRect: cropRect), forKey: "inputRectangle")
        
        // Move the cropped image to the origin of the video frame. When you resize the frame (step 4) it will resize from the origin.
        let imageAtOrigin = self.cropFilter.outputImage!.transformed(
            by: CGAffineTransform(translationX: -cropRect.origin.x, y: -cropRect.origin.y)
        )
        
        request.finish(with: imageAtOrigin, context: self.context)
    })
    
    // Update composition render size
    composition.renderSize = cropSize
    
    // Export cropped video with AVAssetExport session
    guard let export = AVAssetExportSession(
        asset: asset,
        presetName: AVAssetExportPresetHighestQuality)
    else {
        print("Cannot create export session.")
        completionHandler(.failure(NSError()))
        return
    }
    
    let videoName = "Write_Your_Video_Name_Here"
    let exportURL = URL(fileURLWithPath: NSTemporaryDirectory())
        .appendingPathComponent(videoName)
        .appendingPathExtension("mp4") // Change file extension
    
    // Try to remove old file if exist
    try? FileManager.default.removeItem(at: exportURL)
    
    // Assign created mutable video composition to exporter
    export.videoComposition = composition
    export.outputFileType = .mp4 // Change file type (it should be same with extension)
    export.outputURL = exportURL
    export.shouldOptimizeForNetworkUse = false
    
    export.exportAsynchronously {
        DispatchQueue.main.async {
            switch export.status {
            case .completed:
                completionHandler(.success(exportURL))
                break
            default:
                print("Something went wrong during export.")
                if let error = export.error {
                    completionHandler(.failure(error))
                } else {
                    completionHandler(.failure(NSError(domain: "unknown error", code: 0, userInfo: nil)))
                }
                break
            }
        }
    }
}

您可以更改此代码以适合您的目的,我认为这足以让您了解如何使用 Core Image Filters 进行裁剪。


推荐阅读