- 1.1 CoreImage的四种识别功能
- 1.2 边缘检测思路
- 1.3 用高精度边缘识别器识别特征
- 1.4 绘制边缘检测图层
- 1.5 Swift 版本
- 2.1 生成二维码
- 2.2 生成条码
- 2.3 读取二维码(二维码识别)
- 2.4 第三方框架
引言
为了提升用户体验,在OCR识别场景都将利用到边缘检测
涉及的权限
NSCameraUsageDescription
从CSDN下载Demo源码:https://download.csdn.net/download/u011018979/19260280
1、应用场景:为了提升用户体验,在OCR识别场景都将利用到边缘检测 2、原理:采用原生CoreImage框架下CIDetector可进行边缘检测,识别到边缘之后使用CAShapeLayer将边缘绘制并显示
3、原理文章:https://kunnan.blog.csdn.net/article/details/117367345
3、付费文章:iOS Document Scanner:矩形边缘识别(边缘检测 ) CIDetectorTypeRectangle
本文限时免费
I 、矩形边缘识别
1.1 CoreImage的四种识别功能
CoreImage
下CIDetector.h
自带了四种识别功能
/* 人脸识别 */ CORE_IMAGE_EXPORT NSString* const CIDetectorTypeFace NS_AVAILABLE(10_7, 5_0); /* 矩形边缘识别 */ CORE_IMAGE_EXPORT NSString* const CIDetectorTypeRectangle NS_AVAILABLE(10_10, 8_0); /* 二维码识别 */ CORE_IMAGE_EXPORT NSString* const CIDetectorTypeQRCode NS_AVAILABLE(10_10, 8_0); /* 文本识别 */ #if __OBJC2__ CORE_IMAGE_EXPORT NSString* const CIDetectorTypeText NS_AVAILABLE(10_11, 9_0);
1.2 边缘检测思路
采用原生CoreImage
框架下CIDetector
可进行边缘检测
[CIDetector detectorOfType:CIDetectorTypeRectangle context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}];
识别到边缘之后使用CAShapeLayer
将边缘绘制并显示
// 将图像空间的坐标系转换成uikit坐标系 TransformCIFeatureRect featureRect = [self transfromRealRectWithImageRect:imageRect topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight]; // 边缘识别路径 UIBezierPath *path = [UIBezierPath new]; [path moveToPoint:featureRect.topLeft]; [path addLineToPoint:featureRect.topRight]; [path addLineToPoint:featureRect.bottomRight]; [path addLineToPoint:featureRect.bottomLeft]; [path closePath]; // 背景遮罩路径 UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(-5, -5, self.frame.size.width + 10, self.frame.size.height + 10)]; [rectPath setUsesEvenOddFillRule:YES]; [rectPath appendPath:path]; _rectOverlay.path = rectPath.CGPath;
- 边缘识别器
// 高精度边缘识别器 - (CIDetector *)highAccuracyRectangleDetector { static CIDetector *detector = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { detector = [CIDetector detectorOfType:CIDetectorTypeRectangle context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyHigh}]; }); return detector; } // 低精度边缘识别器 - (CIDetector *)rectangleDetetor { static CIDetector *detector = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { detector = [CIDetector detectorOfType:CIDetectorTypeRectangle context:nil options:@{CIDetectorAccuracy : CIDetectorAccuracyLow,CIDetectorTracking : @(YES)}]; }); return detector; }
开启边缘检测
/// 开启边缘检测 @property (nonatomic,assign,getter=isBorderDetectionEnabled) BOOL enableBorderDetection; // 拍照视图 @property (nonatomic, strong) KNCameraCaptureView *captureCameraView; // 拍照视图 - (KNCameraCaptureView *)captureCameraView{ if (!_captureCameraView) { _captureCameraView = [[KNCameraCaptureView alloc] initWithFrame:CGRectMake(0, 0, SCREEN_WIDTH, 300)]; //打开边缘检测 [_captureCameraView setEnableBorderDetection:YES]; _captureCameraView.backgroundColor = kBlackColor; } return _captureCameraView; }
AVCaptureVideoDataOutputSampleBufferDelegate
#pragma mark - AVCaptureVideoDataOutputSampleBufferDelegate -(void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection { if (self.forceStop || _isStopped || _isCapturing || !CMSampleBufferIsValid(sampleBuffer)) return; CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer); CIImage *image = [CIImage imageWithCVPixelBuffer:pixelBuffer]; image = [self filteredImageUsingContrastFilterOnImage:image]; if (self.isBorderDetectionEnabled)//开启了边缘检测 { if (_borderDetectFrame)//开启了边缘识别 { // 用高精度边缘识别器 识别特征 NSArray <CIFeature *>*features = [[self highAccuracyRectangleDetector] featuresInImage:image]; // 选取特征列表中最大的矩形 _borderDetectLastRectangleFeature = [self biggestRectangleInRectangles:features]; _borderDetectFrame = NO; } if (_borderDetectLastRectangleFeature) { _imageDedectionConfidence += .5; // image = [self drawHighlightOverlayForPoints:image topLeft:_borderDetectLastRectangleFeature.topLeft topRight:_borderDetectLastRectangleFeature.topRight bottomLeft:_borderDetectLastRectangleFeature.bottomLeft bottomRight:_borderDetectLastRectangleFeature.bottomRight]; // draw border layer if (rectangleDetectionConfidenceHighEnough(_imageDedectionConfidence)) { [self drawBorderDetectRectWithImageRect:image.extent topLeft:_borderDetectLastRectangleFeature.topLeft topRight:_borderDetectLastRectangleFeature.topRight bottomLeft:_borderDetectLastRectangleFeature.bottomLeft bottomRight:_borderDetectLastRectangleFeature.bottomRight]; } } else { _imageDedectionConfidence = 0.0f; if (_rectOverlay) { _rectOverlay.path = nil; } } } if (self.context && _coreImageContext) { // 将捕获到的图片绘制进_coreImageContext [_coreImageContext drawImage:image inRect:self.bounds fromRect:image.extent]; [self.context presentRenderbuffer:GL_RENDERBUFFER]; [_glkView setNeedsDisplay]; } }
1.4 绘制边缘检测图层
// 绘制边缘检测图层 - (void)drawBorderDetectRectWithImageRect:(CGRect)imageRect topLeft:(CGPoint)topLeft topRight:(CGPoint)topRight bottomLeft:(CGPoint)bottomLeft bottomRight:(CGPoint)bottomRight { if (!_rectOverlay) { _rectOverlay = [CAShapeLayer layer]; _rectOverlay.fillRule = kCAFillRuleEvenOdd; _rectOverlay.fillColor = [UIColor colorWithRed:73/255.0 green:130/255.0 blue:180/255.0 alpha:0.4].CGColor; _rectOverlay.strokeColor = [UIColor whiteColor].CGColor; _rectOverlay.lineWidth = 5.0f; } if (!_rectOverlay.superlayer) { self.layer.masksToBounds = YES; [self.layer addSublayer:_rectOverlay]; } // 将图像空间的坐标系转换成uikit坐标系 TransformCIFeatureRect featureRect = [self transfromRealRectWithImageRect:imageRect topLeft:topLeft topRight:topRight bottomLeft:bottomLeft bottomRight:bottomRight]; // 边缘识别路径 UIBezierPath *path = [UIBezierPath new]; [path moveToPoint:featureRect.topLeft]; [path addLineToPoint:featureRect.topRight]; [path addLineToPoint:featureRect.bottomRight]; [path addLineToPoint:featureRect.bottomLeft]; [path closePath]; // 背景遮罩路径 UIBezierPath *rectPath = [UIBezierPath bezierPathWithRect:CGRectMake(-5, -5, self.frame.size.width + 10, self.frame.size.height + 10)]; [rectPath setUsesEvenOddFillRule:YES]; [rectPath appendPath:path]; _rectOverlay.path = rectPath.CGPath; }
1.5 Swift 版本
- swift
// import CoreImage import UIKit public final class CIImageRectangleDetector: ImageRectangleDetector { public func detect(image: UIImage, completion: @escaping Completion) { DispatchQueue.global().async { guard let ciImage = CIImage(image: image) else { return } guard let detector = CIDetector(ofType: CIDetectorTypeRectangle, context: nil, options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) else { return } let results = detector.features(in: ciImage) let sortedBySize = results.sorted { $0.bounds.area > $1.bounds.area } if let feature = sortedBySize.first as? CIRectangleFeature { let size = ciImage.extent.size let points = [feature.topLeft, feature.topRight, feature.bottomRight, feature.bottomLeft] let normalized = points.map { $0.scaledRelative(size: size) } let quad = Quad(clockwise: normalized) DispatchQueue.main.async { completion(quad) } } else { DispatchQueue.main.async { completion(nil) } } } } }
II、二维码
从iOS7开始集成了二维码的生成和读取功能
2.1 生成二维码
步骤;
1.导入CoreImage框架 2.通过滤镜CIFilter生成二维码
二维码的内容(传统的条形码只能放数字):纯文本、名片、URL
/** 生成QR二维码 @param text 字符串 @param size 二维码大小 @return 返回二维码图像 */ + (UIImage*)createQRWithString:(NSString*)text QRSize:(CGSize)size; /** 生成QR二维码 @param text 字符串 @param size 大小 @param qrColor 二维码前景色 @param bkColor 二维码背景色 @return 二维码图像 */ + (UIImage*)createQRWithString:(NSString*)text QRSize:(CGSize)size QRColor:(UIColor*)qrColor bkColor:(UIColor*)bkColor;
- 基本用法
#pragma mark - QRCodeGenerator + (CIImage *)createQRForString:(NSString *)qrString { NSData *stringData = [qrString dataUsingEncoding:NSUTF8StringEncoding]; // 创建filter CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; // 设置内容和纠错级别 [qrFilter setValue:stringData forKey:@"inputMessage"]; [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"]; // 返回CIImage return qrFilter.outputImage; }
支持 二维码大小设置
+ (UIImage*)createQRWithString:(NSString*)text QRSize:(CGSize)size { NSData *stringData = [text dataUsingEncoding: NSUTF8StringEncoding]; //生成 CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; [qrFilter setValue:stringData forKey:@"inputMessage"]; [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"]; CIImage *qrImage = qrFilter.outputImage; //绘制 CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent]; UIGraphicsBeginImageContext(size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationNone); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage); UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGImageRelease(cgImage); return codeImage; }
支持二维码前/背景色设置
+ (UIImage*)createQRWithString:(NSString*)text QRSize:(CGSize)size QRColor:(UIColor*)qrColor bkColor:(UIColor*)bkColor { NSData *stringData = [text dataUsingEncoding: NSUTF8StringEncoding]; //生成 CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"]; [qrFilter setValue:stringData forKey:@"inputMessage"]; [qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"]; //上色 CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor" keysAndValues: @"inputImage",qrFilter.outputImage, @"inputColor0",[CIColor colorWithCGColor:qrColor.CGColor], @"inputColor1",[CIColor colorWithCGColor:bkColor.CGColor], nil]; CIImage *qrImage = colorFilter.outputImage; //绘制 CGImageRef cgImage = [[CIContext contextWithOptions:nil] createCGImage:qrImage fromRect:qrImage.extent]; UIGraphicsBeginImageContext(size); CGContextRef context = UIGraphicsGetCurrentContext(); CGContextSetInterpolationQuality(context, kCGInterpolationNone); CGContextScaleCTM(context, 1.0, -1.0); CGContextDrawImage(context, CGContextGetClipBoundingBox(context), cgImage); UIImage *codeImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); CGImageRelease(cgImage); return codeImage; }
2.2 生成条码
利用CIFilter生成条码
/** 生成条形码 @param text 字符串 @param size 大小 @return 返回条码图像 */ + (UIImage*)createBarCodeWithString:(NSString*)text QRSize:(CGSize)size { NSData *data = [text dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:false]; CIFilter *filter = [CIFilter filterWithName:@"CICode128BarcodeGenerator"]; [filter setValue:data forKey:@"inputMessage"]; CIImage *barcodeImage = [filter outputImage]; // 消除模糊 CGFloat scaleX = size.width / barcodeImage.extent.size.width; // extent 返回图片的frame CGFloat scaleY = size.height / barcodeImage.extent.size.height; CIImage *transformedImage = [barcodeImage imageByApplyingTransform:CGAffineTransformScale(CGAffineTransformIdentity, scaleX, scaleY)]; return [UIImage imageWithCIImage:transformedImage]; }
2.3 读取二维码(二维码识别)
需要导入AVFoundation框架,利用摄像头识别二维码中的内容(模拟器不行)
1.输入(摄像头) 2.由会话将摄像头采集到的二维码图像转换成字符串数据 3.输出(数据) 4.由预览图层显示扫描场景
- detectorOfType:CIDetectorTypeQRCode
// 声明一个 CIDetector,并设定识别类型 CIDetectorTypeQRCode CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}]; // 取得识别结果 NSArray *features = [detector featuresInImage:[CIImage imageWithCGImage:image.CGImage]];
- 识别条码图片
#pragma mark --识别条码图片 + (void)recognizeImage:(UIImage*)image success:(void(^)(NSArray<LBXScanResult*> *array))block; { if (!image) { block(nil); return; } if (@available(iOS 8.0, *)) { CIImage * cimg = [CIImage imageWithCGImage:image.CGImage]; if (!cimg) { block(nil); return; } NSArray *features = nil; @try { CIDetector*detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:nil options:@{ CIDetectorAccuracy : CIDetectorAccuracyHigh }]; features = [detector featuresInImage:cimg]; } @catch (NSException *exception) { block(nil); return; } @finally { } NSMutableArray<LBXScanResult*> *mutableArray = [[NSMutableArray alloc]initWithCapacity:1]; for (int index = 0; index < [features count]; index ++) { CIQRCodeFeature *feature = [features objectAtIndex:index]; NSString *scannedResult = feature.messageString; LBXScanResult *item = [[LBXScanResult alloc]init]; item.strScanned = scannedResult; item.strBarCodeType = CIDetectorTypeQRCode; item.imgScanned = image; [mutableArray addObject:item]; } if (block) { block(mutableArray); } }else{ if (block) { LBXScanResult *result = [[LBXScanResult alloc]init]; result.strScanned = @"只支持ios8.0之后系统"; block(@[result]); } } }
2.4 第三方框架
LBXZBarSDK
pod 'LBXScan/LBXNative','~> 2.5.1' #系统原生API封装库 pod 'LBXScan/LBXZXing','~> 2.5.1' pod 'LBXScan/UI','~> 2.5.1' #pod 'LBXZBarSDK','~> 1.3'# 删除UIWebView,相机采集分辨率设置高分辨率
iOS安全之UIWebView 被拒的解决方案:用更安全的WKWebView替代UIWebView| 蓄力计划