Почему буфер изображения Yp Cb Cr полностью перемешан в iOS 13? - PullRequest
2 голосов
/ 30 сентября 2019

У меня есть приложение для компьютерного зрения, которое берет изображения с датчика в оттенках серого и обрабатывает их. Получение изображений для iOS написано на Obj-C, а обработка изображений выполняется на C ++ с использованием OpenCV. Поскольку мне нужны только данные яркости, я получаю изображение в двухплоскостном формате полного диапазона YUV (или Yp Cb Cr) 420 и просто назначаю данные буфера объекту OpenCV Mat (см. Код получения ниже). До сих пор это прекрасно работало, пока не вышла новая iOS 13 ... По какой-то причине в iOS 13 полученное изображение смещено, что приводит к диагональным полосам. Глядя на полученное изображение, я подозреваю, что это является следствием изменения порядка расположения компонентов Y Cb и Cr буфера или изменения шага буфера. Кто-нибудь знает, если iOS 13 вводит такого рода изменения и как я мог бы обновить свой код, чтобы избежать этого, желательно с обратной совместимостью?

Вот мой код получения изображения:

//capture config
- (void)initialize {
    AVCaptureDevice *frontCameraDevice;
    NSArray *devices = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *device in devices) {
        if (device.position == AVCaptureDevicePositionFront) {
            frontCameraDevice = device;
        }
    }
    if (frontCameraDevice == nil) {
        NSLog(@"Front camera device not found");
        return;
    }

    _session = [[AVCaptureSession alloc] init];
    _session.sessionPreset = AVCaptureSessionPreset640x480;

    NSError *error = nil;
    AVCaptureDeviceInput *input = [AVCaptureDeviceInput deviceInputWithDevice:frontCameraDevice error: &error];
    if (error != nil) {
        NSLog(@"Error getting front camera device input: %@", error);
    }
    if ([_session canAddInput:input]) {
        [_session addInput:input];
    } else {
        NSLog(@"Could not add front camera device input to session");
    }

    AVCaptureVideoDataOutput *videoOutput = [[AVCaptureVideoDataOutput alloc] init];
    // This is the default, but making it explicit
    videoOutput.alwaysDiscardsLateVideoFrames = YES;

    if ([videoOutput.availableVideoCVPixelFormatTypes containsObject:
                      [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange]]) {
        OSType format = kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
        videoOutput.videoSettings = [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedInt:format]
                                                                 forKey:(id)kCVPixelBufferPixelFormatTypeKey];
    } else {
        NSLog(@"YUV format not available");
    }

    [videoOutput setSampleBufferDelegate:self queue:dispatch_queue_create("extrapage.camera.capture.sample.buffer.delegate", DISPATCH_QUEUE_SERIAL)];
    if ([_session canAddOutput:videoOutput]) {
        [_session addOutput:videoOutput];
    } else {
        NSLog(@"Could not add video output to session");
    }

    AVCaptureConnection *captureConnection = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
    captureConnection.videoOrientation = AVCaptureVideoOrientationPortrait;
}

//acquisition code 
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {

    if (_listener != nil) {
        CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
        OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);

        NSAssert(format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange, @"Only YUV is supported");

        // The first plane / channel (at index 0) is the grayscale plane
        // See more infomation about the YUV format
        // http://en.wikipedia.org/wiki/YUV
        CVPixelBufferLockBaseAddress(pixelBuffer, 0);
        void *baseaddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

        CGFloat width = CVPixelBufferGetWidth(pixelBuffer);
        CGFloat height = CVPixelBufferGetHeight(pixelBuffer);

        cv::Mat frame(height, width, CV_8UC1, baseaddress, 0);

        [_listener onNewFrame:frame];

        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
    }
}

Ответы [ 2 ]

2 голосов
/ 09 октября 2019

Я нашел решение этой проблемы. Это была проблема шага строки: очевидно, в iOS 13 был изменен шаг строки 8-битного двухплоскостного буфера Yp Cb Cr 4: 2: 0. Возможно, для него всегда будет степень 2. Поэтому в некоторых случаях шаг строки больше не равен ширине. Это был случай для меня. Исправить это просто, просто получите шаг строки из информации буфера и передайте его конструктору OpenCV Mat, как показано ниже.

void *baseaddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);         
size_t width = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0);
size_t height = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0);
size_t bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);

cv::Mat frame(height, width, CV_8UC1, baseaddress, bytesPerRow);

Обратите внимание, что я также изменил способ получения ширины и высоты с помощьюразмеры плоскости вместо размеров буфера. Для плоскости Y она всегда должна быть одинаковой. Я не уверен, что это имеет значение.

Также будьте осторожны: после обновления Xcode для поддержки iOS 13 SDK мне пришлось удалить приложение с тестового устройства, потому что в противном случае Xcode продолжал использовать старую версиювместо недавно скомпилированного.

0 голосов
/ 09 октября 2019

это не ответ, но у нас похожая проблема. Я пробовал с другим разрешением, используемым при захвате фотографий, только одно разрешение (2592 x 1936) не работает, другие разрешения работают. Я думаю, что изменение разрешения на 1440x1920, например, может быть решением вашей проблемы.

...