2011-12-13 34 views
16

Tôi đang sử dụng AVFoundation và nhận được đệm mẫu từ AVCaptureVideoDataOutput, tôi có thể viết trực tiếp đến videoWriter bằng cách sử dụng:iOS - Quy mô và cây trồng 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]; 

} 

Những gì tôi muốn làm bây giờ là để cắt và chia tỷ lệ hình ảnh bên trong CMSampleBufferRef mà không chuyển đổi nó thành UIImage hoặc CGImageRef vì nó làm chậm hiệu năng.

Trả lời

21

Nếu bạn sử dụng vimage, bạn có thể làm việc trực tiếp trên dữ liệu đệm mà không chuyển đổi nó sang bất kỳ định dạng hình ảnh nào.

outImg chứa dữ liệu hình ảnh được cắt và thu nhỏ. Mối quan hệ giữa outWidth và cropWidth đặt tỷ lệ. 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); 

Vì vậy, việc thiết cropX0 = 0 và cropY0 = 0 và cropWidth và cropHeight với kích thước ban đầu có nghĩa là không cắt xén (sử dụng toàn bộ ảnh gốc). Thiết lậpWidth = cropWidth và outHeight = cropHeight kết quả không có tỷ lệ. Lưu ý rằng inBuff.rowBytes phải luôn là độ dài của bộ đệm nguồn đầy đủ, không phải là độ dài cắt.

+0

Hi Sten, tôi đã tìm kiếm hướng dẫn cho một ví dụ cắt nhưng không thể tốt, bạn có thể đưa ra một ví dụ làm thế nào để cắt bộ đệm trực tiếp? – Eyal

+0

vImage không có chức năng cắt ảnh – AlexeyVMP

+0

Đúng là vimage không có chức năng cắt xén chuyên dụng. Nhưng bằng cách đặt kích thước của bộ đệm, bạn có thể áp dụng một loại cây trồng cùng với bất kỳ chức năng cơ bản nào, ví dụ: một quy mô. Tôi đã cập nhật câu trả lời của mình với một ví dụ – Sten

7

Bạn có thể xem xét sử dụng CoreImage (5.0+).

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

và sau đó, làm thế nào tôi có thể chuyển đổi nó trở lại CMSampleBuffer, hoặc làm thế nào tôi có thể viết nó xuống bằng self.videoWriterInput? – vodkhang

+0

bất kỳ ai có ý tưởng? – Ondrej

+0

CIContext có phương pháp rasterize một CIImage trở lại thành một CVPixelBuffer. –

1

Để mở rộng quy mô, bạn có thể có AVFoundation thực hiện việc này cho bạn. Xem bài đăng gần đây của tôi here. Đặt giá trị cho khóa AVVideoWidth/AVVideoHeight sẽ chia tỷ lệ hình ảnh nếu chúng không cùng kích thước. Hãy xem các thuộc tính here .Như để cắt xén Tôi không chắc liệu bạn có thể có AVFoundation làm điều này cho bạn hay không. Bạn có thể phải sử dụng OpenGL hoặc CoreImage. Có một vài liên kết tốt trong bài đăng hàng đầu cho số SO question này.

+0

Tôi có thể làm cho nó tự động mở rộng cho tôi, nhưng nó tiếp tục phàn nàn tôi hết bộ nhớ, vì bạn có thể xem bài đăng mới nhất của tôi tại đây http://stackoverflow.com/questions/8561456/ios-automatically-resize-cvpixelbufferref. Có vẻ như lý do là tôi tiếp tục thay đổi kích thước – vodkhang

4

Thông báo

Gần đây tôi cần thiết để viết chức năng này một lần nữa, và thấy rằng phương pháp này dường như không có tác dụng nữa (ít nhất là tôi không thể làm cho nó hoạt động trên iOS 10.3.1). Hình ảnh đầu ra sẽ không được căn chỉnh. Tôi đoán đó là do sai số bytesPerRow.


gốc trả lời

Các bộ đệm chỉ đơn giản là một mảng pixel, do đó, bạn thực sự có thể xử lý bộ đệm trực tiếp mà không sử dụng vImage. Mã được viết bằng Swift, nhưng tôi nghĩ thật dễ dàng để tìm thấy tương đương 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) 

Nếu bạn muốn cắt từ một số vị trí cụ thể, thêm đoạn mã sau:

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

và thay đổi baseAddress trong CGBitmapContextCreate vào startAddress. Đảm bảo không vượt quá chiều rộng và chiều cao của ảnh gốc.

+2

"... mà không chuyển đổi nó thành UIImage hoặc CGImageRef vì điều đó làm chậm hiệu suất." – vkalit

+1

Tôi đã" cắt và chia tỷ lệ hình ảnh bên trong CMSampleBufferRef mà không cần chuyển đổi nó thành UIImage hoặc CGImageRef ".Tôi chỉ lưu nó dưới dạng CGImageRef để sử dụng thêm (ví dụ: hiển thị trên màn hình) Bạn có thể làm bất cứ điều gì bạn muốn với ngữ cảnh bị cắt. – yuji

+0

Hi 黃 昱嘉 cách tốt nhất để liên lạc với bạn là gì? Muốn hỏi một câu hỏi nhanh: Cảm ơn! – Crashalot

0

Hãy thử điều này trên 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; 
    } 
Các vấn đề liên quan