中山网站建设优化,新手wordpress添加注册表,深圳营销网站建设,东方城乡与住房建设部网站OpenCV是一个非常强大的图形处理框架#xff0c;可以运行在Linux、Windows、Android和Mac OS操作系统上#xff0c;在自动驾驶、智能家居、人脸识别、图片处理等方面提供了非常丰富且功能强大的api#xff0c;在图片处理方便#xff0c;基本上可以满足对图片处理的所有需求…OpenCV是一个非常强大的图形处理框架可以运行在Linux、Windows、Android和Mac OS操作系统上在自动驾驶、智能家居、人脸识别、图片处理等方面提供了非常丰富且功能强大的api在图片处理方便基本上可以满足对图片处理的所有需求。近期项目中有使用opencv作为图片处理框架的需求,而且项目对图片处理的需求并不是最常用的8bit色深图片而是16bit色深所以在开发的过程中踩了很多坑同时也对opencv的使用有了更深的理解特此记录回顾也希望能给正在研究OpenCV的小伙伴提供一点思路。
本文简单讲解OpenCV的集成及Mat和UIImage互相转化下一篇文章会详细记录使用OpenCV对图片进行类似于美图秀秀的各种处理功能。
一 集成OpenCV
OpenCV的集成有两种方式
1.使用cocoapods进行集成在Podfile文件中使用
pod OpenCV, ~ 4.7.0即可集成opencv的4.7.0版本
2.手动集成
需要去Opencv官网下载iOS端使用的框架下载地址 https://opencv.org/releases/ 选择iOS端的包下载就好然后将下载下来的文件夹整个导入项目中 即可正常使用
二 Mat和UIImage的互相转化
Mat是OpenCV中提供的一个重要的类Mat中包含了图片的很多信息比如图片的像素宽高、通道数量iOS端使用opencv框架对图片的处理基本上也都需要转化为Mat对象之后才可以正常进行。
注意转化方法使用c代码所以在代码的编写文件以及使用该文件的地方都需要将.m改为.mm以告诉编译器以c的形式来编译这些文件否则会报错。
代码里特意标明了清晰的注释帮助小伙伴理解。想深入梳理的务必阅读伸手党直接复制粘贴就好互相转化的方法对8位及16位的RGB及RGBA图片都做了兼容可以愉快使用。其他特殊格式如16bpp的图片请照葫芦画瓢单独处理思路和方法是一样的。
1.UIImage转Mat
(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.Mat转Image
(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();}//创建数据privderCGDataProviderRef 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, //解码数组 一般传nulltrue, //是否抗锯齿kCGRenderingIntentDefault //使用默认的渲染方式);// 通过cgImage转化出来UIImage对象UIImage *finalImage [UIImage imageWithCGImage:imageRef];//释放imageRefCGImageRelease(imageRef);//释放providerCGDataProviderRelease(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内的alpha通道和读取顺序组合有着明确的规则报错如下
解决方法 第一种方式是通过官网查阅quartz允许的组合搭配官网截图如下
第二种方法是根据提示去设置环境变量在Log窗口打印支持的组合搭配设置方式如下 增加“CGBITMAP_CONTEXT_LOG_ERRORS”位图环境错误log信息的打印然后再运行Log窗口输出如下: 可以看到对于8Bit和16Bit通道位深的图片quartz只支持带有alpha通道的通道的读取方式也有明确规定根据自己的图片格式采取相应的配置就可以了。 因为quartz框架只支持处理带有alpha通道的数据所以3通道的图片采取跟4通道的图片一样的处理方式转化的时候alpha默认会赋最大值归一化的数值位1.0这样即使给图片增加了alpha通道也并不会影响图片的展示
这个地方很坑以16位图片来说即使明知图片是含有alpha通道的而且alpha通道的位置在最后也并不能使用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使用的命名空间也需要额外声明。
有想法欢迎交流等你。