// // KMGOCRManager.m // // // // // #import "KMGOCRManager.h" #import "KMGOCROperation.h" #import "KMOCROperation.h" #import #import #import #import #define KMImageScale 4.0 //#import "KMOCRPreferences.h" //恢复OCR架上版本样式 /* https://cloud.google.com/vision/reference/rest/v1/images/annotate#AnnotateImageResponse languageHints[] List of languages to use for TEXT_DETECTION. In most cases, an empty value yields the best results since it enables automatic language detection. For languages based on the Latin alphabet, setting languageHints is not needed. In rare cases, when the language of the text in the image is known, setting a hint will help get better results (although it will be a significant hindrance if the hint is wrong). Text detection returns an error if one or more of the specified languages is not one of the supported languages. */ NSString * KMGOCRLanguageCodeKey = @"KMGOCRLanguageCodeKey"; NSString * KMGOCRLanguageStringKey = @"KMGOCRLanguageStringKey"; #pragma mark - KMGOCRResult @class KMOCROperationQueue; @interface KMGOCRResult () @end @implementation KMGOCRResult @end #pragma mark - KMGOCRManager @interface KMGOCRManager () @property (nonatomic, strong) NSMutableArray *languages; @property (nonatomic, assign) NSString *fileType; @property (nonatomic, strong) NSMutableArray *ocrPath; @property (nonatomic, strong) NSURL *filePath; @property (nonatomic, assign) NSUInteger finishIndex; //@property (nonatomic, retain) KMOCRPreferences *ocrPreferences; //恢复OCR架上版本样式 @property (nonatomic, strong) VNRecognizeTextRequest *appleRequest; @property (nonatomic, assign) VNRequestTextRecognitionLevel appleRecognitionMode; @property (nonatomic, strong) NSMutableArray *images; @end @implementation KMGOCRManager #pragma mark - Init Methods - (id)init { if (self = [super init]) { } return self; } - (void)dealloc { _delegate = nil; } #pragma mark - Public Methods + (KMGOCRManager *)defaultManager { static KMGOCRManager *singleton = nil; static dispatch_once_t pred; dispatch_once(&pred, ^{ singleton = [[KMGOCRManager alloc] init]; }); return singleton; } + (NSArray *)languages { if ([KMGOCRManager defaultManager].OCRType == KMOCRType_Google) { return @[@{KMGOCRLanguageCodeKey:@"af", KMGOCRLanguageStringKey:@"Afrikaans"}, @{KMGOCRLanguageCodeKey:@"sq", KMGOCRLanguageStringKey:@"Albanian"}, @{KMGOCRLanguageCodeKey:@"ar", KMGOCRLanguageStringKey:@"Arabic"}, @{KMGOCRLanguageCodeKey:@"hy", KMGOCRLanguageStringKey:@"Armenian"}, @{KMGOCRLanguageCodeKey:@"az", KMGOCRLanguageStringKey:@"Azerbaijani"}, @{KMGOCRLanguageCodeKey:@"eu", KMGOCRLanguageStringKey:@"Basque"}, @{KMGOCRLanguageCodeKey:@"be", KMGOCRLanguageStringKey:@"Belarusian"}, @{KMGOCRLanguageCodeKey:@"bn", KMGOCRLanguageStringKey:@"Bengali"}, @{KMGOCRLanguageCodeKey:@"bs", KMGOCRLanguageStringKey:@"Bosnian"}, @{KMGOCRLanguageCodeKey:@"bg", KMGOCRLanguageStringKey:@"Bulgarian"}, @{KMGOCRLanguageCodeKey:@"ca", KMGOCRLanguageStringKey:@"Catalan"}, @{KMGOCRLanguageCodeKey:@"ceb", KMGOCRLanguageStringKey:@"Cebuano"}, @{KMGOCRLanguageCodeKey:@"ny", KMGOCRLanguageStringKey:@"Chichewa"}, @{KMGOCRLanguageCodeKey:@"zh-CN", KMGOCRLanguageStringKey:@"Chinese Simplified"}, @{KMGOCRLanguageCodeKey:@"zh-TW", KMGOCRLanguageStringKey:@"Chinese Traditional"}, @{KMGOCRLanguageCodeKey:@"hr", KMGOCRLanguageStringKey:@"Croatian"}, @{KMGOCRLanguageCodeKey:@"cs", KMGOCRLanguageStringKey:@"Czech"}, @{KMGOCRLanguageCodeKey:@"da", KMGOCRLanguageStringKey:@"Danish"}, @{KMGOCRLanguageCodeKey:@"nl", KMGOCRLanguageStringKey:@"Dutch"}, @{KMGOCRLanguageCodeKey:@"en", KMGOCRLanguageStringKey:@"English"}, @{KMGOCRLanguageCodeKey:@"eo", KMGOCRLanguageStringKey:@"Esperanto"}, @{KMGOCRLanguageCodeKey:@"et", KMGOCRLanguageStringKey:@"Estonian"}, @{KMGOCRLanguageCodeKey:@"tl", KMGOCRLanguageStringKey:@"Filipino"}, @{KMGOCRLanguageCodeKey:@"fi", KMGOCRLanguageStringKey:@"Finnish"}, @{KMGOCRLanguageCodeKey:@"fr", KMGOCRLanguageStringKey:@"French"}, @{KMGOCRLanguageCodeKey:@"gl", KMGOCRLanguageStringKey:@"Galician"}, @{KMGOCRLanguageCodeKey:@"ka", KMGOCRLanguageStringKey:@"Georgian"}, @{KMGOCRLanguageCodeKey:@"de", KMGOCRLanguageStringKey:@"German"}, @{KMGOCRLanguageCodeKey:@"el", KMGOCRLanguageStringKey:@"Greek"}, @{KMGOCRLanguageCodeKey:@"gu", KMGOCRLanguageStringKey:@"Gujarati"}, @{KMGOCRLanguageCodeKey:@"ht", KMGOCRLanguageStringKey:@"Haitian Creole"}, @{KMGOCRLanguageCodeKey:@"ha", KMGOCRLanguageStringKey:@"Hausa"}, @{KMGOCRLanguageCodeKey:@"iw", KMGOCRLanguageStringKey:@"Hebrew"}, @{KMGOCRLanguageCodeKey:@"hi", KMGOCRLanguageStringKey:@"Hindi"}, @{KMGOCRLanguageCodeKey:@"hmn", KMGOCRLanguageStringKey:@"Hmong"}, @{KMGOCRLanguageCodeKey:@"hu", KMGOCRLanguageStringKey:@"Hungarian"}, @{KMGOCRLanguageCodeKey:@"is", KMGOCRLanguageStringKey:@"Icelandic"}, @{KMGOCRLanguageCodeKey:@"ig", KMGOCRLanguageStringKey:@"Igbo"}, @{KMGOCRLanguageCodeKey:@"id", KMGOCRLanguageStringKey:@"Indonesian"}, @{KMGOCRLanguageCodeKey:@"ga", KMGOCRLanguageStringKey:@"Irish"}, @{KMGOCRLanguageCodeKey:@"it", KMGOCRLanguageStringKey:@"Italian"}, @{KMGOCRLanguageCodeKey:@"ja", KMGOCRLanguageStringKey:@"Japanese"}, @{KMGOCRLanguageCodeKey:@"jw", KMGOCRLanguageStringKey:@"Javanese"}, @{KMGOCRLanguageCodeKey:@"kn", KMGOCRLanguageStringKey:@"Kannada"}, @{KMGOCRLanguageCodeKey:@"kk", KMGOCRLanguageStringKey:@"Kazakh"}, @{KMGOCRLanguageCodeKey:@"km", KMGOCRLanguageStringKey:@"Khmer"}, @{KMGOCRLanguageCodeKey:@"ko", KMGOCRLanguageStringKey:@"Korean"}, @{KMGOCRLanguageCodeKey:@"lo", KMGOCRLanguageStringKey:@"Lao"}, @{KMGOCRLanguageCodeKey:@"la", KMGOCRLanguageStringKey:@"Latin"}, @{KMGOCRLanguageCodeKey:@"lv", KMGOCRLanguageStringKey:@"Latvian"}, @{KMGOCRLanguageCodeKey:@"lt", KMGOCRLanguageStringKey:@"Lithuanian"}, @{KMGOCRLanguageCodeKey:@"mk", KMGOCRLanguageStringKey:@"Macedonian"}, @{KMGOCRLanguageCodeKey:@"mg", KMGOCRLanguageStringKey:@"Malagasy"}, @{KMGOCRLanguageCodeKey:@"ms", KMGOCRLanguageStringKey:@"Malay"}, @{KMGOCRLanguageCodeKey:@"ml", KMGOCRLanguageStringKey:@"Malayalam"}, @{KMGOCRLanguageCodeKey:@"mt", KMGOCRLanguageStringKey:@"Maltese"}, @{KMGOCRLanguageCodeKey:@"mi", KMGOCRLanguageStringKey:@"Maori"}, @{KMGOCRLanguageCodeKey:@"mr", KMGOCRLanguageStringKey:@"Marathi"}, @{KMGOCRLanguageCodeKey:@"mn", KMGOCRLanguageStringKey:@"Mongolian"}, @{KMGOCRLanguageCodeKey:@"my", KMGOCRLanguageStringKey:@"Myanmar (Burmese)"}, @{KMGOCRLanguageCodeKey:@"ne", KMGOCRLanguageStringKey:@"Nepali"}, @{KMGOCRLanguageCodeKey:@"no", KMGOCRLanguageStringKey:@"Norwegian"}, @{KMGOCRLanguageCodeKey:@"fa", KMGOCRLanguageStringKey:@"Persian"}, @{KMGOCRLanguageCodeKey:@"pl", KMGOCRLanguageStringKey:@"Polish"}, @{KMGOCRLanguageCodeKey:@"pt", KMGOCRLanguageStringKey:@"Portuguese"}, @{KMGOCRLanguageCodeKey:@"ma", KMGOCRLanguageStringKey:@"Punjabi"}, @{KMGOCRLanguageCodeKey:@"ro", KMGOCRLanguageStringKey:@"Romanian"}, @{KMGOCRLanguageCodeKey:@"ru", KMGOCRLanguageStringKey:@"Russian"}, @{KMGOCRLanguageCodeKey:@"sr", KMGOCRLanguageStringKey:@"Serbian"}, @{KMGOCRLanguageCodeKey:@"st", KMGOCRLanguageStringKey:@"Sesotho"}, @{KMGOCRLanguageCodeKey:@"si", KMGOCRLanguageStringKey:@"Sinhala"}, @{KMGOCRLanguageCodeKey:@"sk", KMGOCRLanguageStringKey:@"Slovak"}, @{KMGOCRLanguageCodeKey:@"sl", KMGOCRLanguageStringKey:@"Slovenian"}, @{KMGOCRLanguageCodeKey:@"so", KMGOCRLanguageStringKey:@"Somali"}, @{KMGOCRLanguageCodeKey:@"es", KMGOCRLanguageStringKey:@"Spanish"}, @{KMGOCRLanguageCodeKey:@"su", KMGOCRLanguageStringKey:@"Sudanese"}, @{KMGOCRLanguageCodeKey:@"sw", KMGOCRLanguageStringKey:@"Swahili"}, @{KMGOCRLanguageCodeKey:@"sv", KMGOCRLanguageStringKey:@"Swedish"}, @{KMGOCRLanguageCodeKey:@"tg", KMGOCRLanguageStringKey:@"Tajik"}, @{KMGOCRLanguageCodeKey:@"ta", KMGOCRLanguageStringKey:@"Tamil"}, @{KMGOCRLanguageCodeKey:@"te", KMGOCRLanguageStringKey:@"Telugu"}, @{KMGOCRLanguageCodeKey:@"th", KMGOCRLanguageStringKey:@"Thai"}, @{KMGOCRLanguageCodeKey:@"tr", KMGOCRLanguageStringKey:@"Turkish"}, @{KMGOCRLanguageCodeKey:@"uk", KMGOCRLanguageStringKey:@"Ukrainian"}, @{KMGOCRLanguageCodeKey:@"ur", KMGOCRLanguageStringKey:@"Urdu"}, @{KMGOCRLanguageCodeKey:@"uz", KMGOCRLanguageStringKey:@"Uzbek"}, @{KMGOCRLanguageCodeKey:@"vi", KMGOCRLanguageStringKey:@"Vietnamese"}, @{KMGOCRLanguageCodeKey:@"cy", KMGOCRLanguageStringKey:@"Welsh"}, @{KMGOCRLanguageCodeKey:@"yi", KMGOCRLanguageStringKey:@"Yiddish"}, @{KMGOCRLanguageCodeKey:@"yo", KMGOCRLanguageStringKey:@"Yoruba"}, @{KMGOCRLanguageCodeKey:@"zu", KMGOCRLanguageStringKey:@"Zulu"}]; } return @[@{KMGOCRLanguageCodeKey:@"en-US", KMGOCRLanguageStringKey:@"English"}, @{KMGOCRLanguageCodeKey:@"fr-FR", KMGOCRLanguageStringKey:@"French"}, @{KMGOCRLanguageCodeKey:@"it-IT", KMGOCRLanguageStringKey:@"Italian"}, @{KMGOCRLanguageCodeKey:@"de-DE", KMGOCRLanguageStringKey:@"German"}, @{KMGOCRLanguageCodeKey:@"es-ES", KMGOCRLanguageStringKey:@"Spanish"}, @{KMGOCRLanguageCodeKey:@"pt-BR", KMGOCRLanguageStringKey:@"Portuguese"}, @{KMGOCRLanguageCodeKey:@"zh-Hant", KMGOCRLanguageStringKey:@"Chinese Traditional"}, @{KMGOCRLanguageCodeKey:@"zh-Hans", KMGOCRLanguageStringKey:@"Chinese Simplified"}]; } static inline NSFont * FontWithSize(NSString *strChar, CGSize size) { CGFloat fontsize = 1.0; NSFont *font = [NSFont systemFontOfSize:fontsize]; CGSize strSize = [strChar sizeWithAttributes:@{NSFontAttributeName:font}]; while ((fontsize<127) && (strSize.width 0) { for (KMGOCROperation *op in queue.operations) { op.operationDelegate = nil; } [queue cancelAll]; } if ([self.delegate respondsToSelector:@selector(GOCRManager:didCancelOCRImageAtIndex:)]) { [self.delegate GOCRManager:self didCancelOCRImageAtIndex:self.finishIndex]; } self.delegate = nil; } #pragma mark - Apple OCR - (void)recognitionAppleImageAtIndex:(NSUInteger)index { if (index >= self.images.count) { if (@available(macOS 10.15, *)) { [_appleRequest cancel]; } _appleRequest = nil; if ([self.delegate respondsToSelector:@selector(GOCRManagerDidFinishOCR:)]) { [self.delegate GOCRManagerDidFinishOCR:self]; } return; } self.finishIndex = index; if ([self.delegate respondsToSelector:@selector(GOCRManager:didStartOCRImageAtIndex:)]) { [self.delegate GOCRManager:self didStartOCRImageAtIndex:self.finishIndex]; } if([self.images[self.finishIndex] isKindOfClass:[NSImage class]]) { [self recognitionAppleImage:self.images[self.finishIndex]]; } else { NSData *data = self.images[self.finishIndex]; NSImage *s = [[NSImage alloc] initWithData:data]; if(_appleRequest) { [_appleRequest cancel]; _appleRequest = nil; } [self recognitionAppleImage:s]; } } - (void)recognitionAppleImage:(NSImage *)image { dispatch_async(dispatch_get_global_queue(0, 0), ^{ __block typeof(self) blockSelf = self; _appleRequest = [[VNRecognizeTextRequest alloc] initWithCompletionHandler:^(VNRequest * _Nonnull request, NSError * _Nullable error) { NSArray *results = nil; if (request.results.count > 0) { results = [blockSelf responseDataRequest:request dictionary:nil imageSize:image.size]; } NSMutableArray *resultArray = [[NSMutableArray alloc] init]; for (KMGOCRResult *result in results) { NSDictionary *dic = @{@"x":@(result.textBounds.origin.x), @"y":@(result.textBounds.origin.y), @"width":@(result.textBounds.size.width), @"height":@(result.textBounds.size.height), @"text":result.text }; [resultArray addObject:dic]; } dispatch_async(dispatch_get_main_queue(), ^{ if (error || !results) { if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(GOCRManager:didFailureOCRImageAtIndex:error:)]) { [blockSelf.delegate GOCRManager:blockSelf didFailureOCRImageAtIndex:blockSelf.finishIndex error:error]; } } else { if (blockSelf.delegate && [blockSelf.delegate respondsToSelector:@selector(GOCRManager:didFinishOCRImageAtIndex:results:)]) { [blockSelf.delegate GOCRManager:blockSelf didFinishOCRImageAtIndex:blockSelf.finishIndex results:results]; } } [blockSelf recognitionAppleImageAtIndex:blockSelf.finishIndex+1]; }); }]; blockSelf.appleRequest.usesCPUOnly = YES; blockSelf.appleRequest.recognitionLevel = self.appleRecognitionMode; if (blockSelf.languages.count > 0) { NSMutableArray *array = [[NSMutableArray alloc] initWithArray:blockSelf.languages]; if ([blockSelf.languages containsObject:@"zh-Hant"]) { [array removeObject:@"zh-Hant"]; [array insertObject:@"zh-Hant" atIndex:0]; } if ([self.languages containsObject:@"zh-Hans"]) { [array removeObject:@"zh-Hans"]; [array insertObject:@"zh-Hans" atIndex:0]; } blockSelf.languages = array; blockSelf.appleRequest.recognitionLanguages = blockSelf.languages; } else { blockSelf.appleRequest.recognitionLanguages = @[@"zh-Hans",@"zh-Hant"]; } NSError *error = nil; VNImageRequestHandler *handle = [[VNImageRequestHandler alloc] initWithCGImage:[blockSelf nsImageToCGImageRef:image] options:@{}]; [handle performRequests:@[blockSelf.appleRequest] error:&error]; }); } #pragma mark - GOCRManager Methods - (void)gocrRecognitionImages:(NSArray *)images withLanguages:(NSArray *)languages { if (!images || images.count == 0) { return; } if ([self.delegate respondsToSelector:@selector(GOCRManagerDidStartOCR:)]) { [self.delegate GOCRManagerDidStartOCR:self]; } for (NSUInteger i=0; i 0) { KMGOCRResult *result = [[KMGOCRResult alloc] init]; result.text = OCRStr; if (self.languages.count > 0) { result.locale = self.languages[0]; } result.textBounds = CGRectZero; [results insertObject:result atIndex:0]; } } return results; } - (CGImageRef)nsImageToCGImageRef:(NSImage*)image; { NSData * imageData = [image TIFFRepresentation]; CGImageRef imageRef; if(imageData) { CGImageSourceRef imageSource = CGImageSourceCreateWithData( (CFDataRef)imageData, NULL); imageRef = CGImageSourceCreateImageAtIndex( imageSource, 0, NULL); } return imageRef; } @end @interface KMOCROperationQueue () @end @implementation KMOCROperationQueue - (void)dealloc { } + (instancetype)sharedInstance { static KMOCROperationQueue *sharedInstance; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [KMOCROperationQueue new]; }); return sharedInstance; } - (void)addOCROperation:(NSOperation *)op { [self addOperation:op]; } - (void)cancelAll { [self cancelAllOperations]; } @end