假设在你的 iOS 应用程序中,你想对图像执行抓取或使用特定插值调整图像大小,作为机器学习模型的预处理步骤。你无法在 Xcode 上使用 Apple 的原生框架(例如 Core Image)轻易地做到这一点。如果你想坚持使用 Apple 的工具,你可能不得不使用Metal,并且仍然需要从头开始编写代码。
或者,你可以将 OpenCV 与你的 Xcode swift 项目集成,并使用 100 多种现成的优化图像处理算法来完成你的工作。尽管集成部分并不完全直接,但它执行起来非常快,因为它使用 C++。(这只会使你的应用程序在 iOS 设备上的大小增加大约 2MB。)
获取OpenCV iOS框架
首先,从其发布(https://opencv.org/releases/)页面下载最新的 OpenCV 版本 3 或 4 iOS 包。解压缩 zip 文件并获取opencv2.framework文件。
(本指南将基于 Xcode 12.3 和 OpenCV3。OpenCV4 的 API 会略有不同。)
将 OpenCV 框架添加到你的项目中
将 opencv2.framework 文件拖放到 Xcode 项目左侧的项目导航器窗格中。然后,Xcode 将提示一个窗口,其中包含用于添加框架的选项。
对于Destination勾选Copy items if needed
对于Added folders选择Create groups选项
对于Add to targets勾选你的目标应用程序。
在左侧导航器窗格中单击你的项目,然后转到Build Phases -> Link Binary With Libraries。你应该能够在那里看到opencv2.framework。(注意,将框架复制到项目中可能需要几秒钟)
(可选)添加与 OpenCV 配合使用的其他框架
根据你的项目使用的内容,你可能还必须链接以下一些框架,以便 OpenCV 正常工作。
- AssetsLibrary.framework
- CoreGraphics.framework
- CoreMedia.framework
- CoreFoundation.framework
- Accelerate.framework
在Build Phases -> Link Binary With Libraries下,点击+号,搜索上面的frameworks并添加。
设置框架搜索路径
在左侧导航器窗格中单击你的项目,然后转到Build Settings -> Framework Search Paths。确保在那里说明了opencv2.framework的正确目录路径。对于项目的根目录,你可以使用**$(PROJECT_DIR)/。**
创建包装器类文件
转到File -> New -> File。创建一个名为OpenCVWrapper的Cocoa Touch 类,并为Language选择Objective-C。将其保存在你的项目目录中,然后单击“ Create Bridging Header ”。
你将在左侧项目导航器窗格中看到 3 个新文件。
- OpenCVWrapper.h
- OpenCVWrapper.m
- {Project Name}-Bridging-Header.h
现在,通过将“OpenCVWrapper.m”重命名为“OpenCVWrapper.mm”,将“OpenCVWrapper.m”的文件扩展名从“ .m ”更改为“ .mm ”。然后,你可以看到 Xcode 将文件识别为Objective-C++文件,位于右侧窗格的File Inspector -> Identity and Type。(你可以按Option+Command+1打开文件检查器)
编辑桥接头文件
打开**{Project Name}-Bridging-Header.h**并添加以下行。
#import "OpenCVWrapper.h"
添加前缀头文件
转到File -> New -> File并筛选“ PCH ”。创建并保存在项目目录中。
将以下行添加到PrefixHeader.pch文件的最后一行#endif
之前。
#ifdef __cplusplus
#include <opencv2/opencv.hpp>
#endif
现在,该文件应包含以下行。
#ifndef PrefixHeader_pch
#define PrefixHeader_pch
#ifdef __cplusplus
#include <opencv2/opencv.hpp>
#endif
#endif
单击左侧导航窗格中的项目,然后转到Build Settings -> Prefix Header。在那里为PrefixHeader.pch添加正确的文件路径。如果该文件位于项目的“ .xcodeproj ”文件所在的目录中,则可以使用$(SRCROOT)/PrefixHeader.pch
.
编辑 OpenCVWrapper.h
我们将创建 3 个类方法。
- getOpenCVVersion — 以字符串形式提供 OpenCV 版本。(我们可以用它来快速检查 OpenCV 是否集成成功。)
- grayscaleImg — 采用 UIImage 并将其灰度输出为 UIImage。(这将给出如何使用 UIImages 的想法。)
- resizeImg — 获取一个 UIImage 并输出其调整大小的UIImage。(这会让你了解如何使用带有 alpha 通道的 UIImages 。)
因此,OpenCVWrapper.h应如下所示。
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface OpenCVWrapper : NSObject
+ (NSString *)getOpenCVVersion;
+ (UIImage *)grayscaleImg:(UIImage *)image;
+ (UIImage *)resizeImg:(UIImage *)image :(int)width :(int)height :(int)interpolation;
@end
NS_ASSUME_NONNULL_END
编辑 OpenCVWrapper.mm
OpenCVWrapper.mm应如下所示。
#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
#import "OpenCVWrapper.h"
/*
* add a method convertToMat to UIImage class
*/
@interface UIImage (OpenCVWrapper)
- (void)convertToMat: (cv::Mat *)pMat: (bool)alphaExists;
@end
@implementation UIImage (OpenCVWrapper)
- (void)convertToMat: (cv::Mat *)pMat: (bool)alphaExists {
if (self.imageOrientation == UIImageOrientationRight) {
/*
* When taking picture in portrait orientation,
* convert UIImage to OpenCV Matrix in landscape right-side-up orientation,
* and then rotate OpenCV Matrix to portrait orientation
*/
UIImageToMat([UIImage imageWithCGImage:self.CGImage scale:1.0 orientation:UIImageOrientationUp], *pMat, alphaExists);
cv::rotate(*pMat, *pMat, cv::ROTATE_90_CLOCKWISE);
} else if (self.imageOrientation == UIImageOrientationLeft) {
/*
* When taking picture in portrait upside-down orientation,
* convert UIImage to OpenCV Matrix in landscape right-side-up orientation,
* and then rotate OpenCV Matrix to portrait upside-down orientation
*/
UIImageToMat([UIImage imageWithCGImage:self.CGImage scale:1.0 orientation:UIImageOrientationUp], *pMat, alphaExists);
cv::rotate(*pMat, *pMat, cv::ROTATE_90_COUNTERCLOCKWISE);
} else {
/*
* When taking picture in landscape orientation,
* convert UIImage to OpenCV Matrix directly,
* and then ONLY rotate OpenCV Matrix for landscape left-side-up orientation
*/
UIImageToMat(self, *pMat, alphaExists);
if (self.imageOrientation == UIImageOrientationDown) {
cv::rotate(*pMat, *pMat, cv::ROTATE_180);
}
}
}
@end
@implementation OpenCVWrapper
+ (NSString *)getOpenCVVersion {
return [NSString stringWithFormat:@"OpenCV Version %s", CV_VERSION];
}
+ (UIImage *)grayscaleImg:(UIImage *)image {
cv::Mat mat;
[image convertToMat: &mat :false];
cv::Mat gray;
NSLog(@"channels = %d", mat.channels());
if (mat.channels() > 1) {
cv::cvtColor(mat, gray, CV_RGB2GRAY);
} else {
mat.copyTo(gray);
}
UIImage *grayImg = MatToUIImage(gray);
return grayImg;
}
+ (UIImage *)resizeImg:(UIImage *)image :(int)width :(int)height :(int)interpolation {
cv::Mat mat;
[image convertToMat: &mat :false];
if (mat.channels() == 4) {
[image convertToMat: &mat :true];
}
NSLog(@"source shape = (%d, %d)", mat.cols, mat.rows);
cv::Mat resized;
// cv::INTER_NEAREST = 0,
// cv::INTER_LINEAR = 1,
// cv::INTER_CUBIC = 2,
// cv::INTER_AREA = 3,
// cv::INTER_LANCZOS4 = 4,
// cv::INTER_LINEAR_EXACT = 5,
// cv::INTER_NEAREST_EXACT = 6,
// cv::INTER_MAX = 7,
// cv::WARP_FILL_OUTLIERS = 8,
// cv::WARP_INVERSE_MAP = 16
cv::Size size = {width, height};
cv::resize(mat, resized, size, 0, 0, interpolation);
NSLog(@"dst shape = (%d, %d)", resized.cols, resized.rows);
UIImage *resizedImg = MatToUIImage(resized);
return resizedImg;
}
@end
“ convertToMat”方法是(https://github.com/tobyliu-sw/OpenCVSample/blob/28ae9c733c4a7545eb126c9b90dca60e22c13e40/OpenCVSample/OpenCVSample.mm#L27)的修改版本,用于将UIImage转换为cv::Mat
。
或者你可以按照这个方法(https://docs.opencv.org/3.4.16/d3/def/tutorial_image_manipulation.html),虽然我没有亲自尝试过。
注意:
NSLog
可用于将字符串打印到控制台。- 在 OpenCV4 中,方法
grayscaleImg
中的CV_RGB2GRAY应该是cv::COLOR_RGB2GRAY。 - 在灰度图像中,没有 alpha 通道。因此,在
grayscaleImg
方法中调用convertToMat时,它被忽略,alphaExists 被设置为 false。 - 调整大小时,alpha 通道也必须调整大小。因此,如果图像有 4 个通道,则在
resizeImg
方法中调用convertToMat 时,将考虑 alpha 通道并将 alphaExists 设置为 true。 - 检查
resizeImg
方法下的注释以找到每个插值的输入整数代码。(例如 — 最近邻 = 0;双线性 = 1;双三次 = 2 等等。)
检查集成
你可以调用“getOpenCVVersion”,如下所示,在“ViewController.swift”中的super.viewDidLoad()行之后的viewDidLoad()中,如果集成成功,它会将OpenCV版本打印到控制台。
print("(OpenCVWrapper.getOpenCVVersion())")
使用 UIImages
假设你有一个名为“ sample_img.png ”的 1280×720 RGBA 图像作为应用程序中的资产,你将能够获得它的灰度UIImage使用swift如下(例如,在第 11 节中的ViewController.swift)。
let rgbaIn = UIImage(named: "sample_img")!
let grayOut = OpenCVWrapper.grayscaleImg(rgbaIn)
同样,你可以使用双线性插值获得其调整为 640×360 大小的 RGBA UIImage ,如下所示。
let rgbaIn = UIImage(named: "sample_img")!
let rgba256 = OpenCVWrapper.resizeImg(rgbaIn, 640, 360, 1)
(如果出现任何错误,请使用顶部的“Product”菜单再次尝试清洁和构建。)
现在你已经了解了如何使用 OpenCV 处理 UIImage。你可以参考 OpenCV C++ 文档来浏览该库。
OpenCV C++ 文档:https://docs.opencv.org/3.4.16/d3/d63/classcv_1_1Mat.html
作者:磐怼怼 | 来源:公众号——深度学习与计算机视觉
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。