Интеграция OpenCV на стороне iOS и взаимное преобразование между Mat и UIImage (с исходным кодом)

OpenCV – это очень мощная платформа для обработки графики, которая может работать в операционных системах Linux, Windows, Android и Mac OS. Она предоставляет очень богатые и мощные API-интерфейсы в таких областях, как автономное вождение, умный дом, распознавание лиц и обработка изображений. Это удобно. для обработки изображений и в основном может удовлетворить все потребности в обработке изображений. В последних проектах необходимо использовать opencv в качестве фреймворка для обработки изображений, а требования проекта к обработке изображений — это не наиболее часто используемые изображения с 8-битной глубиной цвета, а 16-битные изображения с глубиной цвета , поэтому в процессе разработки было наложено много подводных камней. , и в то же время использование opencv имеет более глубокое понимание, поэтому записываем обзор и надеемся предоставить некоторые идеи для небольших партнеров, изучающих OpenCV.

В этой статье кратко объясняется интеграция OpenCV и взаимное преобразование между Mat и UIImage, В следующей статье будут подробно описаны различные функции обработки, аналогичные Meitu Xiuxiu, с использованием OpenCV.

1. Интегрированный OpenCV

Есть два способа интегрировать OpenCV

1. Используйтеcocopods для интеграции и используйте его в Podfile

pod 'OpenCV', '~> 4.7.0'

Версия 4.7.0 opencv может быть интегрирована

2. Ручная интеграция

Вам нужно перейти на официальный сайт Opencv, чтобы загрузить фреймворк, используемый на стороне iOS.Адрес загрузки:
https://opencv.org/releases/
вставьте сюда описание изображения
, просто выберите пакет на стороне iOS для загрузки, а затем импортируйте загруженный папку в проект
вставьте сюда описание изображения
для обычного использования.

Взаимное преобразование двух Mat и UIImage

Mat — важный класс, представленный в OpenCV.Mat содержит много информации об изображении, например ширину и высоту изображения в пикселях, а также количество каналов.Обработка изображения с использованием фреймворка opencv на стороне iOS в основном необходимо преобразовать в объект Mat.может продолжаться нормально.

Примечание. В методе преобразования используется код C++, поэтому вам необходимо изменить .m на .mm в файле записи кода и в том месте, где используется файл, чтобы указать компилятору компилировать эти файлы в виде C++, иначе возникнет ошибка. сообщать.

Четкие комментарии специально помечены в коде, чтобы помочь друзьям понять. Если вы хотите разобраться, вы должны прочитать его. Просто скопируйте и вставьте его напрямую. Метод взаимного преобразования совместим с 8-битными и 16-битными изображениями RGB и RGBA, и вы можете использовать его с удовольствием. Для других специальных форматов, таких как изображения 16bpp, пожалуйста, следуйте рисунку тыквы и обрабатывайте их отдельно, идея и метод те же.

1. UIImage в мат

+(cv::Mat)cvMatFromUIImage:(UIImage *)image
{
    //获取图片的CGImageRef结构体
    CGImageRef imageRef = CGImageCreateCopy([image CGImage]);
    //获取图片尺寸
    CGSize size = CGSizeMake(CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
    //获取图片宽度
    CGFloat cols = size.width;
    //获取图高度
    CGFloat rows = size.height;
    //获取图片颜色空间,创建图片对应Mat对象,需要使用同样的颜色空间
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    
    //判断图片的通道位深及通道数 默认使用8位4通道格式
    int type = CV_16UC4;
    //获取bitmpa位数
    size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
    //获取通道位深
    size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
    //获取通道数
    size_t channels = bitsPerPixel/bitsPerComponent;
    if(channels == 3 || channels == 4){  // 因为quartz框架只支持处理带有alpha通道的数据,所以3通道的图片采取跟4通道的图片一样的处理方式,转化的时候alpha默认会赋最大值,归一化的数值位1.0,这样即使给图片增加了alpha通道,也并不会影响图片的展示
        if(bitsPerComponent == 8){
            //8位3通道 因为iOS端只支持
            type = CV_8UC4;
        }else if(bitsPerComponent == 16){
            //16位3通道
            type = CV_16UC4;
        }else{
            printf("图片格式不支持");
            abort();
        }
    }else{
        printf("图片格式不支持");
        abort();
    }
    
    //创建位图信息  根据通道位深及通道数判断使用的位图信息
    CGBitmapInfo bitmapInfo;
    
    if(bitsPerComponent == 8){
        if(channels == 3){
            bitmapInfo = kCGImageAlphaNone | kCGImageByteOrderDefault;
        }else  if(channels == 4){
            bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrderDefault;
        }else{
            printf("图片格式不支持");
            abort();
        }
    }else if(bitsPerComponent == 16){
        if(channels == 3){  //虽然是三通道,但是iOS端的CGBitmapContextCreate方法不支持16位3通道的创建,所以仍然作为4通道处理
            bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrder16Little;
        }else  if(channels == 4){
            bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrder16Little;
        }else{
            printf("图片格式不支持");
            abort();
        }
    }else{
        printf("图片格式不支持");
        abort();
    }


    //使用获取到的宽高创建mat对象CV_16UC4 为传入的矩阵类型
    cv::Mat cvMat(rows, cols, type); // 每通道8bit 共有4通道(RGB + Alpha通道 RGBA格式)
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // 数据源
                                                    cols,                       // 每行像素数
                                                    rows,                       // 列数(高度)
                                                    bitsPerComponent,                          // 每个通道bit数
                                                    cvMat.step[0],              // 每行字节数
                                                    colorSpace,                 // 颜色空间
                                                    bitmapInfo); // 位图信息(alpha通道信息,字节读取信息)
    //将图片绘制到上下文中mat对象中
    CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), image.CGImage);
    //释放imageRef对象
    CGImageRelease(imageRef);
    //释放颜色空间
    CGColorSpaceRelease(colorSpace);
    //释放上下文环境
    CGContextRelease(contextRef);
    return cvMat;
}

2. Мат для изображения

+(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
    //获取矩阵数据
    NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
    //判断矩阵使用的颜色空间
    CGColorSpaceRef colorSpace;
    if (cvMat.elemSize() == 1) {
        colorSpace = CGColorSpaceCreateDeviceGray();
    } else {
        colorSpace = CGColorSpaceCreateDeviceRGB();
    }
    //创建数据privder
    CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
    
    //获取bitmpa位数
    size_t bitsPerPixel = cvMat.elemSize()*8;
    //获取通道数
    size_t channels = cvMat.channels();
    //获取通道位深
    size_t bitsPerComponent = bitsPerPixel/channels;
    
    //创建位图信息  根据通道位深及通道数判断使用的位图信息
    CGBitmapInfo bitmapInfo;
    if(bitsPerComponent == 8){
        if(channels == 3){
            bitmapInfo = kCGImageAlphaNone | kCGImageByteOrderDefault;
        }else if(channels == 4){
            bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrderDefault;
        }else{
            printf("图片格式不支持");
            abort();
        }
    }else if(bitsPerComponent == 16){
        if(channels == 3){
            bitmapInfo = kCGImageAlphaNone | kCGImageByteOrder16Little;
        }else if(channels == 4){
            bitmapInfo = kCGImageAlphaPremultipliedLast | kCGImageByteOrder16Little;
        }else{
            printf("图片格式不支持");
            abort();
        }
    }else{
        printf("图片格式不支持");
        abort();
    }
    
   

    //根据矩阵及相关信息创建CGImageRef结构体
    CGImageRef imageRef = CGImageCreate(cvMat.cols, //矩阵宽度
                                        cvMat.rows, //矩阵列数
                                        bitsPerComponent,        //通道位深
                                        8 * cvMat.elemSize(),  //每个像素位深
                                        cvMat.step[0],  //每行占用字节数
                                        colorSpace,    //使用的颜色空间
                                        bitmapInfo,//通道排序、大小端读取顺序信息
                                        provider, //数据源
                                        NULL,   //解码数组 一般传null
                                        true, //是否抗锯齿
                                        kCGRenderingIntentDefault   //使用默认的渲染方式
                                        );
    // 通过cgImage转化出来UIImage对象
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    //释放imageRef
    CGImageRelease(imageRef);
    //释放provider
    CGDataProviderRelease(provider);
    //释放颜色空间
    CGColorSpaceRelease(colorSpace);
    return finalImage;
}


3. Небольшой инструмент, который использует Mat для печати подробной информации о картинках, что удобно для проверки данных.

//获取图片信息
+(void)readInfoWithImage:(UIImage*)inputImage{
    Mat inputMat = [CVTools matFromImage:inputImage];
    printf("图片宽度 = %d \n",inputMat.cols);
    printf("图片高度 = %d \n",inputMat.rows);
    printf("通道位深 = %zu \n",inputMat.elemSize()*8/inputMat.channels());
    printf("通道数 %d \n",inputMat.channels());
    printf("每个像素bit数 = %zu \n",inputMat.elemSize()*8);

    printf("每行元素的字节数 = %zu \n",inputMat.step[0]);
}

Три часто задаваемых вопроса

1. Запрашивать неподдерживаемые комбинации параметров

Поскольку фреймворк quarzt 2D имеет строгие правила обработки изображений, существуют четкие правила для комбинации альфа-канала и порядка чтения в Bitmapinfo, и ошибка выглядит следующим образом.
вставьте сюда описание изображения

Решение
Первый способ - проверить комбинации, разрешенные кварцем, через официальный сайт Скриншот официального сайта выглядит следующим образом:
Пожалуйста, добавьте описание изображения

Второй метод заключается в установке переменных среды в соответствии с подсказками и печати поддерживаемых комбинаций в окне журнала.Метод настройки следующий:
вставьте сюда описание изображения
вставьте сюда описание изображения
добавьте «CGBITMAP_CONTEXT_LOG_ERRORS», чтобы распечатать информацию журнала ошибок растровой среды, а затем запустите окно журнала, чтобы выводим следующее: Как
вставьте сюда описание изображения
видите, для 8Bit Для картинок с разрядностью канала 16Bit кварц поддерживает только те, что с альфа-каналами, и метод чтения каналов тоже четко оговорен, и достаточно принять соответствующую конфигурацию по своему формат изображения.
Поскольку платформа кварца поддерживает обработку данных только с альфа-каналом, 3-канальные изображения обрабатываются так же, как и 4-канальные изображения.При преобразовании альфа-каналу по умолчанию будет присвоено максимальное значение, а нормализованное значение равно 1,0, поэтому даже если задан Альфа-канал добавляется к картинке, и это не повлияет на отображение картинки

Это место очень сложно.Для 16-битного изображения, даже если вы знаете, что изображение содержит альфа-канал, а позиция альфа-канала находится в конце, вы не можете использовать информацию о канале изображения kCGImageAlphaLast, но используйте перечисление kCGImageAlphaPremultipliedLast, чтобы ограничить его.Однако, если это 8-битное изображение, такого ограничения нет, и порядок чтения байтов должен быть дополнительно указан, чтобы использовать 16-битный прямой порядок байтов для чтения kCGImageByteOrder16Little.Друзья, которые делают 16- битовая обработка изображений должна обратить на это внимание, глубокая яма.

2. При импорте файлов заголовков обязательно поместите файлы заголовков, используемые oencv, перед всеми ссылками на файлы OC, иначе возникнут конфликты переопределения функций.

Взяв в качестве примера файлы в тестовом проекте, метод ссылки на заголовочный файл:

#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#include <math.h>
#include <iostream>
using namespace cv;
using namespace std;

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

Используемые пространства имен также требуют дополнительных объявлений.

Добро пожаловать для обмена идеями, жду вас.

Supongo que te gusta

Origin blog.csdn.net/mumubumaopao/article/details/130774835
Recomendado
Clasificación