NyrisSDK 0.7.4

NyrisSDK 0.7.4

测试已测试
语言语言 SwiftSwift
许可证 Apache-2.0
发布上次发布2023 年 6 月
SPM支持 SPM

Anas 维护。



NyrisSDK 0.7.4

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 getSimilarProductsmatch 方法都允许通过它们的参数进行不同类型的搜索

  • 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
  1. 我们想要投影到不同框架上的矩形。
  2. 我们想要投影的框架:例如:(0,0, image.width, image.height)
  3. 我们想要投影到的框架:例如,一个UIImageView的框架
  4. 如果需要,请添加填充。
  5. 如果您需要导航标题以避免不必要的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)