AVFoundationを使用していて、サンプルバッファをAVCaptureVideoDataOutput
から取得しています。次を使用して、videoWriterに直接書き込むことができます。
- (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に変換せずにトリミングおよび拡大縮小することです。これにより、パフォーマンスが低下します。
Vimageを使用すると、画像形式に変換せずにバッファデータを直接操作できます。
outImgには、トリミングおよびスケーリングされた画像データが含まれています。 outWidthとcropWidthの関係により、スケーリングが設定されます。
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は、トリミングされた長さではなく、常に完全なソースバッファの長さである必要があることに注意してください。
CoreImage(5.0+)の使用を検討してください。
CIImage *ciImage = [CIImage imageWithCVPixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer)
options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], kCIImageColorSpace, nil]];
ciImage = [[ciImage imageByApplyingTransform:myScaleTransform] imageByCroppingToRect:myRect];
注:元の質問でもスケーリングが要求されていることに気づきませんでした。とにかく、CMSampleBufferを切り抜く必要がある人のために、ここに解決策があります。
バッファは単にピクセルの配列であるため、vImageを使用せずに実際にバッファを直接処理できます。コードはSwiftで書かれていますが、Objective-Cに相当するものを見つけるのは簡単だと思います。
まず、CMSampleBufferがBGRA形式であることを確認します。そうでない場合、使用するプリセットはおそらくYUVであり、後で使用される行あたりのバイト数を台無しにします。
_dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [
String(kCVPixelBufferPixelFormatTypeKey):
NSNumber(value: kCVPixelFormatType_32BGRA)
]
_
次に、サンプルバッファを取得すると、次のようになります。
_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)
_
特定の位置からトリミングする場合は、次のコードを追加します。
_// calculate start position
let bytesPerPixel = 4
let startPoint = [ "x": 10, "y": 10 ]
let startAddress = baseAddress + startPoint["y"]! * bytesPerRow + startPoint["x"]! * bytesPerPixel
_
CGContext()
のbaseAddress
をstartAddress
に変更します。 Origin画像の幅と高さを超えないように注意してください。
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;
}