OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)
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>
使用的命名空间也需要额外声明。
有想法欢迎交流,等你。
相关文章:

OpenCV在iOS端的集成及Mat和UIImage互相转化(附源码)
OpenCV是一个非常强大的图形处理框架,可以运行在Linux、Windows、Android和Mac OS操作系统上,在自动驾驶、智能家居、人脸识别、图片处理等方面提供了非常丰富且功能强大的api,在图片处理方便,基本上可以满足对图片处理的所有需求…...

5月跳槽会有风险,不跳也会有?
今天讲讲跳槽。 说实话跳槽是为了寻求更好的发展,但在跳槽前我们也不能确定下家就是更好的归宿,这就更加需要我们审慎地去对待,不能盲目跳槽。 其次,我们离职和跳槽,其中的原因很大一部分是目前薪资不符合预期。 那…...

【小白版】最简单的 goland package 教程包括自定义包的使用
一、Hello World 最简单的教程,就需要从最简单的事情开始说起: mkdir myappcd myappgo mod init myapp // myapp是主项目名 这行命令将生成一个go.mod文件,这个文件会记录所有的包的依赖关系,一个空的go.mod只有项目名称和go版本…...

IMX6ULL的I2C驱动详细分析
IMX6ULL的I2C驱动详细分析 文章目录 IMX6ULL的I2C驱动详细分析i2c_imx_driver 的平台驱动注册i2c_imx_probe注册函数i2c_imx_algoI2C算法结构体i2c_imx_start开始I2Ci2c_imx_stop停止I2Ci2c_imx_isr中断服务函数i2c_imx_dma_writeDMA 进行写操作的 I2C 传输2c_imx_dma_readi2c_…...
日志迁移到 logback
目标 本次迁移的目标就是用 slf4jlogback 的日志框架来取代目前的 slf4jlog4j 如何迁移 基于 slf4j 的无痛迁移 项目本身是采用的 slf4jlog4j 日志架构,所以迁移到 logback 基本无痛 修改依赖 原本是 slf4jlog4,依赖如下 <dependency><gro…...

开源字节 CRM 系统
开源字节CRM是一款SaaS模式的客户关系管理软件,基于钉钉平台进行研发,以客户管理为核心,包含客户管理、销售全流程管理,合同订单、工单管理、移动审批、数据分析六大模块。 旨在助力企业销售全流程精细化、数字化管理,…...

七、Spring Cloud Alibaba-Sentinel
一、引言 1、了解服务可用性问题,服务挂掉原因 缓存击穿、单点故障、流量激增、线程池爆满、CPU飙升、DB超时、缺乏容错机制或保护机制、负载不均、服务雪崩、异常没处理等。 服务雪崩效应:因服务提供者的不可用导致服务调用者的不可用,并将…...

机器学习与深度学习——通过knn算法分类鸢尾花数据集iris求出错误率并进行可视化
什么是knn算法? KNN算法是一种基于实例的机器学习算法,其全称为K-最近邻算法(K-Nearest Neighbors Algorithm)。它是一种简单但非常有效的分类和回归算法。 该算法的基本思想是:对于一个新的输入样本,通过…...

【MySQL】MySQL基础知识详解
文章目录 1. MySQL概述1.1 数据库相关概念1.1.1 数据库、数据库管理系统与SQL1.1.2 关系型数据库与数据模型 1.2 MySQL数据库1.2.1 MySQL的安装与配置1.2.2 MySQL服务的启动与停止1.2.3 连接MySQL服务端 2. SQL2.1 SQL简介2.2 DDL2.2.1 数据库操作2.2.2 表操作2.2.2.1 创建表2.…...

RabbitMQ日常使用小结
一、使用场景 削峰、解耦、异步。 基于AMQP(高级消息队列协议)协议来统一数据交互,通过channel(网络信道)传递信息。erlang语言开发,并发量12000,支持持久化,稳定性好,集群不支持动态扩展。 RabbitMQ的基本概念 二、组成及工作流…...

博物馆文物馆藏环境空气质量无线监控系统方案
博物馆文物馆藏环境空气质量无线监控系统方案 博物馆无线环境测控系统 博物馆恒温恒湿消毒净化系统 现代化博物馆空气质量一体化3D可视化管控平台 博物馆温湿度在线监控系统 博物馆光照在线监控系统 博物馆二氧化碳在线监控系统 博物馆在线监控系统 博物馆紫外线在线监控…...

测试理论----Bug的严重程度(Severity)和优先级(Priority)的分类
【原文链接】测试理论----Bug的严重程度(Severity)和优先级(Priority)的分类 一、Bug的严重程度(Severity) Bug的Severity(严重程度)指的是一个Bug对软件系统功能影响的程度&#…...

斯坦福、Nautilus Chain等联合主办的 Hackathon 活动,现已接受报名
由 Stanford Blockchain Accelerator、Zebec Protocol、 Nautilus Chain、Rootz Lab 共同主办的黑客松活动,现已接受优秀项目提交参赛申请。 在加密行业发展早期,密码极客们就始终在对区块链世界基础设施,在发展方向的无限可能性进行探索。而…...

00后卷王,把我们这些老油条卷的辞职信都写好了........
现在的小年轻真的卷得过分了。 前段时间我们公司来了个00年的,工作没两年,跳槽到我们公司起薪18K,都快接近我了。 后来才知道人家是个卷王,从早干到晚就差搬张床到工位睡觉了。 最近和他聊了一次天,原来这位小老弟家…...

JavaEE(系列12) -- 常见锁策略
目录 1. 乐观锁和悲观锁 2. 轻量级锁与重量级锁 3. 自旋锁和挂起等待锁 4. 互斥锁和读写锁 5. 可重入锁与不可重入锁 6. 死锁 6.1 死锁的必要条件 6.2 如何避免死锁 7. 公平锁和非公平锁 8. Synchronized原理及加锁过程 8.1 Synchronized 小结 8.2 加锁工作过程 8.2.1 偏向锁…...

前端nginx接口跨域
前提:vue项目本地接口通过proxy都可使用,但是项目部署在服务器上后发现所有接口出现503如下状况 简而言之:页面部署在域名为https://aa.bb.cc.com/vehicle/#/下,但是我接口需访问的是https:// azz.qqv.com/permission/company/gro…...

【国产虚拟仪器】基于 ZYNQ 的电能质量系统高速数据采集系统设计
随着电网中非线性负荷用户的不断增加 , 电能质量问题日益严重 。 高精度数据采集系统能够为电能质 量分析提供准确的数据支持 , 是解决电能质量问题的关键依据 。 通过对比现有高速采集系统的设计方案 , 主 控电路多以 ARM 微控制器搭配…...

Java前缀和算法
一.什么是前缀和算法 通俗来讲,前缀和算法就是使用一个新数组来储存原数组中前n-1个元素的和(如果新数组的当前元素的下标为n,计算当前元素的值为原数组中从0到n-1下标数组元素的和),可能这样讲起来有点抽象࿰…...
pico 的两个双核相关函数的延时问题
pico高级API函数中, multicore_fifo_pop_timeout_us 和 multicore_fifo_push_timeout_us 的延时参数, 如修改为500微秒以上时,其延时似乎远远超过设定值,其反馈速度似乎被主核的交互所左右 ,而修改为200以下时&#x…...

Doxygen源码分析: QCString类依赖的qstr系列C函数浅析
2023-05-20 17:02:21 ChrisZZ imzhuofoxmailcom Hompage https://github.com/zchrissirhcz 文章目录 1. doxygen 版本2. QCString 类简介3. qstr 系列函数浅析qmemmove()qsnprintfqstrdup()qstrfree()qstrlen()qstrcpy()qstrncpy()qisempty()qstrcmp()qstrncmp()qisspace()qstr…...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

Python训练营-Day26-函数专题1:函数定义与参数
题目1:计算圆的面积 任务: 编写一个名为 calculate_circle_area 的函数,该函数接收圆的半径 radius 作为参数,并返回圆的面积。圆的面积 π * radius (可以使用 math.pi 作为 π 的值)要求:函数接收一个位置参数 radi…...

Windows电脑能装鸿蒙吗_Windows电脑体验鸿蒙电脑操作系统教程
鸿蒙电脑版操作系统来了,很多小伙伴想体验鸿蒙电脑版操作系统,可惜,鸿蒙系统并不支持你正在使用的传统的电脑来安装。不过可以通过可以使用华为官方提供的虚拟机,来体验大家心心念念的鸿蒙系统啦!注意:虚拟…...

aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...