26

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 に変換せずにトリミングおよびスケーリングすることです。これは、パフォーマンスが低下するためです。

4

5 に答える 5

35

vimage を使用すると、バッファ データを画像形式に変換せずに直接操作できます。

outImgトリミングおよびスケーリングされた画像データが含まれています。と の関係でスケーリング outWidthを設定します。cropWidthvimage トリミング

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 = 0cropY0 = 0cropWidthとを元のサイズに設定すると、 cropHeight(元の画像全体を使用して) トリミングされません。設定するoutWidth = cropWidthoutHeight = cropHeight、スケーリングは行われません。inBuff.rowBytesトリミングされた長さではなく、常に完全なソース バッファーの長さである必要があることに注意してください。

于 2012-04-08T13:02:35.763 に答える
10

注:元の質問でもスケーリングが要求されていることに気づきませんでした。とにかく、単に 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

を に変更baseAddressCGContext()ますstartAddress。元の画像の幅と高さを超えないようにしてください。

于 2015-07-28T17:45:01.530 に答える
9

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

CIImage *ciImage = [CIImage imageWithCVPixelBuffer:CMSampleBufferGetImageBuffer(sampleBuffer)
                                           options:[NSDictionary dictionaryWithObjectsAndKeys:[NSNull null], kCIImageColorSpace, nil]];
ciImage = [[ciImage imageByApplyingTransform:myScaleTransform] imageByCroppingToRect:myRect];
于 2011-12-13T18:32:27.970 に答える
1

スケーリングの場合、AVFoundation にこれを実行させることができます。ここで私の最近の投稿を参照してください。AVVideoWidth/AVVideoHeight キーの値を設定すると、サイズが異なる場合に画像がスケーリングされます。ここでプロパティを見てください。クロッピングに関しては、AVFoundation にこれを行わせることができるかどうかわかりません。OpenGL または CoreImage を使用する必要がある場合があります。このSO questionの一番上の投稿には、いくつかの良いリンクがあります。

于 2011-12-16T03:53:54.210 に答える
1

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;
    }
于 2017-06-26T15:30:12.107 に答える