Nyris Image Matching SDK for iOS
简介
Nyris Image Matching SDK for iOS(NyrisSDK)允许使用图像匹配服务,该服务根据给定的图像提供产品列表。
更多信息请参阅 nyris.io
功能
- 内置相机管理类。
- 提供拍摄图片产品的 100% 匹配。
- 提供文本搜索。
- 提供从图片中提取边界框。
- 提供图像助手以操作原始相机图像。
最小要求
- Swift 5.x
- 最低部署目标为 iOS 12。
注意:对于 Swift 4.x,请使用 'feature/swift4.x' 分支 -- 不支持 注意:对于 Swift 3.2,请使用 'feature/swift3.2' 分支 -- 不支持
安装
Swift 包管理器
Nyris 图像识别 SDK(NyrisSDK)可通过 Swift 包管理器获取。要安装,只需将以下行添加到您的 Package 文件中的依赖数组:
dependencies: [
.package(url: "https://github.com/nyris/Nyris.IMX.iOS.git", .upToNextMajor(from: "0.4.6"))
]
CocoaPods
Nyris 图像识别 SDK(NyrisSDK)可通过 CocoaPods 获取。要安装,只需在 Podfile 中添加以下行:
pod "NyrisSDK"
对于 Swift 3.2
pod 'NyrisSDK', :git => 'https://github.com/nyris/Nyris.IMX.iOS.git', :branch => 'feature/swift3.2'
Carthage
请将以下内容写入您的Cartfile:github "nyris/Nyris.IMX.iOS"
手动
将 *.swift 文件复制到您的项目中。
设置
首先设置 NyrisClient 共享实例
NyrisClient.instance.setup(clientID: "YOUR-CLIENT-ID")
如果您需要更改端点 URL(例如代理),则可以将遵循 EndpointsProvider 协议的对象作为第二个参数传递给设置
struct CustomEndpoint : EndpointsProvider {
var openIDServer: String = "https://custom-domain.io"
var imageMatchingServer: String = "https://custom-domain.io"
var apiServer: String = "https://custom-domain.io"
}
NyrisClient.instance.setup(clientID: "YOUR-CLIENT-ID", endpointProvider: CustomEndpoint())
这允许您使用 nyris API 上定义的同一组 API 端点,但位于不同的域。
如果您更改了代理中的标题,则可以使用 HeaderMapper
协议提供映射
struct CustomMapping : HeaderMapper {
private let mapping = [
"api_key" : "APIKEY_IN_PROXY_HEADER"
]
public func getKey(mappedKey: String) -> String? {
return mapping[mappedKey]
}
}
NyrisClient.instance.setup(clientID: "YOUR-CLIENT-ID", endpointProvider: CustomEndpoint(), headerEntriesMapper:CustomMapping )
图像匹配
用法
ImageMatchingService
服务允许您获取与提供的图像中产品匹配的报价列表。
基本示例
let service = ImageMatchingService()
let image = ... // YOUR UIImage (at least 512 width or height)
service.getSimilarProducts(image: image) { (offersResult, error) in
// you are on the main thread
}
它将返回 OffersResult
,该结果包含与给定图像中对象匹配的产品列表。
如果您不希望处理图像缩放/旋转,则可以使用 match 方法,它将为您准备给定图像,例如:
let service = ImageMatchingService()
let image = ... // YOUR UIImage (e.g: 1024x1024)
// The match method will create a scaled down (512x512) image copy
service.match(image: image) { (offerList, error) in
// you are on the main thread
}
如果您是从相机拍照,则可以使用 match 方法通过启用 useDeviceOrientation 参数来正确旋转和缩放图像,例如:
let service = ImageMatchingService()
let image = ... // UIImage coming from camera
// The image will be rotated to portrait mode and scaled down
service.match(image: image, useDeviceOrientation:true) { (offerList, error) in
// you are on the main thread
}
如果您正在使用 UIImageView,则有一个可用的扩展方法
imageView.match { (offerList, error) in
// you are on the main thread
}
注意:调用任何UIImageView扩展方法之前,请确保已设置SDK客户端。
搜索类型
both getSimilarProducts
和 match
方法都允许通过它们的参数进行不同类型的搜索
- isSemanticSearch: 仅启用语义搜索
- isFirstStageOnly: 启用精确匹配
优惠格式
默认输出格式设置为 "application/offers.complete+json",您可以通过以下方式更改它
service.outputFormat = "Your output format"
附加头信息属性
您可以使用一些附加头信息属性来更改结果。
service.xOptions = "default"
您可以在此处找到所有附加头信息属性。
结果语言
默认情况下,服务将查找所有可用语言的服务。您可以通过设置以下内容来覆盖此行为
service.acceptLanguage = "EN" //"DE", "FR" ...
将其设置为设备语言
service.acceptLanguage = (Locale.current as NSLocale).object(forKey: .languageCode) as? String ?? "*"
重要提示:提供图像的宽度和高度必须至少为512,例如:512x400,200x512。有关更多详细信息,请参阅ImageHelper部分。
请求筛选
为了筛选图像匹配服务的输出结果,您需要按照以下方式填写过滤器属性:
let service = ImageMatchingService()
service.filters = [
NyrisSearchFilter(type: "filter-type", values: ["value-1", "value-2", "value-3"]),
]
NyrisSearchFilter
是一种结构,可以包含筛选类型和值列表。要获取筛选器(类型和值),请联系客户支持。
文本搜索
用法
SearchService
服务允许您获取与文本查询匹配的报价列表。
示例
let service = SearchService()
service.search(query: "water") { (offerList, error) in
}
它将返回 OffersResult
,该结果包含与查询匹配的产品列表。
报价格式
默认输出格式设置为 "application/offers.complete+json",您可以通过以下方式更改它
service.outputFormat = "Your output format"
附加标题属性
您可以使用一些附加头信息属性来更改结果。
service.xOptions = "default"
您可以在此处找到所有附加头信息属性。
结果语言
默认情况下,服务将寻找所有可用语言的那一刻。您可以设置来覆盖此行为:
service.acceptLanguage = "EN" //"DE", "FR" ...
将其设置为设备语言
service.acceptLanguage = (Locale.current as NSLocale).object(forKey: .languageCode) as? String ?? "*"
边界框提取
使用
ProductExtractionService
服务允许您从给定图像中提取对象边界框。它将识别图片中的对象。
基本示例
let service = ProductExtractionService()
let image = ... // Your UIImage (at least 512 width or height)
let displayFrame = displayView.frame
service.extractObjects(from image:image, displayFrame:displayFrame) { (boxes, error) in
// Main thread
}
这将返回一个从给定图像中提取并已投影到显示帧的 ExtractedObject
的列表。它可以直接在屏幕上显示,无需任何进一步操作。
如果您正在使用 UIImageView,则有一个可用的扩展方法
imageView.extractProducts { (objects, error) in
// you are on the main thread
}
方法支持所有不修改图像宽高比的内容模式值,例如
.scaleAspectFit
.scaleAspectFill
.center
请注意,在使用 .center
或 .scaleAspectFill
的情况下,您可能会遇到屏幕外的起始位置。
裁剪
根据 ExtractedObject
裁剪图像区域,可以使用
let croppedImage = ImageHelper.crop(from: self.imageView,
extractedObject: box)
然后您可以将此裁剪后的图像发送到匹配的服务。
重要! imageView.image必须与用于提取框的图像相同,且不得有任何尺寸修改。框应已投影到屏幕上。如果您想裁剪未投影的框(原始API结果),请参阅ImageHelper裁剪部分。
灵活使用
如果您不希望对服务器结果的输出进行任何修改(投影),请使用 getExtractObjects
let service = ProductExtractionService()
let image = ... // YOUR UIImage (at least 512 width or height)
service.getExtractObjects(from: image) { (objects, error) in
// Main thread
}
此示例将返回从给定图像中提取的 ExtractedObject
列表。这些无法在不将图像帧(0,0,image.width, image.height)的区域投影到所需的显示帧的情况下显示在屏幕上。
您可以使用以下方式将一个 ExtractedObject
投影到显示帧
let box:ExtractedObject = // Your extracted object
let extractionFrame = CGRect(origin: CGPoint.zero, size: imageSource.size)
let displayFrame:CGRect = // e.g: UIImageView frame
let projectedObject = box.projectOn(projectionFrame: displayFrame,
from: extractionFrame)
重要提示
提供的图像的宽度或高度至少应为512,例如:512x400,200x512。
有关将 ExtractedObject
区域投影到不同帧的更多信息,请参阅 ImageHelper 部分。
反馈API
使用请求ID(来自 OffersResult
)和 FeedbackService
,您可以将多个事件提交到我们的分析引擎。您可以在枚举 NyrisFeedbackEventType
中找到支持的事件列表。
发送反馈事件
let feedbackService = FeedbackService()
// offersResult is returned from matching service.
feedbackService.sendEvent(eventType: .click(positions: [0], productIds: [offersResult.products[0].oid]),
requestID: offersResult.requestID,
sessionID: offersResult.sessionID){ (result:Result<String>) in
// Result will contain empty string in case of success.
}
关于requestID的注意事项 当您从匹配服务发送请求时,您可以获取一个requestID,它将是OffersResult
对象的一部分。
关于sessionID的注意事项 这代表用户发送的第一个请求ID,它可以存在于匹配服务返回的OffersResult
中。如果您不想将多个请求组合成一个会话,则sessionID
可以与requestID
相同。
关于区域事件类型的注意事项 区域将接受一个需要归一化(0-1)的CGRect,如果与.region
枚举相关联的CGRect未归一化,则.sendEvent
将在回调结果中返回InvalidInput错误。
摄像头使用
NyrisSDK具有内置的Camera类,提供图像捕获功能。您还可以使用自己的摄像头实现。
使用以下代码创建CameraManager实例
设置摄像头管理器
lazy var cameraManager: CameraManager = {
let configuration = CameraConfiguration(metadata: [],
captureMode: .none, sessionPresent: SessionPreset.high)
return CameraManager(configuration: configuration)
}()
请求使用权限
然后,请求摄像头使用权限,并在权限被授予时显示摄像头视图
if cameraManager.permission != .authorized {
cameraManager.updatePermission()
}
显示相册预览
if cameraManager.permission == .authorized {
cameraManager.addObservers()
// Display the preview of the camera along with a rect of intrest for scanning.
cameraManager.display(on: cameraView, scannerFrame: scanView.frame)
}
您必须使用以下方式取消订阅
cameraManager.removeObservers()
获取条形码和其他元数据
您可以通过设置 barcodeScannerDelegate 代理来通过相机管理器获取条形码
cameraManager = CameraManager(configuration: configuration)
cameraManager.barcodeScannerDelegate = self
订阅设备旋转
如果您想让视频预览和图像在设备旋转时旋转,将相机管理器的可选 useDeviceRotation
设置为 true
if cameraManager.permission != .authorized {
/// ...
} else {
cameraManager.setup(useDeviceRotation: true)
/// ...
}
启动会话
要启动相机会话,调用以下方法
cameraManager.start()
捕获图像
最后,通过调用以下方法拍照:
cameraManager.takePicture { [weak self] image in
// handle the picture
}
结束会话
不再使用摄像头,或者应用程序处于后台模式时,请调用停止方法
self.cameraManager.stop()
权限更新
用户可以随时更改摄像头使用权限,为此,您需要遵守 CameraAuthorizationDelegate
协议。
class CameraController {
override func viewDidAppear(_ animated: Bool) {
/// code
self.cameraManager.authorizationDelegate = self
}
}
extension CameraController : CameraAuthorizationDelegate {
func didChangeAuthorization(cameraManager: CameraManager, authorization: SessionSetupResult) {
switch authorization {
case .authorized:
if self.cameraManager.isRunning == false {
self.cameraManager.setup()
}
self.cameraManager.display(on: self.cameraView)
default:
///showError(message: "Please authorize camera access to use this app"
}
}
}
重要提示:请确保将 NSCameraUsageDescription 或 Privacy - Camera usage description 添加到您的 plist 文件中。否则,如果在 iOS 10 或更高版本上尝试访问摄像头,您的应用将会崩溃。
重要提示:如果您正在使用 CameraManager
,则无需关心下一部分。
图像助手
API 需要至少一个尺寸等于 512 的图像,例如:512x200,400x512。
使用 CameraManager
类拍摄的图片会自动缩放并正确调整方向,因此如果您使用该类,则无需担心图像尺寸和旋转。
如果您正在使用自己的摄像头逻辑或另一个第三方摄像头库,NyrisSDK 提供了一个 ImageHelper
类,该类提供缩放和旋转图像的方法。
重要提示:来自 iPhone 摄像头的照片默认为横幅,ImageHelper
提供了一种纠正方向的方法。
准备图像
准备方法抽象化了相机图像的缩放和旋转,您可以使用它如下:
let (preparedImage, error) = ImageHelper.(image:cameraImage, useDeviceOrientation:true)
这将返回一个包含准备好的图像和错误的元组,两者均为可空。请确保该方法未失败。准备好的图像将被缩放和旋转。
如果您希望有更多灵活性,请阅读以下部分。
旋转图像
由于默认旋转为横向,因此应将图像旋转到当前方向,为此,请调用:
/// imageData is type of Data
/// useDeviceOrientation, to rotate the image based on device orientation
let image = ImageHelper.correctOrientation(imageData, useDeviceOrientation:true)
这将返回,一个方向已纠正的图像。
调整图像大小
要将图像调整到预期的512像素大小(更多详细信息请点击此处),请调用以下方法:
// This method will use target pixel-area to resize the image to while keeping aspect ratio.
// It guarantee that one side is 512.
ImageHelper.resizeWithRatio(image: image, size: CGSize(width: 512, height: 512))
ImageHelper.resizeWithRatio
方法将根据目标像素区域调整图像大小到提供的大小,同时保持宽高比。
边界框投影
如果您向ProductExtractionService
发送一个512x900的图像,服务将返回一个ExtractedObject
来标识图像指定维度(512x900)中的对象,假设我们获得了一个边界框:
- x : 30
- y: 40
- 宽度:100
- 高度:140
如果将此值投影到设备屏幕上,则将不正确,要正确在设备屏幕上显示这些框,我们需要使用以下方法将边界框投影到屏幕尺寸:
let scaledRectangle = ImageHelper.applyRectProjection(
on: self, // 1
from: baseFrame, // 2
to: projectionFrame, // 3
padding: 0, // 4
navigationHeaderHeight: 0) //5
- 我们想要投影到不同框架上的矩形。
- 我们想要投影的框架:例如:(0,0, image.width, image.height)
- 我们想要投影到的框架:例如,一个UIImageView的框架
- 如果需要,请添加填充。
- 如果您需要导航标题以避免不必要的Y偏移,请使用。
这将返回一个准备在设备屏幕上显示的边界框。
边界框裁剪
如果您在UIImageView(或任何其他视图)上投影一个ExtractedObject
,可以使用以下方法进行裁剪:
let crop = ImageHelper.crop(from: self.imageView,
extractedObject: box)
如果您使用getExtractObjects
方法请求了ExtractedObject
,并且想在没有投影的情况下进行裁剪,请使用:
let rect = box.region.toCGRect()
let crop = ImageHelper.crop(image: image, croppingRect: rect)