web-dev-qa-db-ja.com

iOS-CMSampleBufferRef / CVImageBufferRefのスケーリングとトリミング

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に変換せずにトリミングおよび拡大縮小することです。これにより、パフォーマンスが低下します。

23
vodkhang

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は、トリミングされた長さではなく、常に完全なソースバッファの長さである必要があることに注意してください。

33
Sten

CoreImage(5.0+)の使用を検討してください。

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

注:元の質問でもスケーリングが要求されていることに気づきませんでした。とにかく、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()baseAddressstartAddressに変更します。 Origin画像の幅と高さを超えないように注意してください。

8
yuji

スケーリングについては、AVFoundationにこれを行わせることができます。私の最近の投稿を参照してください ここ 。 AVVideoWidth/AVVideoHeightキーの値を設定すると、画像のサイズが同じでない場合に画像が拡大縮小されます。プロパティを見てください ここ 。トリミングに関しては、AVFoundationにこれを実行してもらうことができるかどうかはわかりません。 OpenGLまたはCoreImageの使用に頼らなければならない場合があります。このためのトップ投稿にいくつかの良いリンクがあります SO質問

2
Steve McFarlin

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;
    }
1
wu qiuhao