Я пытаюсь обрезать CVImageBuffer (из AVCaptureOutput), используя boundingBox обнаруженного лица из Vision (VNRequest). Когда я рисую над AVCaptureVideoPreviewLayer, используя:
let origin = previewLayer.layerPointConverted(fromCaptureDevicePoint: rect.origin)
let size = previewLayer.layerPointConverted(fromCaptureDevicePoint: rect.size.cgPoint)
для преобразования прямоугольника ограничивающего прямоугольника в координату previewLayer, все работает как положено. Но когда я пытаюсь применить к CVImageBuffer (с другим размером), результат неправильный.
Ниже вы можете увидеть, как я конвертируюкоордината возвращенного ограничивающего прямоугольника и способ обрезки CVPixelBuffer
class ViewController: UIViewController {
var sequenceHandler = VNSequenceRequestHandler()
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
// 1
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return }
// 2
let detectFaceRequest = VNDetectFaceLandmarksRequest(completionHandler: { [weak self] (request, error) in
guard error == nil else { return }
let cropedImage = self?.crop(request: request, imageBuffer: imageBuffer) // Croped buffer
})
// 3
do {
try sequenceHandler.perform(
[detectFaceRequest],
on: imageBuffer,
orientation: .leftMirrored)
} catch {
print(error.localizedDescription)
}
}
func crop(request: VNRequest, imageBuffer: CVImageBuffer) -> CVImageBuffer? {
guard let results = request.results as? [VNFaceObservation], let result = results.first else { return nil }
let width = CGFloat(CVPixelBufferGetWidth(imageBuffer)) // 1080
let height = CGFloat(CVPixelBufferGetHeight(imageBuffer)) // 1920
let cropRect = VNImageRectForNormalizedRect(result.boundingBox, Int(width), Int(height)) // Converting the boundingbox rect to the the image
CVPixelBufferLockBaseAddress(imageBuffer, .readOnly)
guard let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) else { return nil }
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer)
let bytesPerPixel = 4
let startAddress = baseAddress + Int(cropRect.origin.y) * bytesPerRow + Int(cropRect.origin.x) * bytesPerPixel
guard let context = CGContext(data: startAddress, width: Int(cropRect.size.width), height: Int(cropRect.size.height), bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue), let data = context.data else {
return nil
}
var pixelBuffer: CVPixelBuffer?
let createBufferResult = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, Int(cropRect.size.width), Int(cropRect.size.height), kCVPixelFormatType_32BGRA, data, bytesPerRow, nil, nil, nil, &pixelBuffer)
guard createBufferResult != 0 else {
print(createBufferResult)
return nil
}
CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly)
return pixelBuffer
}
}