Acuant iOS SDK v11.3.2
2020年3月
查看https://github.com/Acuant/iOSSDKV11/releases以获取发行说明。
简介
本文件提供了关于 Acuant iOS SDK 的详细信息。下面描述了 Acuant 推荐的工作流程。
注意可接受的图像应裁剪良好、清晰且无反光,分辨率至少为 300 dpi(用于数据采集)或 600 dpi(用于验证)。宽高比应合理并与身份证相匹配。
先决条件
- iOS 版本 11.0 或更高
模块
SDK 包含以下模块
Acuant Face Capture Library (AcuantFaceCapture)
- 使用原生 iOS 摄像头捕获人脸。
Acuant Passive Liveness Library (AcuantPassiveLiveness)
- 使用专用算法检测真人
Acuant Common Library (AcuantCommon)
- 包含所有共享内部模型和支持类
Acuant Camera Library (AcuantCamera)
- 使用 iOS 原生摄像头库实现
- 使用 AcuantImagePreparation 进行裁剪
Acuant Image Preparation Library (AcuantImagePreparation)
- 包含所有图像处理,如裁剪、计算锐度和反光
Acuant Document Processing Library (AcuantDocumentProcessing)
- 包含所有上传文档图像、处理和获取结果的方法
Acuant人脸匹配库(AcuantFaceMatch)
- 包含一种用于匹配两张人脸图像的方法
Acuant HG 生存性库(AcuantHGLiveness)
- 使用iOS本地摄像头库以专有算法捕获面部生存性
Acuant IP 生存性库(AcuantIPLiveness)
- 使用专用算法检测真人
手动设置
- 添加以下依赖的嵌入框架
-
AcuantFaceCapture
-
AcuantPassiveLiveness
-
AcuantCommon
-
AcuantImagePreparation
-
AcuanCamera
-
AcuantDocumentProcessing
-
AcuantHGLiveness
-
AcuantFaceMatch
-
AcuantIPLiveness - iProov.framework - KeychainAccess.framework - SocketIO.framework - Starscream.framework - SwiftyJSON.framework
-
在Xcode中打开您的项目,导航到应用程序项目设置中的“构建阶段”标签。添加一个新的“运行脚本”。
-
将以下内容添加到脚本中。
/usr/local/bin/carthage copy-frameworks
-
创建新的 inputFileList.xcfilelist 和 outputFileList.xcfilelist。将必要的框架添加到这两个文件中。请参阅存储库中的示例。
-
将.xcfilelist 添加到您的运行脚本中。获取更多信息,请访问 https://github.com/Carthage/Carthage。
使用COCOAPODS
-
如果您正在使用COCOAPODS,则添加以下Podfile
platform :ios, '11.0' pod 'AcuantiOSSDKV11', '~> 11.3.2'
-
在构建设置中为所有Acuant pod框架启用“BUILD_FOR_DISTRIBUTION”。
-
使用Cocoapods。将其添加到Podfile中。
post_install do |installer| installer.pods_project.targets.each do |target| if ['AcuantiOSSDKV11', 'KeychainAccess', 'Socket.IO-Client-Swift', 'Starscream' 'SwiftyJSON'].include? target.name target.build_configurations.each do |config| config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES' end end end end
-
手动
-
必需设置
-
创建一个名为 AcuantConfig 的 plist 文件,其中包含以下详细信息
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>acuant_username</key> <string>[email protected]</string> <key>acuant_password</key> <string>xxxxxxxxxx</string> <key>acuant_subscription</key> <string>xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</string> <key>frm_endpoint</key> <string>https://frm.acuant.net</string> <key>passive_liveness_endpoint</key> <string>https://passlive.acuant.net</string> <key>med_endpoint</key> <string>https://medicscan.acuant.net</string> <key>assureid_endpoint</key> <string>https://services.assureid.net</string> </dict> </plist>
使用AcuantCamera捕获图像
-
AcuantCamera最好在纵向模式下使用。在使用摄像头之前锁定应用的方向。
-
设置回调
// Returns the image and barcodeString captured from device public protocol CameraCaptureDelegate { func setCapturedImage(image:Image, barcodeString:String?) }
-
打开摄像头
注意:现在选项通过一个选项对象定义。请参阅 AcuantCameraOptions 获取所有可配置的字段。
let options = AcuantCameraOptions(autoCapture: true, hideNavigationBar: true) let documentCameraController = DocumentCameraController.getCameraController(delegate:self!, cameraOptions: options) navigationController.pushViewController(documentCameraController, animated: false)
-
获取捕获的图像
public protocol CameraCaptureDelegate { func setCapturedImage(image:Image, barcodeString:String?) }
使用DocumentCaptureSesssion(查看DocumentCameraController.swift以获取参考)进行自定义UI
-
获取DocumentCaptureSesssion。
@objc public protocol DocumentCaptureDelegate { func readyToCapture() // gets called when triggering capture func documentCaptured(image:UIImage, barcodeString:String?) // gets called with captured result } let captureSession = DocumentCaptureSession.getDocumentCaptureSession( delegate: DocumentCaptureDelegate, // session callback frameDelegate: FrameAnalysisDelegate, // frame anaylsis callback autoCapture:Bool, // enable frame analysis captureDevice:AVCaptureDevice?) // AV Capture Device
-
开始会话,然后将会话添加到AVCaptureVideoPreviewLayer。
captureSession.start() // will start the capture session. let videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession) //add custom ui
-
接收帧结果。
@objc public enum FrameResult : Int{ case NO_DOCUMENT, // No document SMALL_DOCUMENT, // Document is small BAD_ASPECT_RATIO, // Document type does not match aspect ratio GOOD_DOCUMENT, // Document is good to trigger capture DOCUMENT_NOT_IN_FRAME // Document is not in frame } @objc public protocol FrameAnalysisDelegate{ func onFrameAvailable(frameResult: FrameResult, points: Array<CGPoint>?) }
-
触发捕获。
captureSession.enableCapture()
-
DocumentCaptureDelegate将执行并带有结果
func documentCaptured(image:UIImage, barcodeString:String?)
注意: AcuantCamera依赖于<ссxsstrong>AcuantImagePreparation和< Получитеstrong>AcuantCommon。
AcuantImagePreparation
此模块包含所有图像准备功能。
-
初始化
AcuantImagePreparation.initialize(delegate: InitializationDelegate) public protocol InitializationDelegate { func initializationFinished(error: AcuantError?); }
注意:如果您在初始化时没有使用配置文件,那么请使用以下语句(提供适当的凭据以供用户名、密码和订阅ID使用)
Credential.setUsername(username: "xxx") Credential.setPassword(password: "xxxx") Credential.setSubscription(subscription: "xxxxxx") let endpoints = Endpoints() endpoints.frmEndpoint = "https://frm.acuant.net" endpoints.healthInsuranceEndpoint = "https://medicscan.acuant.net" endpoints.idEndpoint = "https://services.assureid.net" Credential.setEndpoints(endpoints: endpoints) AcuantImagePreparation.initialize(delegate:self)
不带订阅ID的初始化
AcuantImagePreparation可以通过提供用户名和密码来初始化。但是,如果不提供订阅ID,则应用程序只能捕获图像并获取图像。没有订阅ID
-
只能使用<олучитеstrong>AcuantCamera、AcuantImagePreparation和<учитеstrong>AcuantHGLiveness模块。
-
SDK可用于捕获身份证明文件。
-
捕获的图像可以从SDK中导出。请参阅AcuantCamera项目中的DocumentCaptureDelegate协议。
public protocol DocumentCaptureDelegate { func readyToCapture() func documentCaptured(image:UIImage, barcodeString:String?) }
裁剪
在捕获图像后,将其发送到裁剪库进行裁剪。
public class func crop(data: CroppingData)->Image
// CroppingData & Image are part of AcuantCommon
// Sample
let croppingData = CroppingData()
croppingData.image = image // UIImage
let croppedImage = AcuantImagePreparation.crop(data: croppingData)
清晰度
sharpness方法返回 image 的清晰度值。如果清晰度值大于50,则认为图像是清晰的(不是模糊的)。
public class func sharpness(image: UIImage)->Int
反射光
《glare` 方法返回图像的光线反射值。如果光线反射值为100,则图像不包含光线反射。如果光线反射值为0,则图像包含光线反射。
public class func glare(image: UIImage)->Int
AcuantDocumentProcessing
在捕获文档图像后,可以使用以下步骤进行处理。
注意:如果上传失败并出现错误,请使用更好的图像重试图像上传。
-
创建实例
public class func createInstance(options:IdOptions,delegate:CreateInstanceDelegate) public protocol CreateInstanceDelegate{ func instanceCreated(instanceId : String?,error:AcuantError?); }
-
上传图像
public class func uploadImage(instancdId:String,data:IdData,options:IdOptions,delegate:UploadImageDelegate) public protocol UploadImageDelegate{ func imageUploaded(error: AcuantError?,classification:Classification?); }
-
获取数据
public class func getData(instanceId:String,isHealthCard:Bool,delegate:GetDataDelegate?) public protocol UploadImageDelegate{ func imageUploaded(error: AcuantError?,classification:Classification?); }
-
删除实例
public class func deleteInstance(instanceId : String,type:DeleteType, delegate:DeleteDelegate) public protocol DeleteDelegate { func instanceDeleted(success : Bool) }
Acuant Face Capture
-
(可选)设置默认图像。在示例应用程序项目的“Assets”目录中定位“acuant_default_face_image.png”。如果需要,将其添加到您的应用程序中。
-
在应用程序的可本地化字符串中设置本地化字符串
"acuant_face_camera_initial" = "Align face to start capture"; "acuant_face_camera_face_too_close" = "Too Close! Move Away"; "acuant_face_camera_face_too_far" = "Move Closer"; "acuant_face_camera_face_has_angle" = "Face has Angle. Do not tilt"; "acuant_face_camera_face_not_in_frame" = "Move in Frame"; "acuant_face_camera_face_moved" = "Hold Steady"; "acuant_face_camera_capturing_2" = "Capturing\n2..."; "acuant_face_camera_capturing_1" = "Capturing\n1...";
-
设置任何必要的UI自定义
class FaceAcuantCameraOptions{ public let totalCaptureTime: Int //totoal time to capture public let bracketColorDefault: CGColor //bracket color default (no face) public let bracketColorError: CGColor //bracket color error (error in face requirements) public let bracketColorGood: CGColor //bracket color good (good face requirements) public let fontColorDefault: CGColor //font color default public let fontColorError: CGColor //font color error public let fontColorGood: CGColor //font color good public let defaultImageUrl: String //default image public let showOval: Bool // show oval } //example let options = FaceAcuantCameraOptions()
-
获取控制器并将其推送到navigationController
let controller = AcuantFaceCaptureController() controller.options = options controller.callback = { [weak self] (image: UIImage?) in if(image == nil){ //user canceled } } self.navigationController.pushViewController(controller, animated: true)
-
使用捕获结果使用回调。
Acuant Passive Liveness
Acuant建议使用`LiveAssessment`属性而不是分数来评估响应。《AcuantPassiveLiveness.startSelfieCapture`会返回一个重缩放的图像。
遵循以下建议,以有效地处理图像以实现被动活体识别。
图像要求
- 高度:最小480像素,建议720或1080像素
- 压缩:不建议对图像进行压缩(JPEG 70级别或以上是可以接受的)。为了获得最佳效果,请使用未压缩的图像。
人脸要求
- 垂直旋转:面俯仰角和偏航角:从-20到20度,±3度
- 平面旋转:面翻滚角:从-30到30度,±3度
- 瞳距:眼睛之间的最小距离 90 ± 5像素
- 面部大小:至少200像素的任何一个维度
- 每张图像中的面部:1
- 太阳镜:必须摘除
捕获要求
以下可能显着增加错误或错误结果
- 使用运动模糊效果
- 纹理过滤
- 面部及其最近周围环境的聚光灯
- 光线条件差或彩色光的环境
注意 此API不支持使用鱼眼镜头。
-
使用UIImage获取被动活度结果
//liveness request class AcuantLivenessRequest{ public let image: UIImage public init(image: UIImage) } //liveness response class AcuantLivenessResponse{ public let score: Int public let result: AcuantLivenessAssessment public enum AcuantLivenessAssessment: String{ case Error case PoorQuality case Live case NotLive } } //liveness response class AcuantLivenessError{ public let errorCode: AcuantLivenessErrorCode? public let description: String? public enum AcuantLivenessErrorCode: String{ case Unknown case FaceTooClose case FaceNotFound case FaceTooSmall case FaceAngleTooLarge case FailedToReadImage case InvalidRequest case InvalidRequestSettings case Unauthorized case NotFound case InternalError case InvalidJson } } //example AcuantPassiveLiveness.postLiveness(request: AcuantLivenessRequest(image: image)){ [weak self] (result: AcuantLivenessResponse?, error: AcuantLivenessError?) in //response }
AcuantHGLiveness
该模块通过使用眨眼检测来检查是否存在活动(主题是否为活人)。该用户界面代码包含在示例应用(FaceLivenessCameraController.swift)中,客户可以根据具体需求对其进行修改。
Sample应用中的Acuant UI
// Code for HG Live controller
let liveFaceViewController = FaceLivenessCameraController()
liveFaceViewController.delegate : AcuantHGLiveFaceCaptureDelegate = self
self.navigationController.pushViewController(liveFaceViewController, animated: true)
自定义UI,创建面部活度捕捉会话
enum AcuantFaceType : Int {
case NONE // No face
case FACE_TOO_CLOSE // face is too close camera
case FACE_MOVED // face moved from its original position
case FACE_TOO_FAR // face is too far from camera
case FACE_NOT_IN_FRAME // face is not in frame
case FACE_GOOD_DISTANCE // face is good distance and in frame
}
public protocol AcuantHGLiveFaceCaptureDelegate {
func liveFaceDetailsCaptured(liveFaceDetails: LiveFaceDetails?, faceType: AcuantHGLiveness.AcuantFaceType)
}
public class func getFaceCaptureSession(delegate:AcuantHGLiveFaceCaptureDelegate?, captureDevice: AVCaptureDevice?)-> FaceCaptureSession
let faceCaptureSession = AcuantHGLiveness.getFaceCaptureSession(delegate: self, captureDevice: captureDevice)
AcuantIPLiveness
AcuantIPLiveness模块用于检查主题是否为活人。
-
运行设置
AcuantIPLiveness.performLivenessSetup(delegate:LivenessSetupDelegate) public protocol LivenessSetupDelegate{ func livenessSetupSucceeded(result:LivenessSetupResult) // Called when setup succeeds func livenessSetupFailed(error:AcuantError) // Called when setup failed } public class LivenessSetupResult { public var apiKey : String public var token : String public var userId : String public var apiEndpoint : String }
-
进行活度测试
注意 您可以使用 LivenessSetupResult 根据需要自定义界面。
// Adjust various colors for the camera preview: setupResult.ui.lineColor = .white setupResult.ui.backgroundColor = .black setupResult.ui.loadingTintColor = .lightGray setupResult.ui.notReadyTintColor = .orange setupResult.ui.readyTintColor = .green setupResult.ui.title = "title" // Specify a custom title to be shown. Defaults to nil which will show an auto generated message. Set to empty string ("") to hide the message entirely. setupResult.ui.regularFont = "SomeFont" setupResult.ui.boldFont = "SomeFont-Bold" setupResult.ui.fonts = ["SomeFont", "SomeFont-Bold"] // If using custom fonts, specify them here (don't forget to add them to your Info.plist!) setupResult.ui.logoImage = UIImage(named: "foo") setupResult.ui.scanLineDisabled = false // Disables the vertical sweeping scanline while flashing setupResult.ui.autoStartDisabled = false // Disable the "auto start" countdown functionality. The user will have to tap the screen to start liveness test AcuantIPLiveness.performLivenessTest(setupResult:LivenessSetupResult, delegate:LivenessTestDelegate) public protocol LivenessTestDelegate{ func livenessTestCompleted() // This is for the test; called when Enroll is complete func livenessTestCompletedWithError(error:AcuantError?) // This is for the test; called when Enroll is complete and error occured func livenessTestProcessing(progress: Double, message: String) // This is for real-time notifications of progress of liveness test. It will be called after user captures live face. It is intended to be used for custom UI progress notification. }
-
获取活度测试结果
AcuantIPLiveness.getLivenessTestResult(token:String,userId:String,delegate:LivenessTestResultDelegate) public protocol LivenessTestResultDelegate{ func livenessTestResultReceived(result:LivenessResult) // Called when test result was received successfully func livenessTestResultReceiveFailed(error:AcuantError) // Called when test result was not received } public class LivenessTestResult { public var passedLivenessTest : Bool = false public var image : UIImage? = nil }
以下是依赖列表
- iProov.framework
- KeychainAccess.framework
- SocketIO.framework
- Startscream.framework
- SwiftyJSON.framework
AcuantFaceMatch
此模块用于匹配两张面部图像
public class func processFacialMatch(facialData : FacialMatchData, delegate : FacialMatchDelegate)
public protocol FacialMatchDelegate {
func facialMatchFinished(result:FacialMatchResult?)
}
public class FacialMatchData{
public var faceImageOne : UIImage // Facial image from ID Card (image gets compressed by 80%)
public var faceImageTwo : UIImage // Facial image from selfie capture during liveness check (image gets compressed by 80%)
}
错误代码
public class AcuantErrorCodes{
public static let ERROR_InvalidCredentials = -1
public static let ERROR_InvalidLicenseKey = -2
public static let ERROR_InvalidEndpoint = -3
public static let ERROR_InitializationNotFinished = -4
public static let ERROR_Network = -5
public static let ERROR_InvalidJson = -6
public static let ERROR_CouldNotCrop = -7
public static let ERROR_NotEnoughMemory = -8
public static let ERROR_BarcodeCaptureFailed = -9
public static let ERROR_BarcodeCaptureTimedOut = -10
public static let ERROR_BarcodeCaptureNotAuthorized = -11
public static let ERROR_LiveFaceCaptureNotAuthorized = -12
public static let ERROR_CouldNotCreateConnectInstance = -13
public static let ERROR_CouldNotUploadConnectImage = -14
public static let ERROR_CouldNotUploadConnectBarcode = -15
public static let ERROR_CouldNotGetConnectData = -16
public static let ERROR_CouldNotProcessFacialMatch = -17
public static let ERROR_CardWidthNotSet = -18
public static let ERROR_CouldNotGetHealthCardData = -19
public static let ERROR_CouldNotClassifyDocument = -20
public static let ERROR_LowResolutionImage = -21
public static let ERROR_BlurryImage = -22
public static let ERROR_ImageWithGlare = -23
public static let ERROR_CouldNotGetIPLivenessToken = -24
public static let ERROR_NotALiveFace = -25
public static let ERROR_CouldNotAccessLivenessData = -26
}
错误描述
public class AcuantErrorDescriptions {
public static let ERROR_DESC_InvalidCredentials = "Invalid credentials"
public static let ERROR_DESC_InvalidLicenseKey = "Invalid License Key"
public static let ERROR_DESC_InvalidEndpoint = "Invalid endpoint"
public static let ERROR_DESC_Network = "Network problem"
public static let ERROR_DESC_InitializationNotFinished = "Initialization not finished"
public static let ERROR_DESC_InvalidJson = "Invalid Json response"
public static let ERROR_DESC_CouldNotCrop = "Could not crop image"
public static let ERROR_DESC_BarcodeCaptureFailed = "Barcode capture failed"
public static let ERROR_DESC_BarcodeCaptureTimedOut = "Barcode capture timed out"
public static let ERROR_DESC_BarcodeCaptureNotAuthorized = "Barcode capture is not authorized"
public static let ERROR_DESC_LiveFaceCaptureNotAuthorized = "Live face capture is not authorized"
public static let ERROR_DESC_CouldNotCreateConnectInstance = "Could not create connect Instance"
public static let ERROR_DESC_CouldNotUploadConnectImage = "Could not upload image to connect instance"
public static let ERROR_DESC_CouldNotUploadConnectBarcode = "Could not upload barcode to connect instance"
public static let ERROR_DESC_CouldNotGetConnectData = "Could not get connect image data"
public static let ERROR_DESC_CardWidthNotSet = "Card width not set"
public static let ERROR_DESC_CouldNotGetHealthCardData = "Could not get health card data"
public static let ERROR_DESC_CouldNotClassifyDocument = "Could not classify document"
public static let ERROR_DESC_LowResolutionImage = "Low resolution image"
public static let ERROR_DESC_BlurryImage = "Blurry image"
public static let ERROR_DESC_ImageWithGlare = "Image has glare"
public static let ERROR_DESC_CouldNotGetIPLivenessToken = "Could not get face liveness token"
public static let ERROR_DESC_NotALiveFace = "Not a live face"
public static let ERROR_DESC_CouldNotAccessLivenessData = "Could not access liveness data"
}
图片
public class Image {
public var image: UIImage? = nil
public var dpi: Int = 0 // dpi value of the captured image
public var error: AcuantError? = nil
public var isCorrectAspectRatio = false // If the captured image has the correct aspect ratio
public var aspectRatio: Float = 0.0 // Aspect ratio of the captured image
public var points: Array<CGPoint> = []
public var isPassport = false
public init(){}
}
AcuantCameraOptions
public class AcuantCameraOptions {
timeInMsPerDigit: Int = 900,
digitsToShow: Int = 2,
allowBox: Bool = true,
autoCapture: Bool = true,
hideNavigationBar: Bool = true,
bracketLengthInHorizontal: Int = 80,
bracketLengthInVertical: Int = 50,
defaultBracketMarginWidth: CGFloat = 0.5,
defaultBracketMarginHeight: CGFloat = 0.6,
colorHold: CGColor = UIColor.yellow.cgColor,
colorCapturing: CGColor = UIColor.green.cgColor,
colorBracketAlign: CGColor = UIColor.black.cgColor,
colorBracketCloser: CGColor = UIColor.red.cgColor,
colorBracketHold: CGColor = UIColor.yellow.cgColor,
colorBracketCapture: CGColor = UIColor.green.cgColor
}
IdOptions
public class IdOptions {
public var cardSide: CardSide = CardSide.Front
public var isHealthCard: Bool = false
public var isRetrying: Bool = false
public var authenticationSensitivity: AuthenticationSensitivity = AuthenticationSensitivity.Normal
}
常见问题
为什么在Apple App Store发布应用时会出现“不支持架构”错误?
所有框架都是 fat(多架构)二进制文件,包含 切片,用于 armv7、arm64、 i386 和 x86(64) CPU 架构。ARM 切片由物理 iOS 设备使用,而 i386 和 x86(64) 用于模拟器。
使用 lipo 命令来检查二进制中包含哪些切片
lipo -info <path to the file>
您也可以使用 lipo 命令来删除不需要的切片
lipo -remove i386 <Path to the file> -o <Output file path>
lipo -remove x86_64 <Path to the file> -o <Output file path>
为什么在存档样本应用程序时出现代码签名“AcuantCommon.framework”错误?
Acuant 为所有模拟器和设备所需的 CPU 架构提供支持。然而,在导出或发布到 Test Flight/App Store 时,应从框架二进制文件中删除模拟器架构(i386 和 x86(64))。
- 存档应用程序。
- 选择存档,然后点击 分发应用> App Store> 导出。
我该如何混淆我的 iOS 应用程序?
Acuant 不提供混淆工具,但是有一些第三方工具可供选择,包括 iXGuard 和 Arxan。
版权所有 2020 Acuant Inc. 保留所有权利。
本文档包含由 Acuant 和其各自许可方拥有的专有和保密信息及创意作品。未经 Acuant 事先书面明确许可,禁止以任何形式或任何手段全部或部分使用、复制、发布、分发、展示、修改或传输此类技术。除非 Acuant 以书面形式明确提供,否则拥有此信息不视为授予任何 Acuant 知识产权的许可或权利,无论是由于先占、暗示还是其他方式。
AssureID 和 i-Dentify 是 Acuant Inc. 的商标。本文档中提到的其他 Acuant 产品或服务名称或标志是 Acuant 的商标或注册商标。
所有 3M 商标是 Gemalto Inc. 的商标。
Windows 是微软公司的注册商标。
本文档可能提及 Acuant 之外的公司的一些产品、服务或公司名称,仅用于识别目的。此类名称通常被视为商标或服务标志。在实际中,如果 Acuant 明知有此类主张,该名称将首字母大写或全部大写。但是,您应联系有关公司以获取有关此类名称及其注册状态的更完整信息。
有关技术支持,请访问:https://support.acuant.com
Acuant Inc. 6080 Center Drive, Suite 850, Los Angeles, CA 90045