iphone objective-c ios avfoundation cmsamplebufferref

iphone - iOS-Escala y recorte CMSampleBufferRef/CVImageBufferRef



objective-c avfoundation (5)

Nota: No me di cuenta de que la pregunta original también pedía una escala. Pero de todos modos, para aquellos que simplemente necesitan recortar CMSampleBuffer, aquí está la solución.

El búfer es simplemente una matriz de píxeles, por lo que puede procesar el búfer directamente sin usar vImage. El código está escrito en Swift, pero creo que es fácil encontrar el equivalente de Objective-C.

En primer lugar, asegúrese de que su CMSampleBuffer esté en formato BGRA. De lo contrario, el ajuste preestablecido que utilice es probablemente YUV y arruina los bytes por filas que se usarán más adelante.

dataOutput = AVCaptureVideoDataOutput() dataOutput.videoSettings = [ String(kCVPixelBufferPixelFormatTypeKey): NSNumber(value: kCVPixelFormatType_32BGRA) ]

Luego, cuando obtenga el búfer de muestra:

let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! CVPixelBufferLockBaseAddress(imageBuffer, .readOnly) let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) let cropWidth = 640 let cropHeight = 640 let colorSpace = CGColorSpaceCreateDeviceRGB() let context = CGContext(data: baseAddress, width: cropWidth, height: cropHeight, bitsPerComponent: 8, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue | CGBitmapInfo.byteOrder32Little.rawValue) // now the cropped image is inside the context. // you can convert it back to CVPixelBuffer // using CVPixelBufferCreateWithBytes if you want. CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) // create image let cgImage: CGImage = context!.makeImage()! let image = UIImage(cgImage: cgImage)

Si desea recortar desde alguna posición específica, agregue el siguiente código:

// calculate start position let bytesPerPixel = 4 let startPoint = [ "x": 10, "y": 10 ] let startAddress = baseAddress + startPoint["y"]! * bytesPerRow + startPoint["x"]! * bytesPerPixel

y cambie la baseAddress en CGBitmapContextCreate en startAddress . Asegúrese de no exceder el ancho y la altura de la imagen de origen.

Estoy usando AVFoundation y obteniendo el búfer de muestra de AVCaptureVideoDataOutput , puedo escribirlo directamente en videoWriter usando:

- (void)writeBufferFrame:(CMSampleBufferRef)sampleBuffer { CMTime lastSampleTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer); if(self.videoWriter.status != AVAssetWriterStatusWriting) { [self.videoWriter startWriting]; [self.videoWriter startSessionAtSourceTime:lastSampleTime]; } [self.videoWriterInput appendSampleBuffer:sampleBuffer]; }

Lo que quiero hacer ahora es recortar y escalar la imagen dentro de CMSampleBufferRef sin convertirla en UIImage o CGImageRef porque eso ralentiza el rendimiento.


Para escalar puede hacer que AVFoundation haga esto por usted. Ver mi reciente publicación here . Al establecer el valor para AVVideoWidth / AVVideoHeight, la escala escalará las imágenes si no tienen las mismas dimensiones. Eche un vistazo a las propiedades here . En cuanto a los recortes, no estoy seguro de que AVFoundation pueda hacer esto por usted. Puede que tenga que recurrir al uso de OpenGL o CoreImage. Hay un par de buenos enlaces en la publicación superior para esta pregunta SO .


Podría considerar el uso de CoreImage (5.0+).

CIImage *ciImage = [CIImage imageWithCVPixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer) options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], kCIImageColorSpace, nil]]; ciImage = [[ciImage imageByApplyingTransform:myScaleTransform] imageByCroppingToRect:myRect];


Si usa vimage, puede trabajar directamente en los datos del búfer sin convertirlos a ningún formato de imagen.

outImg contiene los datos de imagen recortados y escalados. La relación entre outWidth y cropWidth establece la escala.

int cropX0, cropY0, cropHeight, cropWidth, outWidth, outHeight; CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer,0); void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer); size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); vImage_Buffer inBuff; inBuff.height = cropHeight; inBuff.width = cropWidth; inBuff.rowBytes = bytesPerRow; int startpos = cropY0*bytesPerRow+4*cropX0; inBuff.data = baseAddress+startpos; unsigned char *outImg= (unsigned char*)malloc(4*outWidth*outHeight); vImage_Buffer outBuff = {outImg, outHeight, outWidth, 4*outWidth}; vImage_Error err = vImageScale_ARGB8888(&inBuff, &outBuff, NULL, 0); if (err != kvImageNoError) NSLog(@" error %ld", err);

Por lo tanto, establecer cropX0 = 0 y cropY0 = 0 y cropWidth y cropHeight en el tamaño original significa que no se recortará (utilizando toda la imagen original). Si se establece outWidth = cropWidth and outHeight = cropHeight, no se obtiene ninguna escala. Tenga en cuenta que inBuff.rowBytes siempre debe tener la longitud del búfer de origen completo, no la longitud recortada.


Prueba esto en Swift3

func resize(_ destSize: CGSize)-> CVPixelBuffer? { guard let imageBuffer = CMSampleBufferGetImageBuffer(self) else { return nil } // Lock the image buffer CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0)) // Get information about the image let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) let bytesPerRow = CGFloat(CVPixelBufferGetBytesPerRow(imageBuffer)) let height = CGFloat(CVPixelBufferGetHeight(imageBuffer)) let width = CGFloat(CVPixelBufferGetWidth(imageBuffer)) var pixelBuffer: CVPixelBuffer? let options = [kCVPixelBufferCGImageCompatibilityKey:true, kCVPixelBufferCGBitmapContextCompatibilityKey:true] let topMargin = (height - destSize.height) / CGFloat(2) let leftMargin = (width - destSize.width) * CGFloat(2) let baseAddressStart = Int(bytesPerRow * topMargin + leftMargin) let addressPoint = baseAddress!.assumingMemoryBound(to: UInt8.self) let status = CVPixelBufferCreateWithBytes(kCFAllocatorDefault, Int(destSize.width), Int(destSize.height), kCVPixelFormatType_32BGRA, &addressPoint[baseAddressStart], Int(bytesPerRow), nil, nil, options as CFDictionary, &pixelBuffer) if (status != 0) { print(status) return nil; } CVPixelBufferUnlockBaseAddress(imageBuffer,CVPixelBufferLockFlags(rawValue: 0)) return pixelBuffer; }