そこで、Appleの指示に従ってAVCaptureSession
を使用してビデオセッションをキャプチャしました: http://developer.Apple.com/iphone/library/qa/qa2010/qa1702.html 。私が直面している問題の1つは、カメラ/ iPhoneデバイスの向きが垂直(およびAVCaptureVideoPreviewLayer
が垂直カメラストリームを表示)しているにもかかわらず、出力画像が横向きモードになっているように見えることです。サンプルコードのimageFromSampleBuffer:
内のimageBufferの幅と高さを確認し、それぞれ640pxと480pxを取得しました。なぜそうなのか誰もが知っていますか?
ありがとう!
ヘッダーAVCaptureSession.hを見てください。さまざまなビデオの向きを定義するAVCaptureVideoOrientation
という列挙型の定義があります。 AVCaptureConnectionオブジェクトには、AVCaptureVideoOrientation
であるvideoOrientationというプロパティがあります。これを設定して、ビデオの向きを変更できるはずです。おそらくAVCaptureVideoOrientationLandscapeRight
またはAVCaptureVideoOrientationLandscapeLeft
が必要でしょう。
セッションの出力を見ると、セッションのAVCaptureConnectionsを見つけることができます。出力には、その出力の接続の配列であるconnectionsプロパティがあります。
オリエンテーションの問題を修正するために、imageFromSampleBuffer
に簡単な1行の変更を加えました(「I modified ...」の下のコードのコメントを参照)。私がこれに時間をかけすぎたので、それが誰かを助けることを願っています。
// Create a UIImage from sample buffer data
- (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer {
// Get a CMSampleBuffer's Core Video image buffer for the media data
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
// Lock the base address of the pixel buffer
CVPixelBufferLockBaseAddress(imageBuffer, 0);
// Get the number of bytes per row for the pixel buffer
void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
// Get the number of bytes per row for the pixel buffer
size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);
// Get the pixel buffer width and height
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
// Create a device-dependent RGB color space
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
// Create a bitmap graphics context with the sample buffer data
CGContextRef context1 = CGBitmapContextCreate(baseAddress, width, height, 8,
bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
// Create a Quartz image from the pixel data in the bitmap graphics context
CGImageRef quartzImage = CGBitmapContextCreateImage(context1);
// Unlock the pixel buffer
CVPixelBufferUnlockBaseAddress(imageBuffer,0);
// Free up the context and color space
CGContextRelease(context1);
CGColorSpaceRelease(colorSpace);
// Create an image object from the Quartz image
//I modified this line: [UIImage imageWithCGImage:quartzImage]; to the following to correct the orientation:
UIImage *image = [UIImage imageWithCGImage:quartzImage scale:1.0 orientation:UIImageOrientationRight];
// Release the Quartz image
CGImageRelease(quartzImage);
return (image);
}
あなたはこれを難しくしています。
DidOutputSampleBufferで、画像を取得する前に向きを変更するだけです。モノですが、あなたは持っています
public class OutputRecorder : AVCaptureVideoDataOutputSampleBufferDelegate {
public override void DidOutputSampleBuffer (AVCaptureOutput captureOutput, CMSampleBuffer sampleBuffer, AVCaptureConnection connection)
{
try {
connection.videoOrientation = AVCaptureVideoOrientation.LandscapeLeft;
objCでは、このメソッドです
- ( void ) captureOutput: ( AVCaptureOutput * ) captureOutput
didOutputSampleBuffer: ( CMSampleBufferRef ) sampleBuffer
fromConnection: ( AVCaptureConnection * ) connection
これが正しいシーケンスです。
AVCaptureVideoDataOutput *videoCaptureOutput = [[AVCaptureVideoDataOutput alloc] init];
if([self.captureSession canAddOutput:self.videoCaptureOutput]){
[self.captureSession addOutput:self.videoCaptureOutput];
}else{
NSLog(@"cantAddOutput");
}
// set portrait orientation
AVCaptureConnection *conn = [self.videoCaptureOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoOrientation:AVCaptureVideoOrientationPortrait];
例えば:
AVCaptureConnection *captureConnection = <a capture connection>;
if ([captureConnection isVideoOrientationSupported]) {
captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}
デフォルトはAVCaptureVideoOrientationLandscapeRight
のようです。
QA1744:AV Foundationを使用したビデオの方向の設定 も参照してください。
CIImageを使用する必要があり、バッファーからの方向が間違っている場合は、この修正を使用しました。
それと同じくらい簡単。ところで、3、1、6、8の数字はここから https://developer.Apple.com/reference/imageio/kcgimagepropertyorientation
そして、なぜ3,1,6,8が正しい組み合わせなのかを聞かないでください。私はそれを見つけるためにブルートフォース法を使用しました。理由を知っている場合は、コメントで説明してください...
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection
{
// common way to get CIImage
CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CFDictionaryRef attachments = CMCopyDictionaryOfAttachments(kCFAllocatorDefault, sampleBuffer, kCMAttachmentMode_ShouldPropagate);
CIImage *ciImage = [[CIImage alloc] initWithCVPixelBuffer:pixelBuffer
options:(__bridge NSDictionary *)attachments];
if (attachments) {
CFRelease(attachments);
}
// fixing the orientation of the CIImage
UIInterfaceOrientation curOrientation = [[UIApplication sharedApplication] statusBarOrientation];
if (curOrientation == UIInterfaceOrientationLandscapeLeft){
ciImage = [ciImage imageByApplyingOrientation:3];
} else if (curOrientation == UIInterfaceOrientationLandscapeRight){
ciImage = [ciImage imageByApplyingOrientation:1];
} else if (curOrientation == UIInterfaceOrientationPortrait){
ciImage = [ciImage imageByApplyingOrientation:6];
} else if (curOrientation == UIInterfaceOrientationPortraitUpsideDown){
ciImage = [ciImage imageByApplyingOrientation:8];
}
// ....
}
AVCaptureVideoPreviewLayer
の向きが正しい場合、画像をキャプチャする前に向きを簡単に設定できます。
AVCaptureStillImageOutput *stillImageOutput;
AVCaptureVideoPreviewLayer *previewLayer;
NSData *capturedImageData;
AVCaptureConnection *videoConnection = [stillImageOutput connectionWithMediaType:AVMediaTypeVideo];
if ([videoConnection isVideoOrientationSupported]) {
[videoConnection setVideoOrientation:previewLayer.connection.videoOrientation];
}
[stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler:^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
CFDictionaryRef exifAttachments =
CMGetAttachment(imageSampleBuffer, kCGImagePropertyExifDictionary, NULL);
if (exifAttachments) {
// Do something with the attachments.
}
// TODO need to manually add GPS data to the image captured
capturedImageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
UIImage *image = [UIImage imageWithData:capturedImageData];
}];
また、UIImageOrientation
とAVCaptureVideoOrientation
は異なることに注意することが重要です。 UIImageOrientationUp
は、地面に向かってボリュームコントロールdownを備えたランドスケープモードを指します(考えれば、not upボリュームコントロールをシャッターボタンとして使用します)。
したがって、空(AVCaptureVideoOrientationPortrait
)を指す電源ボタンを使用した縦向きは、実際にはUIImageOrientationLeft
です。
向きの問題は前面カメラにあるため、デバイスの種類を確認して新しい画像を生成すると、間違いなく向きの問題が解決されます。
-(void)capture:(void(^)(UIImage *))handler{
AVCaptureConnection *videoConnection = nil;
for (AVCaptureConnection *connection in self.stillImageOutput.connections)
{
for (AVCaptureInputPort *port in [connection inputPorts])
{
if ([[port mediaType] isEqual:AVMediaTypeVideo] )
{
videoConnection = connection;
break;
}
}
if (videoConnection) { break; }
}
[self.stillImageOutput captureStillImageAsynchronouslyFromConnection:videoConnection completionHandler: ^(CMSampleBufferRef imageSampleBuffer, NSError *error) {
if (imageSampleBuffer != NULL) {
NSData *imageData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageSampleBuffer];
**UIImage *capturedImage = [UIImage imageWithData:imageData];
if (self.captureDevice == [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo][1]) {
capturedImage = [[UIImage alloc] initWithCGImage:capturedImage.CGImage scale:1.0f orientation:UIImageOrientationLeftMirrored];
}**
handler(capturedImage);
}
}];
}
まず、ビデオ出力の構成で次の行を入力します。
guard let connection = videoOutput.connection(withMediaType:
AVFoundation.AVMediaTypeVideo) else { return }
guard connection.isVideoOrientationSupported else { return }
guard connection.isVideoMirroringSupported else { return }
connection.videoOrientation = .portrait
connection.isVideoMirrored = position == .front
次に、一般設定でランドスケープモードのチェックを外して、Portaitのみをサポートするようにターゲットを設定します。
( ソース )