2011-12-13 3 views
16

Я использую AVFoundation и получение буфера образца из AVCaptureVideoDataOutput, я могу написать это непосредственно videoWriter с помощью:IOS - Масштаб и растениеводство 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]; 

} 

То, что я хочу сделать сейчас, чтобы обрезать и масштабируйте изображение внутри CMSampleBufferRef без преобразования его в UIImage или CGImageRef, поскольку это замедляет производительность.

ответ

21

Если вы используете vimage, вы можете работать непосредственно с данными буфера без преобразования его в любой формат изображения.

outImg содержит данные обрезанных и масштабированных изображений. Связь между outWidth и cropWidth устанавливает масштабирование. 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); 

Так установка cropX0 = 0 и cropY0 = 0 и cropWidth и cropHeight к исходному размеру означает не кадрирование (используя весь исходное изображение). Установка outWidth = cropWidth и outHeight = cropHeight не приводит к масштабированию. Обратите внимание, что inBuff.rowBytes всегда должно быть длиной полного исходного буфера, а не обрезанной длины.

+0

Привет, Стив, я искал руководство для примера обрезки, но не мог его отличить, можно ли привести пример, как обрезать буфер напрямую? – Eyal

+0

vImage не имеет функций обрезки – AlexeyVMP

+0

Правильно, что vimage не имеет выделенной функции обрезки. Но, установив размер буфера, вы можете применить урожай вместе с практически любой функцией, например. масштаб. Я обновил свой ответ на примере – Sten

7

Возможно, вы можете использовать CoreImage (5.0+).

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

, а затем, как я могу преобразовать его обратно в CMSampleBuffer, или как я могу написать его вниз с помощью self.videoWriterInput? – vodkhang

+0

кто-нибудь идеи? – Ondrej

+0

CIContext имеет методы для растеризации CIImage обратно в CVPixelBuffer. –

1

Для масштабирования вы можете использовать AVFoundation для этого. См. Мое последнее сообщение here. Установка значения для кнопки AVVideoWidth/AVVideoHeight будет масштабировать изображения, если они не совпадают. Взгляните на свойства here. Что касается обрезки, я не уверен, что вы можете сделать AVFoundation для этого. Возможно, вам придется прибегать к использованию OpenGL или CoreImage. Есть пара хороших ссылок в верхнем посте для этого SO question.

+0

Я могу сделать это автоматически масштабируемым для меня, но он продолжает жаловаться на нехватку памяти, так как вы можете посмотреть мой новый пост здесь http://stackoverflow.com/questions/8561456/ios-automatically-resize-cvpixelbufferref. Кажется, что причина в том, что я продолжаю изменять размер – vodkhang

4

Уведомление

Недавно я должен был написать эту функцию еще раз, и обнаружили, что этот метод не похоже на работу больше (по крайней мере, я не мог заставить его работать на IOS 10.3.1). Выходное изображение не будет выравниваться. Я предполагаю, что это из-за неправильного bytesPerRow.


Оригинал ответ

Буфер просто массив пикселей, так что вы на самом деле можете обрабатывать буфер непосредственно без использования vImage. Код написан в Swift, но я думаю, что легко найти эквивалент 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) 

Если вы хотите обрезать с какой-то конкретной позиции, добавьте следующий код:

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

и изменения baseAddress в CGBitmapContextCreate - startAddress. Не превышайте ширину и высоту изображения оригинала.

+2

«... без преобразования в UIImage или CGImageRef, поскольку это замедляет производительность.« – vkalit

+1

Я« обрезал и масштабировал изображение внутри CMSampleBufferRef без преобразования его в UIImage или CGImageRef ». Я просто сохраняю его как CGImageRef для дальнейшего использования (например, показать на экране). Вы можете делать все, что хотите, с обрезанным контекстом. – yuji

+0

Привет 黃 昱嘉 Каков наилучший способ связаться с вами? Хотелось бы задать короткий вопрос. Спасибо! – Crashalot

0

Попробуйте это на 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; 
    }