| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693 |
- //
- // LBXScanWrapper.swift
- // swiftScan
- //
- // Created by lbxia on 15/12/10.
- // Copyright © 2015年 xialibing. All rights reserved.
- //
- import UIKit
- import AVFoundation
- public struct LBXScanResult {
-
- //码内容
- public var strScanned:String? = ""
- //扫描图像
- public var imgScanned:UIImage?
- //码的类型
- public var strBarCodeType:String? = ""
-
- //码在图像中的位置
- public var arrayCorner:[AnyObject]?
-
- public init(str:String?,img:UIImage?,barCodeType:String?,corner:[AnyObject]?)
- {
- self.strScanned = str
- self.imgScanned = img
- self.strBarCodeType = barCodeType
- self.arrayCorner = corner
- }
- }
- open class LBXScanWrapper: NSObject,AVCaptureMetadataOutputObjectsDelegate {
-
- let device = AVCaptureDevice.default(for: AVMediaType.video)
- var input:AVCaptureDeviceInput?
- var output:AVCaptureMetadataOutput
-
- let session = AVCaptureSession()
- var previewLayer:AVCaptureVideoPreviewLayer?
- var stillImageOutput:AVCaptureStillImageOutput?
-
- //存储返回结果
- var arrayResult:[LBXScanResult] = [];
-
- //扫码结果返回block
- var successBlock:([LBXScanResult]) -> Void
-
- //是否需要拍照
- var isNeedCaptureImage:Bool
-
- //当前扫码结果是否处理
- var isNeedScanResult:Bool = true
-
- /**
- 初始化设备
- - parameter videoPreView: 视频显示UIView
- - parameter objType: 识别码的类型,缺省值 QR二维码
- - parameter isCaptureImg: 识别后是否采集当前照片
- - parameter cropRect: 识别区域
- - parameter success: 返回识别信息
- - returns:
- */
- init( videoPreView:UIView,objType:[AVMetadataObject.ObjectType] = [(AVMetadataObject.ObjectType.qr as NSString) as AVMetadataObject.ObjectType],isCaptureImg:Bool,cropRect:CGRect=CGRect.zero,success:@escaping ( ([LBXScanResult]) -> Void) )
- {
- do{
- input = try AVCaptureDeviceInput(device: device!)
- }
- catch let error as NSError {
- print("AVCaptureDeviceInput(): \(error)")
- }
-
- successBlock = success
-
- // Output
- output = AVCaptureMetadataOutput()
-
- isNeedCaptureImage = isCaptureImg
-
- stillImageOutput = AVCaptureStillImageOutput();
-
- super.init()
-
- if device == nil || input == nil {
- return
- }
-
- if session.canAddInput(input!) {
- session.addInput(input!)
- }
-
- if session.canAddOutput(output) {
- session.addOutput(output)
- }
-
- if session.canAddOutput(stillImageOutput!) {
- session.addOutput(stillImageOutput!)
- }
-
- let outputSettings:Dictionary = [AVVideoCodecJPEG:AVVideoCodecKey]
- stillImageOutput?.outputSettings = outputSettings
-
- session.sessionPreset = AVCaptureSession.Preset.high
-
- //参数设置
- output.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
-
- output.metadataObjectTypes = objType
-
- // output.metadataObjectTypes = [AVMetadataObjectTypeQRCode,AVMetadataObjectTypeEAN13Code, AVMetadataObjectTypeEAN8Code, AVMetadataObjectTypeCode128Code]
-
- if !cropRect.equalTo(CGRect.zero)
- {
- //启动相机后,直接修改该参数无效
- output.rectOfInterest = cropRect
- }
-
- previewLayer = AVCaptureVideoPreviewLayer(session: session)
- previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
-
- var frame:CGRect = videoPreView.frame
- frame.origin = CGPoint.zero
- previewLayer?.frame = frame
-
- videoPreView.layer .insertSublayer(previewLayer!, at: 0)
-
- if ( device!.isFocusPointOfInterestSupported && device!.isFocusModeSupported(AVCaptureDevice.FocusMode.continuousAutoFocus) )
- {
- do
- {
- try input?.device.lockForConfiguration()
-
- input?.device.focusMode = AVCaptureDevice.FocusMode.continuousAutoFocus
-
- input?.device.unlockForConfiguration()
- }
- catch let error as NSError {
- print("device.lockForConfiguration(): \(error)")
-
- }
- }
- }
-
- public func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
- captureOutput(output, didOutputMetadataObjects: metadataObjects, from: connection)
- }
-
- func start()
- {
- if !session.isRunning
- {
- isNeedScanResult = true
- session.startRunning()
- }
- }
- func stop()
- {
- if session.isRunning
- {
- isNeedScanResult = false
- session.stopRunning()
- }
- }
-
- open func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
-
- if !isNeedScanResult
- {
- //上一帧处理中
- return
- }
-
- isNeedScanResult = false
-
- arrayResult.removeAll()
-
- //识别扫码类型
- for current:Any in metadataObjects
- {
- if (current as AnyObject).isKind(of: AVMetadataMachineReadableCodeObject.self)
- {
- let code = current as! AVMetadataMachineReadableCodeObject
-
- //码类型
- let codeType = code.type
- // print("code type:%@",codeType)
- //码内容
- let codeContent = code.stringValue
- // print("code string:%@",codeContent)
-
- //4个字典,分别 左上角-右上角-右下角-左下角的 坐标百分百,可以使用这个比例抠出码的图像
- // let arrayRatio = code.corners
-
- arrayResult.append(LBXScanResult(str: codeContent, img: UIImage(), barCodeType: codeType.rawValue, corner: code.corners as [AnyObject]?))
- }
- }
-
- if arrayResult.count > 0
- {
- if isNeedCaptureImage
- {
- captureImage()
- }
- else
- {
- stop()
- successBlock(arrayResult)
- }
-
- }
- else
- {
- isNeedScanResult = true
- }
-
- }
-
- //MARK: ----拍照
- open func captureImage()
- {
- let stillImageConnection:AVCaptureConnection? = connectionWithMediaType(mediaType: AVMediaType.video as AVMediaType, connections: (stillImageOutput?.connections)! as [AnyObject])
-
-
- stillImageOutput?.captureStillImageAsynchronously(from: stillImageConnection!, completionHandler: { (imageDataSampleBuffer, error) -> Void in
-
- self.stop()
- if imageDataSampleBuffer != nil
- {
- let imageData: Data = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(imageDataSampleBuffer!)!
- let scanImg:UIImage? = UIImage(data: imageData)
-
-
- for idx in 0...self.arrayResult.count-1
- {
- self.arrayResult[idx].imgScanned = scanImg
- }
- }
-
- self.successBlock(self.arrayResult)
-
- })
- }
-
- open func connectionWithMediaType(mediaType:AVMediaType, connections:[AnyObject]) -> AVCaptureConnection?
- {
- for connection:AnyObject in connections
- {
- let connectionTmp:AVCaptureConnection = connection as! AVCaptureConnection
-
- for port:Any in connectionTmp.inputPorts
- {
- if (port as AnyObject).isKind(of: AVCaptureInput.Port.self)
- {
- let portTmp:AVCaptureInput.Port = port as! AVCaptureInput.Port
- if portTmp.mediaType == mediaType
- {
- return connectionTmp
- }
- }
- }
- }
- return nil
- }
-
-
- //MARK:切换识别区域
- open func changeScanRect(cropRect:CGRect)
- {
- //待测试,不知道是否有效
- stop()
- output.rectOfInterest = cropRect
- start()
- }
-
- //MARK: 切换识别码的类型
- open func changeScanType(objType:[AVMetadataObject.ObjectType])
- {
- //待测试中途修改是否有效
- output.metadataObjectTypes = objType
- }
-
- open func isGetFlash()->Bool
- {
- if (device != nil && device!.hasFlash && device!.hasTorch)
- {
- return true
- }
- return false
- }
-
- /**
- 打开或关闭闪关灯
- - parameter torch: true:打开闪关灯 false:关闭闪光灯
- */
- open func setTorch(torch:Bool)
- {
- if isGetFlash()
- {
- do
- {
- try input?.device.lockForConfiguration()
-
- input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
-
- input?.device.unlockForConfiguration()
- }
- catch let error as NSError {
- print("device.lockForConfiguration(): \(error)")
-
- }
- }
-
- }
-
-
- /**
- ------闪光灯打开或关闭
- */
- open func changeTorch()
- {
- if isGetFlash()
- {
- do
- {
- try input?.device.lockForConfiguration()
-
- var torch = false
-
- if input?.device.torchMode == AVCaptureDevice.TorchMode.on
- {
- torch = false
- }
- else if input?.device.torchMode == AVCaptureDevice.TorchMode.off
- {
- torch = true
- }
-
- input?.device.torchMode = torch ? AVCaptureDevice.TorchMode.on : AVCaptureDevice.TorchMode.off
-
- input?.device.unlockForConfiguration()
- }
- catch let error as NSError {
- print("device.lockForConfiguration(): \(error)")
-
- }
- }
- }
-
- //MARK: ------获取系统默认支持的码的类型
- static func defaultMetaDataObjectTypes() ->[AVMetadataObject.ObjectType]
- {
- var types =
- [AVMetadataObject.ObjectType.qr,
- AVMetadataObject.ObjectType.upce,
- AVMetadataObject.ObjectType.code39,
- AVMetadataObject.ObjectType.code39Mod43,
- AVMetadataObject.ObjectType.ean13,
- AVMetadataObject.ObjectType.ean8,
- AVMetadataObject.ObjectType.code93,
- AVMetadataObject.ObjectType.code128,
- AVMetadataObject.ObjectType.pdf417,
- AVMetadataObject.ObjectType.aztec
- ]
- //if #available(iOS 8.0, *)
-
- types.append(AVMetadataObject.ObjectType.interleaved2of5)
- types.append(AVMetadataObject.ObjectType.itf14)
- types.append(AVMetadataObject.ObjectType.dataMatrix)
- return types as [AVMetadataObject.ObjectType]
- }
-
-
- static func isSysIos8Later()->Bool
- {
- // return floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_8_0
-
- if #available(iOS 8, *) {
- return true;
- }
- return false
- }
-
- /**
- 识别二维码码图像
-
- - parameter image: 二维码图像
-
- - returns: 返回识别结果
- */
- static public func recognizeQRImage(image:UIImage) ->[LBXScanResult]
- {
- var returnResult:[LBXScanResult]=[]
-
- if LBXScanWrapper.isSysIos8Later()
- {
- //if #available(iOS 8.0, *)
-
- let detector:CIDetector = CIDetector(ofType: CIDetectorTypeQRCode, context: nil, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])!
-
- let img = CIImage(cgImage: (image.cgImage)!)
-
- let features:[CIFeature]? = detector.features(in: img, options: [CIDetectorAccuracy:CIDetectorAccuracyHigh])
-
- if( features != nil && (features?.count)! > 0)
- {
- let feature = features![0]
-
- if feature.isKind(of: CIQRCodeFeature.self)
- {
- let featureTmp:CIQRCodeFeature = feature as! CIQRCodeFeature
-
- let scanResult = featureTmp.messageString
-
-
- let result = LBXScanResult(str: scanResult, img: image, barCodeType: AVMetadataObject.ObjectType.qr.rawValue,corner: nil)
-
- returnResult.append(result)
- }
- }
-
- }
-
- return returnResult
- }
-
-
- //MARK: -- - 生成二维码,背景色及二维码颜色设置
- static public func createCode( codeType:String, codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage?
- {
- //if #available(iOS 8.0, *)
-
- let stringData = codeString.data(using: String.Encoding.utf8)
-
-
- //系统自带能生成的码
- // CIAztecCodeGenerator
- // CICode128BarcodeGenerator
- // CIPDF417BarcodeGenerator
- // CIQRCodeGenerator
- let qrFilter = CIFilter(name: codeType)
-
-
- qrFilter?.setValue(stringData, forKey: "inputMessage")
-
- qrFilter?.setValue("H", forKey: "inputCorrectionLevel")
-
-
- //上色
- let colorFilter = CIFilter(name: "CIFalseColor", parameters: ["inputImage":qrFilter!.outputImage!,"inputColor0":CIColor(cgColor: qrColor.cgColor),"inputColor1":CIColor(cgColor: bkColor.cgColor)])
-
-
- let qrImage = colorFilter!.outputImage!;
-
- //绘制
- let cgImage = CIContext().createCGImage(qrImage, from: qrImage.extent)!
-
-
- UIGraphicsBeginImageContext(size);
- let context = UIGraphicsGetCurrentContext()!;
- context.interpolationQuality = CGInterpolationQuality.none;
- context.scaleBy(x: 1.0, y: -1.0);
- context.draw(cgImage, in: context.boundingBoxOfClipPath)
- let codeImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- return codeImage
-
- }
-
- static public func createCode128( codeString:String, size:CGSize,qrColor:UIColor,bkColor:UIColor )->UIImage?
- {
- let stringData = codeString.data(using: String.Encoding.utf8)
-
-
- //系统自带能生成的码
- // CIAztecCodeGenerator 二维码
- // CICode128BarcodeGenerator 条形码
- // CIPDF417BarcodeGenerator
- // CIQRCodeGenerator 二维码
- let qrFilter = CIFilter(name: "CICode128BarcodeGenerator")
- qrFilter?.setDefaults()
- qrFilter?.setValue(stringData, forKey: "inputMessage")
-
-
-
- let outputImage:CIImage? = qrFilter?.outputImage
- let context = CIContext()
- let cgImage = context.createCGImage(outputImage!, from: outputImage!.extent)
-
- let image = UIImage(cgImage: cgImage!, scale: 1.0, orientation: UIImage.Orientation.up)
-
-
- // Resize without interpolating
- let scaleRate:CGFloat = 20.0
- let resized = resizeImage(image: image, quality: CGInterpolationQuality.none, rate: scaleRate)
-
- return resized;
- }
-
-
- //MARK:根据扫描结果,获取图像中得二维码区域图像(如果相机拍摄角度故意很倾斜,获取的图像效果很差)
- static func getConcreteCodeImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->UIImage?
- {
- let rect:CGRect = getConcreteCodeRectFromImage(srcCodeImage: srcCodeImage, codeResult: codeResult)
-
- if rect.isEmpty
- {
- return nil
- }
-
- let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect)
-
- if img != nil
- {
- let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right)
- return imgRotation
- }
- return nil
- }
- //根据二维码的区域截取二维码区域图像
- static public func getConcreteCodeImage(srcCodeImage:UIImage,rect:CGRect)->UIImage?
- {
- if rect.isEmpty
- {
- return nil
- }
-
- let img = imageByCroppingWithStyle(srcImg: srcCodeImage, rect: rect)
-
- if img != nil
- {
- let imgRotation = imageRotation(image: img!, orientation: UIImage.Orientation.right)
- return imgRotation
- }
- return nil
- }
-
- //获取二维码的图像区域
- static public func getConcreteCodeRectFromImage(srcCodeImage:UIImage,codeResult:LBXScanResult)->CGRect
- {
- if (codeResult.arrayCorner == nil || (codeResult.arrayCorner?.count)! < 4 )
- {
- return CGRect.zero
- }
-
- let corner:[[String:Float]] = codeResult.arrayCorner as! [[String:Float]]
-
- let dicTopLeft = corner[0]
- let dicTopRight = corner[1]
- let dicBottomRight = corner[2]
- let dicBottomLeft = corner[3]
-
- let xLeftTopRatio:Float = dicTopLeft["X"]!
- let yLeftTopRatio:Float = dicTopLeft["Y"]!
-
- let xRightTopRatio:Float = dicTopRight["X"]!
- let yRightTopRatio:Float = dicTopRight["Y"]!
-
- let xBottomRightRatio:Float = dicBottomRight["X"]!
- let yBottomRightRatio:Float = dicBottomRight["Y"]!
-
- let xLeftBottomRatio:Float = dicBottomLeft["X"]!
- let yLeftBottomRatio:Float = dicBottomLeft["Y"]!
-
- //由于截图只能矩形,所以截图不规则四边形的最大外围
- let xMinLeft = CGFloat( min(xLeftTopRatio, xLeftBottomRatio) )
- let xMaxRight = CGFloat( max(xRightTopRatio, xBottomRightRatio) )
-
- let yMinTop = CGFloat( min(yLeftTopRatio, yRightTopRatio) )
- let yMaxBottom = CGFloat ( max(yLeftBottomRatio, yBottomRightRatio) )
-
- let imgW = srcCodeImage.size.width
- let imgH = srcCodeImage.size.height
-
- //宽高反过来计算
- let rect = CGRect(x: xMinLeft * imgH, y: yMinTop*imgW, width: (xMaxRight-xMinLeft)*imgH, height: (yMaxBottom-yMinTop)*imgW)
- return rect
- }
-
- //MARK: ----图像处理
-
- /**
- @brief 图像中间加logo图片
- @param srcImg 原图像
- @param LogoImage logo图像
- @param logoSize logo图像尺寸
- @return 加Logo的图像
- */
- static public func addImageLogo(srcImg:UIImage,logoImg:UIImage,logoSize:CGSize )->UIImage
- {
- UIGraphicsBeginImageContext(srcImg.size);
- srcImg.draw(in: CGRect(x: 0, y: 0, width: srcImg.size.width, height: srcImg.size.height))
- let rect = CGRect(x: srcImg.size.width/2 - logoSize.width/2, y: srcImg.size.height/2-logoSize.height/2, width:logoSize.width, height: logoSize.height);
- logoImg.draw(in: rect)
- let resultingImage = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
- return resultingImage!;
- }
-
- //图像缩放
- static func resizeImage(image:UIImage,quality:CGInterpolationQuality,rate:CGFloat)->UIImage?
- {
- var resized:UIImage?;
- let width = image.size.width * rate;
- let height = image.size.height * rate;
-
- UIGraphicsBeginImageContext(CGSize(width: width, height: height));
- let context = UIGraphicsGetCurrentContext();
- context!.interpolationQuality = quality;
- image.draw(in: CGRect(x: 0, y: 0, width: width, height: height))
-
- resized = UIGraphicsGetImageFromCurrentImageContext();
- UIGraphicsEndImageContext();
-
- return resized;
- }
-
-
- //图像裁剪
- static func imageByCroppingWithStyle(srcImg:UIImage,rect:CGRect)->UIImage?
- {
- let imageRef = srcImg.cgImage
- let imagePartRef = imageRef!.cropping(to: rect)
- let cropImage = UIImage(cgImage: imagePartRef!)
-
- return cropImage
- }
- //图像旋转
- static func imageRotation(image:UIImage,orientation:UIImage.Orientation)->UIImage
- {
- var rotate:Double = 0.0;
- var rect:CGRect;
- var translateX:CGFloat = 0.0;
- var translateY:CGFloat = 0.0;
- var scaleX:CGFloat = 1.0;
- var scaleY:CGFloat = 1.0;
-
- switch (orientation) {
- case UIImage.Orientation.left:
- rotate = .pi/2;
- rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width);
- translateX = 0;
- translateY = -rect.size.width;
- scaleY = rect.size.width/rect.size.height;
- scaleX = rect.size.height/rect.size.width;
- break;
- case UIImage.Orientation.right:
- rotate = 3 * .pi/2;
- rect = CGRect(x: 0, y: 0, width: image.size.height, height: image.size.width);
- translateX = -rect.size.height;
- translateY = 0;
- scaleY = rect.size.width/rect.size.height;
- scaleX = rect.size.height/rect.size.width;
- break;
- case UIImage.Orientation.down:
- rotate = .pi;
- rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height);
- translateX = -rect.size.width;
- translateY = -rect.size.height;
- break;
- default:
- rotate = 0.0;
- rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height);
- translateX = 0;
- translateY = 0;
- break;
- }
-
- UIGraphicsBeginImageContext(rect.size);
- let context = UIGraphicsGetCurrentContext()!;
- //做CTM变换
- context.translateBy(x: 0.0, y: rect.size.height);
- context.scaleBy(x: 1.0, y: -1.0);
- context.rotate(by: CGFloat(rotate));
- context.translateBy(x: translateX, y: translateY);
-
- context.scaleBy(x: scaleX, y: scaleY);
- //绘制图片
- context.draw(image.cgImage!, in: CGRect(x: 0, y: 0, width: rect.size.width, height: rect.size.height))
- let newPic = UIGraphicsGetImageFromCurrentImageContext();
-
- return newPic!;
- }
-
- deinit
- {
- // print("LBXScanWrapper deinit")
- }
- }
|