2011-12-13 11 views
16

Sto usando AVFoundation e ottenere il tampone da AVCaptureVideoDataOutput, posso scrivere direttamente al videoWriter utilizzando:iOS - Scala e raccolto CMSampleBufferRef/CVImageBufferRef

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

    [self.videoWriterInput appendSampleBuffer:sampleBuffer]; 

} 

Quello che voglio fare ora è quello di ritagliare e ridimensiona l'immagine all'interno di CMSampleBufferRef senza convertirla in UIImage o CGImageRef perché ciò rallenta le prestazioni.

risposta

21

Se si utilizza vimage, è possibile lavorare direttamente sui dati del buffer senza convertirli in alcun formato di immagine.

outImg contiene i dati dell'immagine ritagliata e ridimensionata. La relazione tra outWidth e cropWidth imposta il ridimensionamento. vimage cropping

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); 

Quindi impostando cropX0 = 0 e cropY0 = 0 e cropWidth e cropHeight alla dimensione originale significa nessuna ritaglio (utilizzando l'intera immagine originale). Impostare outWidth = cropWidth e outHeight = cropHeight non comporta ridimensionamenti. Si noti che inBuff.rowBytes dovrebbe sempre essere la lunghezza del buffer di origine completo, non la lunghezza ritagliata.

+0

Hi Sten, ho cercato nella guida un esempio di ritaglio ma non è stato possibile correggerne uno, puoi dare un esempio su come ritagliare il buffer direttamente? – Eyal

+0

v L'immagine non ha funzionalità di ritaglio – AlexeyVMP

+0

È corretto che vimage non abbia una funzione di ritaglio dedicata. Ma impostando la dimensione del buffer è possibile applicare un ritaglio insieme essenzialmente a qualsiasi funzione, ad es. una scala. Ho aggiornato la mia risposta con un esempio – Sten

7

È possibile considerare l'utilizzo di CoreImage (5.0+).

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

e poi, come posso riconvertirlo in CMSampleBuffer, o come posso scriverlo usando self.videoWriterInput? – vodkhang

+0

qualcuno qualche idea? – Ondrej

+0

CIContext dispone di metodi per rasterizzare un oggetto CII in un CVPixelBuffer. –

1

Per il ridimensionamento, AVFoundation può fare ciò per te. Vedi il mio recente post here. L'impostazione del valore per AVVideoWidth/AVVideoHeight ridimensiona le immagini se non hanno le stesse dimensioni. Dai un'occhiata alle proprietà here. Come per il ritaglio non sono sicuro che tu possa fare AVFoundation per te. Potrebbe essere necessario ricorrere a OpenGL o CoreImage. Ci sono un paio di buoni link nel post superiore per questo SO question.

+0

Posso renderlo automaticamente scalabile per me, ma continua a lamentarmi di aver esaurito la memoria, dato che puoi guardare il mio post più nuovo qui http://stackoverflow.com/questions/8561456/ios-automatically-resize-cvpixelbufferref. Sembra che continui a cambiare le dimensioni – vodkhang

4

Avviso

Recentemente ho bisogno di scrivere di nuovo questa funzione, e ha scoperto che questo metodo non sembra più funzionare (almeno io non sono riuscito a farlo funzionare su iOS 10.3.1). L'immagine di output non si allineava. Suppongo che sia a causa del torto bytesPerRow.


risposta originale

Il buffer è semplicemente una matrice di pixel, quindi in realtà è possibile elaborare il buffer direttamente senza utilizzare vimage. Il codice è scritto in Swift, ma penso che sia facile trovare l'equivalente Objective-C.

Swift 3

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) 

CVPixelBufferUnlockBaseAddress(imageBuffer, .readOnly) 

// create image 
let cgImage: CGImage = context!.makeImage()! 
let image = UIImage(cgImage: cgImage) 

Swift 2

let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)! 

CVPixelBufferLockBaseAddress(imageBuffer, 0) 

let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer) 
let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer) 
let cropWidth = 640 
let cropHeight = 640 
let colorSpace = CGColorSpaceCreateDeviceRGB() 

let context = CGBitmapContextCreate(baseAddress, cropWidth, cropHeight, 8, bytesPerRow, colorSpace, CGImageAlphaInfo.NoneSkipFirst.rawValue | CGBitmapInfo.ByteOrder32Little.rawValue) 

// create image 
let cgImage: CGImageRef = CGBitmapContextCreateImage(context)! 
let image = UIImage(CGImage: cgImage) 

Se si vuole ritagliare da qualche specifica posizione, aggiungere il seguente codice:

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

e il cambiamento lo baseAddress in CGBitmapContextCreate in startAddress. Assicurati di non superare la larghezza e l'altezza dell'immagine di origine.

+2

"... senza convertirlo in UIImage o CGImageRef perché ciò rallenta le prestazioni." – vkalit

+1

Ho fatto" ritagliare e ridimensionare l'immagine all'interno del CMSampleBufferRef senza convertirlo in UIImage o CGImageRef ". L'ho appena salvato come CGImageRef per ulteriore utilizzo (ad esempio mostra sullo schermo). Puoi fare quello che vuoi con il contesto ritagliato. – yuji

+0

Ciao 黃 昱嘉 qual è il modo migliore per contattarti? Vorrei fare una breve domanda Grazie – Crashalot

0

provare questo 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; 
    }