Как получить компонент Y из CMSamleBuffer привел к AVCaptureceSess?

StackOverflow https://stackoverflow.com/questions/4085474

  •  28-09-2019
  •  | 
  •  

Вопрос

Привет, я пытаюсь получить доступ к необработанным данным с камеры iPhone с помощью AVCapturesessionsess. Я следую руководству, предоставленному Apple (Ссылка здесь).

Необработанные данные из обратногоbuffer находится в формате YUV (я корремся здесь о формате RAW Video Frame ??), как напрямую получить данные для компонента Y из необработанных данных, хранящихся в образецбуфере.

Это было полезно?

Решение

При настройке AVCaptureVideodataUTPUTT, который возвращает рамы RAW Camera, вы можете установить формат кадров, используя код, как следующее:

[videoOutput setVideoSettings:[NSDictionary dictionaryWithObject:[NSNumber numberWithInt:kCVPixelFormatType_32BGRA] forKey:(id)kCVPixelBufferPixelFormatTypeKey]];

В этом случае указан формат BGRA Pixel (я использовал это для соответствия формату цвета для текстуры OpenGL ES). Каждый пиксель в этом формате имеет один байт для синего, зеленого, красного и альфа, в этом порядке. Собираясь с этим позволяет легко вытащить цветные компоненты, но вы пожертвоваете небольшим характеристиком, нуждающийся в том, чтобы сделать преобразование из камеры-конного цвета YUV Colorspace.

Другие поддерживаемые цвета пространства kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange и kCVPixelFormatType_420YpCbCr8BiPlanarFullRange на новее устройства и kCVPixelFormatType_422YpCbCr8 на iPhone 3G. То VideoRange или FullRange Суффикс просто указывает, возвращаются ли байты от 16 до 235 на Y и 16 - 240 для ультрафиолетового или полного 0 - 255 для каждого компонента.

Я верю, что цвета по умолчанию, используемое экземпляром AvcaptureVideodatautuTutaTatueOutputtate, - это плоский пространственный пространственный пространство YUV 4: 2: 0 (за исключением iPhone 3G, где он переплетелен 4: 2: 2). Это означает, что существуют две плоскости данных изображения, содержащиеся в видеокадре, с самолетом Y сначала. Для каждого пикселя в вашем полученном изображении есть один байт для значения Y в этом пикселе.

Вы бы получили эти данные RAW Y, реализовав что-то подобное в вашем делегате обратного вызова:

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection
{
    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);

    unsigned char *rawPixelBase = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);

    // Do something with the raw pixels here

    CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
}

Затем вы можете понять местоположение в данных кадра для каждой координаты X, Y на изображении и вытяните байт, который соответствует компоненту Y при этой координате.

Образец Findmyicone Apple от WWDC 2010. (Доступно наряду с видеороликами) показывает, как обрабатывать RAW BGRA Data из каждого кадра. Я также создал пример приложения, которое вы можете скачать код для здесь, что выступает Отслеживание объектов на основе цвета Использование живого видео с камеры iPhone. Оба показывают, как обработать RAW Pixel DATA, но ни одна из этих работ в Velaspace YUV.

Другие советы

В дополнение к ответу Брэда и ваш собственный код, вы хотите рассмотреть следующее:

Так как ваше изображение имеет две отдельные самолеты, функция Cvpixelbuffergetbasexdress. не вернет базовый адрес самолета, а скорее базовый адрес дополнительной структуры данных. Это, вероятно, связано с текущей реализацией, которую вы получаете достаточно близко к первому самолету, чтобы вы могли видеть изображение. Но это причина, по которой она смещена и имеет мусор наверху слева. Правильный способ получения первого самолета:

unsigned char *rowBase = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);

Ряд на изображении может быть длиннее ширины изображения (из-за округления). Вот почему существуют отдельные функции для получения ширины и количество байтов на ряд. У вас нет этой проблемы в данный момент. Но это может измениться с следующей версией iOS. Таким образом, ваш код должен быть:

int bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
int bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
int bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0);
int size = bufferHeight * bytesPerRow ;

unsigned char *pixel = (unsigned char*)malloc(size);

unsigned char *rowBase = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0);
memcpy (pixel, rowBase, size);

Пожалуйста, обратите внимание, что ваш код будет несчастной неудачу на iPhone 3G.

Если вам нужен только канал яркости, рекомендую к использованию формата BGRA, так как он поставляется с накладным расходом преобразования. Apple предлагает использовать BGRA, если вы делаете рендеринг вещей, но вам это не нужно для извлечения информации о яркости. Как уже упоминалось Брэд, самый эффективный формат - это родной формат YUV.

Тем не менее, извлечение правых байтов из буфера образца немного сложно, особенно в отношении iPhone 3G с межливым форматом YUV 422. Итак, вот мой код, который отлично работает с iPhone 3G, 3GS, iPod Touch 4 и iPhone 4S.

#pragma mark -
#pragma mark AVCaptureVideoDataOutputSampleBufferDelegate Methods
#if !(TARGET_IPHONE_SIMULATOR)
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection;
{
    // get image buffer reference
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    // extract needed informations from image buffer
    CVPixelBufferLockBaseAddress(imageBuffer, 0);
    size_t bufferSize = CVPixelBufferGetDataSize(imageBuffer);
    void *baseAddress = CVPixelBufferGetBaseAddress(imageBuffer);
    CGSize resolution = CGSizeMake(CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer));

    // variables for grayscaleBuffer 
    void *grayscaleBuffer = 0;
    size_t grayscaleBufferSize = 0;

    // the pixelFormat differs between iPhone 3G and later models
    OSType pixelFormat = CVPixelBufferGetPixelFormatType(imageBuffer);

    if (pixelFormat == '2vuy') { // iPhone 3G
        // kCVPixelFormatType_422YpCbCr8     = '2vuy',    
        /* Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1 */

        // copy every second byte (luminance bytes form Y-channel) to new buffer
        grayscaleBufferSize = bufferSize/2;
        grayscaleBuffer = malloc(grayscaleBufferSize);
        if (grayscaleBuffer == NULL) {
            NSLog(@"ERROR in %@:%@:%d: couldn't allocate memory for grayscaleBuffer!", NSStringFromClass([self class]), NSStringFromSelector(_cmd), __LINE__);
            return nil; }
        memset(grayscaleBuffer, 0, grayscaleBufferSize);
        void *sourceMemPos = baseAddress + 1;
        void *destinationMemPos = grayscaleBuffer;
        void *destinationEnd = grayscaleBuffer + grayscaleBufferSize;
        while (destinationMemPos <= destinationEnd) {
            memcpy(destinationMemPos, sourceMemPos, 1);
            destinationMemPos += 1;
            sourceMemPos += 2;
        }       
    }

    if (pixelFormat == '420v' || pixelFormat == '420f') {
        // kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange = '420v', 
        // kCVPixelFormatType_420YpCbCr8BiPlanarFullRange  = '420f',
        // Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]).  
        // Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]).
        // baseAddress points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
        // i.e.: Y-channel in this format is in the first third of the buffer!
        int bytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(imageBuffer, 0);
        baseAddress = CVPixelBufferGetBaseAddressOfPlane(imageBuffer,0);
        grayscaleBufferSize = resolution.height * bytesPerRow ;
        grayscaleBuffer = malloc(grayscaleBufferSize);
        if (grayscaleBuffer == NULL) {
            NSLog(@"ERROR in %@:%@:%d: couldn't allocate memory for grayscaleBuffer!", NSStringFromClass([self class]), NSStringFromSelector(_cmd), __LINE__);
            return nil; }
        memset(grayscaleBuffer, 0, grayscaleBufferSize);
        memcpy (grayscaleBuffer, baseAddress, grayscaleBufferSize); 
    }

    // do whatever you want with the grayscale buffer
    ...

    // clean-up
    free(grayscaleBuffer);
}
#endif

Это просто кульминация тяжелой работы всех остальных, выше и других потоках, преобразованных в SWIFT 3 для всех, кто находит его полезным.

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
    if let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) {
        CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)

        let pixelFormatType = CVPixelBufferGetPixelFormatType(pixelBuffer)
        if pixelFormatType == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
           || pixelFormatType == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange {

            let bufferHeight = CVPixelBufferGetHeight(pixelBuffer)
            let bufferWidth = CVPixelBufferGetWidth(pixelBuffer)

            let lumaBytesPerRow = CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0)
            let size = bufferHeight * lumaBytesPerRow
            let lumaBaseAddress = CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0)
            let lumaByteBuffer = unsafeBitCast(lumaBaseAddress, to:UnsafeMutablePointer<UInt8>.self)

            let releaseDataCallback: CGDataProviderReleaseDataCallback = { (info: UnsafeMutableRawPointer?, data: UnsafeRawPointer, size: Int) -> () in
                // https://developer.apple.com/reference/coregraphics/cgdataproviderreleasedatacallback
                // N.B. 'CGDataProviderRelease' is unavailable: Core Foundation objects are automatically memory managed
                return
            }

            if let dataProvider = CGDataProvider(dataInfo: nil, data: lumaByteBuffer, size: size, releaseData: releaseDataCallback) {
                let colorSpace = CGColorSpaceCreateDeviceGray()
                let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.noneSkipFirst.rawValue)

                let cgImage = CGImage(width: bufferWidth, height: bufferHeight, bitsPerComponent: 8, bitsPerPixel: 8, bytesPerRow: lumaBytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo, provider: dataProvider, decode: nil, shouldInterpolate: false, intent: CGColorRenderingIntent.defaultIntent)

                let greyscaleImage = UIImage(cgImage: cgImage!)
                // do what you want with the greyscale image.
            }
        }

        CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags.readOnly)
    }
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top