iOS может эффективно реализовать преобразование данных RGB/YUV без использования libyuv |
iOS может эффективно реализовать преобразование данных RGB/YUV без использования libyuv |

Разработчики, занимающиеся обработкой аудио, видео или изображений, обычно более или менее контактируют с libyuv[1] Эта библиотека с открытым исходным кодом, с которой мы занимаемся разработкой аудио и видео. YUVRGB Он часто используется при преобразовании, повороте и масштабировании данных в других форматах. либюв на основе C Реализацию языка можно найти в Windows、Linux、Mac OS、Android、iOS Его можно использовать на нескольких платформах, и он оптимизирован для ускорения набора команд, что приводит к повышению производительности.

Фактически, Apple также предоставляет набор инструментов под названием vImage родной API 来支持高производительностьобработка данных изображений。vImage да Accelerate часть библиотеки, используя традиционные C Реализация языка, ориентированная на высокоуровневую обработку изображений, многие интерфейсы требуют ручного управления памятью, мы основе vImage Высокая производительность также может быть достигнута RGB и YUV Преобразование данных.

1. Конвертируйте BGRA в ARGB

Следующий пример кода реализует iOS BGRA Конвертировать ARGB,Также напечатано vImage и libyuv Время обработки, интерфейс и libyuv Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.

Если вам нужно изменить формат Конвертировать на RGBA Подождите других последовательностей и отрегулируйте permuteMap Просто следуйте приказу.

Язык кода:javascript
копировать
+ (void)testBGRAToARGB:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer {
    CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    
    NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;
    
    // использовать vImage Данные реализации Конвертировать:
    [self.class BGRAToARGB:CVPixelBufferGetBaseAddress(srcPixelBuffer) src_stride:CVPixelBufferGetBytesPerRow(srcPixelBuffer) dst:CVPixelBufferGetBaseAddress(dstPixelBuffer) dst_stride:CVPixelBufferGetBytesPerRow(dstPixelBuffer) width:CVPixelBufferGetHeight(srcPixelBuffer) height:CVPixelBufferGetWidth(srcPixelBuffer)];
    
    NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;

    // использовать libyuv Данные реализации Конвертировать:
    RGBAToARGB(CVPixelBufferGetBaseAddress(srcPixelBuffer),CVPixelBufferGetBytesPerRow(srcPixelBuffer),CVPixelBufferGetBaseAddress(dstPixelBuffer),CVPixelBufferGetBytesPerRow(dstPixelBuffer),CVPixelBufferGetHeight(srcPixelBuffer),CVPixelBufferGetWidth(srcPixelBuffer));

    NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
    NSLog(@"BGRAToARGB duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);
    
    CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}


+ (int)BGRAToARGB:(const uint8_t *)src src_stride:(int)src_stride
              dst:(uint8_t *)dst dst_stride:(int)dst_stride width:(int)width height:(int)height {
    if (width == 0 || height == 0 || !src || src_stride == 0 || dst_stride == 0 || !dst) {
        return -1;
    }
    
    vImage_Buffer srcBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = src_stride,
       .data = src
    };
    
    vImage_Buffer dstBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = dst_stride,
       .data = dst
    };

    const uint8_t permuteMap[4] = {3, 2, 1, 0}; // transfer BGRA to ARGB
    return vImagePermuteChannels_ARGB8888(&srcBufferInfo, &dstBufferInfo, permuteMap, kvImageNoFlags);
}

Ниже мы провели несколько тестов и получили трудоемкие результаты:

Язык кода:javascript
копировать
BGRAToARGB duration: vImage=0.11, yuv=0.13
BGRAToARGB duration: vImage=0.10, yuv=0.10
BGRAToARGB duration: vImage=0.13, yuv=0.09
BGRAToARGB duration: vImage=0.09, yuv=0.10
BGRAToARGB duration: vImage=0.12, yuv=0.13
BGRAToARGB duration: vImage=0.10, yuv=0.10
BGRAToARGB duration: vImage=0.09, yuv=0.08

Анализ ключевых параметров в примере кода:

  • permuteMap Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.
  • vImagePermuteChannels_ARGB8888 Параметр исходного формата и целевой формат соответствуют vImage_Buffer и permuteMap,каждый vImage_Buffer настраивать Ширина, высота, количество байтов строки, указатель адреса.
  • CVPixelBuffer Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress Разблокировано.

2. Конвертируйте YUV(NV12) в BGRA.

существовать iOS генерал-лейтенант NV12 Отформатированный CVPixelBuffer сохранить как UIImage Если есть такие требования, как извлечение видеокадров, сначала необходимо NV12 Конвертировать BGRA。Пример кода ниже реализует и сравнивает vImage и libyuv производительность,интерфейси libyuv Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.

Если исходные данные I420,Метод Конвертировать можно изменить на vImageConvert_420Yp8_Cb8_Cr8ToARGB8888

Язык кода:javascript
копировать
+ (void)testNV12ToBGRA:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer {
    vImage_Flags flags = kvImageNoFlags;
    static vImage_YpCbCrToARGB info;
    static dispatch_once_t onceTokenYUVToBGRA;
    dispatch_once(&onceTokenYUVToBGRA, ^{
        // vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){16, 128, 235, 240, 255, 0, 255, 1};
        vImage_YpCbCrPixelRange pixelRange = {0, 128, 255, 255, 255, 1, 255, 0}; //fullrange - fullrange
        
        vImageConvert_YpCbCrToARGB_GenerateConversion(kvImage_YpCbCrToARGBMatrix_ITU_R_709_2, &pixelRange,
                                                      &info, kvImage420Yp8_CbCr8,
                                                      kvImageARGB8888, flags);
    });
    
    CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    
    NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;

    // использовать vImage Данные реализации Конвертировать:
    [self.class NV12ToBGRA:CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 0) src_stride_y:CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 0) src_uv:CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 1) src_stride_uv:CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 1) dst:CVPixelBufferGetBaseAddress(dstPixelBuffer) dst_stride:CVPixelBufferGetBytesPerRow(dstPixelBuffer) width:CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0) height:CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0) info:info];
    
    NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;

    // использовать libyuv Данные реализации Конвертировать:
    NV12ToARGB(CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 0), CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 0), CVPixelBufferGetBaseAddressOfPlane(srcPixelBuffer, 1), CVPixelBufferGetBytesPerRowOfPlane(srcPixelBuffer, 1), CVPixelBufferGetBaseAddress(dstPixelBuffer), CVPixelBufferGetBytesPerRow(dstPixelBuffer), CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0), CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0));

    NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
    NSLog(@"NV12ToBGRA duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);
    
    CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}

+ (int)NV12ToBGRA:(uint8_t *)src_y src_stride_y:(int)src_stride_y src_uv:(uint8_t*)src_uv src_stride_uv:(int)src_stride_uv dst:(const uint8_t *)dst dst_stride:(int)dst_stride
            width:(int)width height:(int)height info:(vImage_YpCbCrToARGB)info {
    if (!src_y || src_stride_y == 0 || !src_uv || src_stride_uv == 0 || !dst || dst_stride == 0 || width == 0 || height == 0) {
        return -1;
    }
    
    vImage_Flags flags = kvImageNoFlags;
    
    vImage_Buffer srcYBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = src_stride_y,
       .data = src_y};
    
    vImage_Buffer srcUVBufferInfo = {
       .width = (width  + 1) >> 1,
       .height = (height + 1) >> 1,
       .rowBytes = src_stride_uv,
       .data = src_uv};
    
    vImage_Buffer dstBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = dst_stride,
       .data = dst};
    
    const uint8_t permuteMap[4] = {3, 2, 1, 0}; // transfer ARGB to BGRA
    return vImageConvert_420Yp8_CbCr8ToARGB8888(&srcYBufferInfo, &srcUVBufferInfo, &dstBufferInfo, &info, 
                                                permuteMap, 255, flags);
}

Ниже мы провели несколько тестов и получили трудоемкие результаты:

Язык кода:javascript
копировать
NV12ToBGRA duration: vImage=0.16, yuv=0.13
NV12ToBGRA duration: vImage=0.13, yuv=0.12
NV12ToBGRA duration: vImage=0.14, yuv=0.13
NV12ToBGRA duration: vImage=0.13, yuv=0.13
NV12ToBGRA duration: vImage=0.15, yuv=0.11
NV12ToBGRA duration: vImage=0.12, yuv=0.11
NV12ToBGRA duration: vImage=0.12, yuv=0.11
NV12ToBGRA duration: vImage=0.14, yuv=0.11
NV12ToBGRA duration: vImage=0.13, yuv=0.10

Анализ ключевых параметров в примере кода:

  • vImageConvert_YpCbCrToARGB_GenerateConversion 需要настраивать 601709,чтобы и FullRangeVideoRangekvImage420Yp8_CbCr8 представлять NV12
  • permuteMap Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.
  • vImageConvert_420Yp8_CbCr8ToARGB8888 Функция по умолчанию Конвертировать: ARGB,Но цель Конвертироватьдля BGRA,所к需调整настраивать permuteMap
  • CVPixelBuffer Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress Разблокировано.
  • srcUVBufferInfo Иглы ширины и высоты будут округлены до нечетных чисел и выровнены. libyuv

3. Конвертировать BGRA в YUV(NV12)

когда UIImage Конвертироватьдля NV12 Отформатированный Когда используется CVPixelBuffer, например, при смешивании изображений и видео, изображение Конвертировать должно быть NV12 Чтобы закодировать, вам нужно сначала BGRA Конвертировать NV12。Пример кода ниже реализует и сравнивает vImage и libyuv производительность,интерфейси libyuv Будьте последовательны, и повторное тестирование показало, что производительность в основном такая же.

Если вам нужно Конвертировать для I420,Метод Конвертировать можно изменить на vImageConvert_ARGB8888To420Yp8_Cb8_Cr8

Язык кода:javascript
копировать
+ (void)testBGRAToNV12:(CVPixelBufferRef)srcPixelBuffer dstPixelBuffer:(CVPixelBufferRef)dstPixelBuffer{
    vImage_Flags flags = kvImageNoFlags;
    static vImage_ARGBToYpCbCr info;
    static dispatch_once_t onceTokenBGRAToNV12;
    dispatch_once(&onceTokenBGRAToNV12, ^{
        // vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){16, 128, 235, 240, 255, 0, 255, 1};
        vImage_YpCbCrPixelRange pixelRange = (vImage_YpCbCrPixelRange){0, 128, 255, 255, 255, 1, 255, 0};
        vImageConvert_ARGBToYpCbCr_GenerateConversion(
                                                      kvImage_ARGBToYpCbCrMatrix_ITU_R_709_2,
                                                      &pixelRange,
                                                      &info,
                                                      kvImageARGB8888,
                                                      kvImage420Yp8_CbCr8,
                                                      0);
    });
    
    CVPixelBufferLockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferLockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    
    NSTimeInterval time1 = [[NSDate date] timeIntervalSince1970] * 1000;

    // использовать vImage Данные реализации Конвертировать:
    [self.class BGRAToNV12:CVPixelBufferGetBaseAddress(srcPixelBuffer) src_stride:CVPixelBufferGetBytesPerRow(srcPixelBuffer) dst_y:CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0) dst_stride_y:CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 0) dst_uv:CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1) dst_stride_uv:CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 1) width:CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0) height:CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0) info:info];

    NSTimeInterval time2 = [[NSDate date] timeIntervalSince1970] * 1000;

    // использовать libyuv Данные реализации Конвертировать:
    ARGBToNV12(CVPixelBufferGetBaseAddress(srcPixelBuffer), CVPixelBufferGetBytesPerRow(srcPixelBuffer), CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 0), CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 0), CVPixelBufferGetBaseAddressOfPlane(dstPixelBuffer, 1), CVPixelBufferGetBytesPerRowOfPlane(dstPixelBuffer, 1), CVPixelBufferGetWidthOfPlane(srcPixelBuffer, 0), CVPixelBufferGetHeightOfPlane(srcPixelBuffer, 0));

    NSTimeInterval time3 = [[NSDate date] timeIntervalSince1970] * 1000;
    NSLog(@"BGRAToNV12 duration: vImage=%.2f, yuv=%.2f", time2 - time1, time3 - time2);

    CVPixelBufferUnlockBaseAddress(dstPixelBuffer, kCVPixelBufferLock_ReadOnly);
    CVPixelBufferUnlockBaseAddress(srcPixelBuffer, kCVPixelBufferLock_ReadOnly);
}

+ (int)BGRAToNV12:(const uint8_t*)src src_stride:(int)src_stride
            dst_y:(uint8_t*)dst_y dst_stride_y:(int)dst_stride_y dst_uv:(uint8_t*)dst_uv dst_stride_uv:(int)dst_stride_uv width:(int)width height:(int)height info:(vImage_ARGBToYpCbCr)info{
    if(!src || src_stride == 0 || !dst_y || dst_stride_y == 0 || !dst_uv || dst_stride_uv == 0 || width == 0 || height == 0){
        return -1;
    }
    
    vImage_Buffer srcBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = src_stride,
       .data = src};
    
    vImage_Buffer yBufferInfo = {
       .width = width,
       .height = height,
       .rowBytes = dst_stride_y,
       .data = dst_y};
    
    vImage_Buffer uvBufferInfo = {
       .width = (width  + 1) >> 1,
       .height = (height + 1) >> 1,
       .rowBytes = dst_stride_uv,
       .data = dst_uv};

    const uint8_t permuteMap[4] = { 3, 2, 1, 0}; // transfer ARGB to BGRA
    return vImageConvert_ARGB8888To420Yp8_CbCr8(
       &srcBufferInfo,
       &yBufferInfo,
       &uvBufferInfo,
       &info,
       permuteMap,
       kvImageDoNotTile);
}

Ниже мы провели несколько тестов и получили трудоемкие результаты:

Язык кода:javascript
копировать
BGRAToNV12 duration: vImage=0.16, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.19
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.16, yuv=0.19
BGRAToNV12 duration: vImage=0.10, yuv=0.12
BGRAToNV12 duration: vImage=0.10, yuv=0.12

Анализ ключевых параметров в примере кода:

  • vImageConvert_ARGBToYpCbCr_GenerateConversion 需要настраивать 601709,чтобы и FullRangeVideoRangekvImage420Yp8_CbCr8 представлять NV12
  • permuteMap Четыре значения параметра представляют четыре канала исходного формата. Индексный порядок четырех параметров представляет собой порядок существования четырех каналов в исходном формате в целевом формате.
  • vImageConvert_ARGB8888To420Yp8_CbCr8 Функция по умолчанию Конвертировать: ARGB,Но цель Конвертироватьдля BGRA,所к需调整настраивать permuteMap
  • CVPixelBuffer Чтобы получить адрес, вам нужно сначала позвонить по нему. CVPixelBufferLockBaseAddress Блокировка, вызов после завершения использования CVPixelBufferUnlockBaseAddress Разблокировано.
  • uvBufferInfo Иглы ширины и высоты будут округлены до нечетных чисел и выровнены. libyuv

Ссылки

[1]

libyuv: https://chromium.googlesource.com/libyuv/libyuv

boy illustration
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода