日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

iOS二维码识别/二维码生成

發布時間:2024/1/1 编程问答 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 iOS二维码识别/二维码生成 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • 前言
      • 掃一掃識別二維碼圖片
      • 長按圖片識別二維碼圖片
      • 生成二維碼圖片
      • 代碼完善
        • 識別二維碼圖片優化
      • 小結

前言

最近在做一個關于二維碼的組件,已發布,現總結下。
開發的APP所需支持的最低版本為7.0,最初的方案為掃描使用蘋果自帶的API實現掃一掃的功能、使用ZXing識別從相冊或別人轉發的二維碼圖片。但發現ZXing識別從相冊中來的圖片性能很差,很多圖片識別不了,且耗時較長,遂使用ZBar來實現識別從相冊或別人轉發的二維碼圖片。
這個組件重要有三個功能,掃一掃識別二維碼圖片、長按圖片識別二維碼圖片和生成二維碼圖片。

掃一掃識別二維碼圖片

- (void)initCapture {AVCaptureDevice* inputDevice =[AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];[inputDevice lockForConfiguration:nil];if ([inputDevice hasTorch]) {inputDevice.torchMode = AVCaptureTorchModeAuto;}[inputDevice unlockForConfiguration];AVCaptureDeviceInput *captureInput =[AVCaptureDeviceInput deviceInputWithDevice:inputDevice error:nil];if (!captureInput) {if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {UIAlertController *alterVC = [UIAlertController alertControllerWithTitle:@"系統提示" message:@"您已關閉相機使用權限,請至手機“設置->隱私->相機”中打開" preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:nil];[alterVC addAction:confirmAction];[self presentViewController:alterVC animated:YES completion:nil];} else {UIAlertController *alterVC = [UIAlertController alertControllerWithTitle:@"系統提示" message:@"未能找到相機設備" preferredStyle:UIAlertControllerStyleAlert];UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:nil];[alterVC addAction:confirmAction];[self presentViewController:alterVC animated:YES completion:nil];}return;}AVCaptureMetadataOutput *captureOutput = [[AVCaptureMetadataOutput alloc] init];[captureOutput setMetadataObjectsDelegate:self queue:_queue];self.captureOutput = captureOutput;self.captureSession = [[AVCaptureSession alloc] init];[self.captureSession addInput:captureInput];[self.captureSession addOutput:captureOutput];CGFloat w = 1920.f;CGFloat h = 1080.f;if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1920x1080]) {self.captureSession.sessionPreset = AVCaptureSessionPreset1920x1080;} else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {self.captureSession.sessionPreset = AVCaptureSessionPreset1280x720;w = 1280.f;h = 720.f;} else if ([self.captureSession canSetSessionPreset:AVCaptureSessionPreset640x480]) {self.captureSession.sessionPreset = AVCaptureSessionPreset640x480;w = 960.f;h = 540.f;}captureOutput.metadataObjectTypes = [captureOutput availableMetadataObjectTypes];CGRect bounds = [[UIScreen mainScreen] bounds];if (!self.prevLayer) {self.prevLayer = [AVCaptureVideoPreviewLayer layerWithSession:self.captureSession];}self.prevLayer.frame = bounds;self.prevLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;[self.view.layer insertSublayer:self.prevLayer atIndex:0];// 計算rectOfInterestCGFloat p1 = bounds.size.height/bounds.size.width;CGFloat p2 = w/h;CGRect cropRect = CGRectMake(CGRectGetMinX(_cropRect) - kQRReaderScanExpandWidth, CGRectGetMinY(_cropRect) - kQRReaderScanExpandHeight, CGRectGetWidth(_cropRect) + 2*kQRReaderScanExpandWidth, CGRectGetHeight(_cropRect) + 2*kQRReaderScanExpandHeight);if (fabs(p1 - p2) < 0.00001) {captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y /bounds.size.height,cropRect.origin.x/bounds.size.width,cropRect.size.height/bounds.size.height,cropRect.size.width/bounds.size.width);} else if (p1 < p2) {//實際圖像被截取一段高CGFloat fixHeight = bounds.size.width * w / h;CGFloat fixPadding = (fixHeight - bounds.size.height)/2;captureOutput.rectOfInterest = CGRectMake((cropRect.origin.y + fixPadding)/fixHeight,cropRect.origin.x/bounds.size.width,cropRect.size.height/fixHeight,cropRect.size.width/bounds.size.width);} else {CGFloat fixWidth = bounds.size.height * h / w;CGFloat fixPadding = (fixWidth - bounds.size.width)/2;captureOutput.rectOfInterest = CGRectMake(cropRect.origin.y/bounds.size.height,(cropRect.origin.x + fixPadding)/fixWidth,cropRect.size.height/bounds.size.height,cropRect.size.width/fixWidth);} }

長按圖片識別二維碼圖片

識別圖片使用的是ZBar,最初的方案為ZXing,因為ZXing有人在維護,但ZXing識別相冊中的二維碼圖片或本地的圖片,有些圖片根本就識別不出來,且耗時較長,所以改為使用ZBar。在網上找到一篇文章再見ZXing 使用系統原生代碼處理QRCode,實測發現在iOS9,iphone4s上傳回來的數組為空。代碼如下:

//decode- (NSString *)decodeQRImageWith:(UIImage*)aImage {NSString *qrResult = nil;//iOS8及以上可以使用系統自帶的識別二維碼圖片接口,但此api有問題,在一些機型上detector為nil。// if (iOS8_OR_LATER) {// CIContext *context = [CIContext contextWithOptions:nil];// CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];// CIImage *image = [CIImage imageWithCGImage:aImage.CGImage];// NSArray *features = [detector featuresInImage:image];// CIQRCodeFeature *feature = [features firstObject];//// qrResult = feature.messageString;// } else {ZBarReaderController* read = [ZBarReaderController new];CGImageRef cgImageRef = aImage.CGImage;ZBarSymbol* symbol = nil;for(symbol in [read scanImage:cgImageRef]) break;qrResult = symbol.data ;return qrResult;}

無圖無真相:


detector的值為nil,也就是說

CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCode context:context options:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];

CIDetector的初始化方法無效。推測是蘋果API的問題。

生成二維碼圖片

在iOS8及以上版本使用蘋果的API生成二維碼圖片,代碼如下:

- (UIImage *)encodeQRImageWithContent:(NSString *)content size:(CGSize)size {UIImage *codeImage = nil;if (iOS8_OR_LATER) {NSData *stringData = [content dataUsingEncoding: NSUTF8StringEncoding];//生成CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];[qrFilter setValue:stringData forKey:@"inputMessage"];[qrFilter setValue:@"M" forKey:@"inputCorrectionLevel"];UIColor *onColor = [UIColor blackColor];UIColor *offColor = [UIColor whiteColor];//上色CIFilter *colorFilter = [CIFilter filterWithName:@"CIFalseColor"keysAndValues:@"inputImage",qrFilter.outputImage,@"inputColor0",[CIColor colorWithCGColor:onColor.CGColor],@"inputColor1",[CIColor colorWithCGColor:offColor.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);codeImage = UIGraphicsGetImageFromCurrentImageContext();UIGraphicsEndImageContext();CGImageRelease(cgImage);} else {codeImage = [QRCodeGenerator qrImageForString:content imageSize:size.width];}return codeImage; }

iOS8以下使用libqrencode庫來生成二維碼圖片。

代碼完善

2015年12月11日

QA測試發現,服務端生成的二維碼,使用ZBar識別不出來,但將這張圖片保存到相冊,然后發送就可以識別出來。最初的想法是要服務端修改生成的二維碼,但安卓能夠識別出來,此路不通,那只有看ZBar的源碼了。

- (id <NSFastEnumeration>) scanImage: (CGImageRef) image {timer_start;int nsyms = [self scanImage: imagewithScaling: 0];//沒有識別出來,判斷CGImageRef對象的寬和高是否大于640,大于或等于的話進行縮放再進行掃描if(!nsyms &&CGImageGetWidth(image) >= 640 &&CGImageGetHeight(image) >= 640)// make one more attempt for close up, grainy imagesnsyms = [self scanImage: imagewithScaling: .5];NSMutableArray *syms = nil;if(nsyms) {// quality/type filteringint max_quality = MIN_QUALITY;for(ZBarSymbol *sym in scanner.results) {zbar_symbol_type_t type = sym.type;int quality;if(type == ZBAR_QRCODE)quality = INT_MAX;elsequality = sym.quality;if(quality < max_quality) {zlog(@" type=%d quality=%d < %d\n",type, quality, max_quality);continue;}if(max_quality < quality) {max_quality = quality;if(syms)[syms removeAllObjects];}zlog(@" type=%d quality=%d\n", type, quality);if(!syms)syms = [NSMutableArray arrayWithCapacity: 1];[syms addObject: sym];}}zlog(@"read %d filtered symbols in %gs total\n",(!syms) ? 0 : [syms count], timer_elapsed(t_start, timer_now()));return(syms); }

在這里就產生了一個解決有些二維碼圖片識別不出來的解決思路:將傳過來的UIImage的寬和高設置為640,識別不出來再進行縮放識別。修改UIImage的代碼如下:

-(UIImage *)TransformtoSize:(CGSize)Newsize {// 創建一個bitmap的contextUIGraphicsBeginImageContext(Newsize);// 繪制改變大小的圖片[self drawInRect:CGRectMake(0, 0, Newsize.width, Newsize.height)];// 從當前context中創建一個改變大小后的圖片UIImage *TransformedImg=UIGraphicsGetImageFromCurrentImageContext();// 使當前的context出堆棧UIGraphicsEndImageContext();// 返回新的改變大小后的圖片return TransformedImg; }

這樣類似于ZXing中的tryHard設置為YES。識別不出來的二維碼圖片就可以識別了。

2016年5月20日
bug: 點擊進入掃一掃界面,退出,再進入,這樣重復5次左右,掃一掃之前的界面的會出現卡頓。
原因:多次進入掃一掃界面,再退出,因此界面未被系統回收,captureSession對象一直在運行,會造成內存泄露,引起上一個界面卡頓。
解決方案:在視圖將要消失的時候,確保captureSession對象停止運行。

- (void)viewWillDisappear:(BOOL)animated {[super viewWillDisappear:animated];if ([self.captureSession isRunning]) {[self.captureSession stopRunning];} }

2018年4月28日

識別二維碼圖片優化

近期通過bugly收集卡頓問題發現,二維碼組件在識別二維碼圖片時,會出現卡頓問題。為優化識別速度,采用了三種方案,并分別進行測試,并對測試數據進行分析,最終挑選出最優的方案。

任務A:使用系統提供的CoreImage的CIDetector接口去識別二維碼圖片,返回對應的字符串;
任務B:使用zbar中的方法去識別二維碼圖片,返回對應的字符串。

//任務A + (NSString *)useSystemMethodDecodeImage:(UIImage *)image {NSString *resultString = nil;CIDetector *detector = [CIDetector detectorOfType:CIDetectorTypeQRCodecontext:niloptions:@{CIDetectorAccuracy:CIDetectorAccuracyHigh}];if (detector) {CIImage *ciImage = [CIImage imageWithCGImage:image.CGImage];NSArray *features = [detector featuresInImage:ciImage];CIQRCodeFeature *feature = [features firstObject];resultString = feature.messageString;}return resultString; } //任務B + (NSString *)useZbarMethodDecodeImage:(UIImage *)image {UIImage *decodeImage = image;if (decodeImage.size.width < 641) {decodeImage = [decodeImage TransformtoSize:CGSizeMake(640, 640)];}QRCodeZBarReaderController* read = [QRCodeZBarReaderController new];CGImageRef cgImageRef = decodeImage.CGImage;QRCodeZBarSymbol *symbol = nil;for(symbol in [read scanImage:cgImageRef]) break;return symbol.data; }
  • 方案A:先執行任務A,如果獲取到的字符串為空,再執行任務B。
+ (NSString *)planOneDecodeWithImage:(UIImage *)image index:(NSInteger)index{NSMutableString *costTimeInfo = [NSMutableString stringWithFormat:@"%ld\r\n",index];CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();NSString *detectorString = [MUIQRCodeDecoder useSystemMethodDecodeImage:image];CFAbsoluteTime detectorCostTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"detector : %f ms\r\n",detectorCostTime *1000.0]];NSAssert(detectorString.length > 0, @"detector fail!");CFAbsoluteTime zbarStartTime = CFAbsoluteTimeGetCurrent();NSString *zbarSymbolString = [MUIQRCodeDecoder useZbarMethodDecodeImage:image];NSAssert(zbarSymbolString.length > 0, @"zbar fail!");CFAbsoluteTime zbarCostTime = (CFAbsoluteTimeGetCurrent() - zbarStartTime);[costTimeInfo appendString:[NSString stringWithFormat:@"zbar : %f ms\r\n",zbarCostTime *1000.0]];CFAbsoluteTime totalCostTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"total cost : %f ms\r\n",totalCostTime *1000.0]];[costTimeInfo appendString:[NSString stringWithFormat:@"detectorString : %@ ms\r\n",detectorString]];[costTimeInfo appendString:[NSString stringWithFormat:@"zbarSymbolString : %@ ms\r\n",zbarSymbolString]];return [costTimeInfo copy]; }
  • 方案B:同時執行任務A和任務B,兩者均執行完后,返回識別的結果;
+ (NSString *)planTwoDecodeWithImage:(UIImage *)image index:(NSInteger)index { __block NSMutableString *costTimeInfo = [NSMutableString stringWithFormat:@"%ld\r\n",index];__block NSString *detectorString = nil;__block NSString *zbarSymbolString = nil;CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{detectorString = [MUIQRCodeDecoder useSystemMethodDecodeImage:image];NSAssert(detectorString.length > 0, @"detector fail!");CFAbsoluteTime costTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"detector : %f ms\r\n",costTime *1000.0]];});dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{zbarSymbolString = [MUIQRCodeDecoder useZbarMethodDecodeImage:image];NSAssert(zbarSymbolString.length > 0, @"zbar fail!");CFAbsoluteTime costTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"zbar : %f ms\r\n",costTime *1000.0]];});dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{dispatch_semaphore_signal(semaphore);});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);CFAbsoluteTime totalCostTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"total cost : %f ms\r\n",totalCostTime *1000.0]];[costTimeInfo appendString:[NSString stringWithFormat:@"detectorString : %@ ms\r\n",detectorString]];[costTimeInfo appendString:[NSString stringWithFormat:@"zbarSymbolString : %@ ms\r\n",zbarSymbolString]];return [costTimeInfo copy]; }
  • 方案C:同時執行任務A和任務B
    1、任務A先執行完且識別成功,返回識別結果;
    2、任務B先執行完且識別成功,返回識別結果;
    3、任務A和任務B均識別失敗,兩者均執行完后,返回識別的結果。
+ (NSString *)planThreeDecodeWithImage:(UIImage *)image index:(NSInteger)index {__block NSMutableString *costTimeInfo = [NSMutableString stringWithFormat:@"%ld\r\n",index];__block NSString *detectorString = nil;__block NSString *zbarSymbolString = nil;__block BOOL isNeedSendSignal = YES;CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);dispatch_group_t group = dispatch_group_create();dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{detectorString = [MUIQRCodeDecoder useSystemMethodDecodeImage:image];//NSAssert(detectorString.length > 0, @"detector fail!");CFAbsoluteTime costTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"detector : %f ms\r\n",costTime *1000.0]];if (detectorString.length > 0 && isNeedSendSignal) {isNeedSendSignal = NO;dispatch_semaphore_signal(semaphore);}});dispatch_group_async(group, dispatch_get_global_queue(0,0), ^{zbarSymbolString = [MUIQRCodeDecoder useZbarMethodDecodeImage:image];//NSAssert(zbarSymbolString.length > 0, @"zbar fail!");CFAbsoluteTime costTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"zbar : %f ms\r\n",costTime *1000.0]];if (zbarSymbolString.length > 0 && isNeedSendSignal) {isNeedSendSignal = NO;dispatch_semaphore_signal(semaphore);}});dispatch_group_notify(group, dispatch_get_global_queue(0,0), ^{if (isNeedSendSignal) {dispatch_semaphore_signal(semaphore);}});dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);CFAbsoluteTime totalCostTime = (CFAbsoluteTimeGetCurrent() - startTime);[costTimeInfo appendString:[NSString stringWithFormat:@"total cost : %f ms\r\n",totalCostTime *1000.0]];[costTimeInfo appendString:[NSString stringWithFormat:@"detectorString : %@ ms\r\n",detectorString]];[costTimeInfo appendString:[NSString stringWithFormat:@"zbarSymbolString : %@ ms\r\n",zbarSymbolString]]; return [costTimeInfo copy]; }

測試數據如下所示:(取了前10張圖片)

分析測試數據發現:
1、在測試第一張二維碼圖片時,總耗時均較大,如果第一次識別使用的是系統方法,耗時超過500ms,這也是為什么會出現卡頓的原因;
2、使用系統方法去識別二維碼圖片時,如果不是第一次去識別,耗時較小,在65ms以內;
3、使用zbar的方法去識別二維碼圖片,耗時均值在200ms以內;
4、在方案C中,如果第一次使用系統方法,耗時為226ms。

總結得出,從優化卡頓問題的角度出發,使用方案C最優,同時發現,如果使用系統方法能識別出二維碼圖片,在初始化之后(也就是第二次使用),耗時最短。同時因為在實際的使用場景中,圖片是一張一張識別的,識別過程有一個間隔時間,如果已經使用系統方法識別過二維碼圖片,那下次識別就能達到最優。所以使用方案C的話,最差情況均值在200ms左右,最好的情況和方案A中第二次使用系統方法耗時基本一致。綜合考慮,使用方案C。

小結

在實際的項目開發過程中,設想的情況和實際情況會存在偏差,需要自己時刻使用性能調優工具,根據數據去進行優化,而不能想當然的認為某種方式是最優的。
源碼和demo請點這里
參考的文章鏈接如下
再見ZXing 使用系統原生代碼處理QRCode
IOS二維碼掃描,你需要注意的兩件事
[Zbar算法流程介紹](http://blog.csdn.net/u013738531/article/details/54574262)

總結

以上是生活随笔為你收集整理的iOS二维码识别/二维码生成的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。