swift - 如何快速从选定区域裁剪视频帧大小?
问题描述
我正在开发视频编辑应用程序。我想通过提供选定区域来裁剪视频,但找不到任何解决方案。
我现在要做的是旋转、裁剪和切换视频。我设法旋转和切换,但我被困在裁剪中。我发现的那段代码只从顶部中心和底部裁剪。我希望将我的纹理视图拖动到视频帧上的任何位置,以获取坐标映射到像素和裁剪。
解决方案
我知道已经有一段时间了,但我会回复以防它可能对其他人有帮助。
您可以使用AVMutableVideoComposition(.., applyingCIFiltersWithHandler)
核心图像过滤器CICrop
来CILanczosScaleTransform
裁剪视频资产,然后将生成的合成导出为新的视频文件
让我给你一个示例代码:
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 进行裁剪。
推荐阅读
- node.js - 如何获取 Firebase 函数请求大小?
- c# - 没有通用方法“ThenBy”
- r - 将字符串转换为具有不同数量元素的数据框
- javascript - 如何在 ReactJS 中打开 Order
- java - Java Generics AnyType,我如何允许调用任何方法?
- mysql - 识别 SQL 中的模式。可能吗?
- android - 使用 RxJava 发出 Single
- > 从给定多个 id 的 firestore
- openstack-neutron - openstack neutron-gateway - 错误:“无法将网络设备 eno2 添加到 ofproto(设备或资源繁忙)”
- discord - 如何“链接”一个频道,例如我的 Discord Bot 消息中的提及?
- python - 不明白这个 tkinter.TclError