KMGOCROperation.m 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. //
  2. // KMGOCROperation.m
  3. // PDF Reader Pro Edition
  4. //
  5. // Created by 万军 on 2020/3/23.
  6. //
  7. #import "KMGOCROperation.h"
  8. #import "KMGOCRManager.h"
  9. #import <AppKit/AppKit.h>
  10. #define KMGOC_API_URL @"https://vision.googleapis.com/v1/images:annotate"
  11. #if VERSION_FREE
  12. #define KMGOC_API_KEY @"AIzaSyBhSRohpngAu8pSgFDXPytslNDHgGm7uDs"
  13. #else
  14. #define KMGOC_API_KEY @"AIzaSyCJuqJ9YvtkFKMl1mW3Yq-av3mmI9ScbRY"
  15. #endif
  16. @interface KMGOCROperation ()
  17. @property (assign, nonatomic, getter = isExecuting) BOOL executing;
  18. @property (assign, nonatomic, getter = isFinished) BOOL finished;
  19. @property (assign, nonatomic, getter = isCancelled) BOOL cancelled;
  20. @property (nonatomic, strong) NSString *fileName;
  21. @property (nonatomic, strong) NSImage *orcImage;
  22. @property (nonatomic, strong) NSURLSessionDataTask *task;
  23. @property (nonatomic, assign) NSInteger imageIndex;
  24. @end
  25. @implementation KMGOCROperation
  26. - (void)dealloc {
  27. }
  28. - (instancetype)initWithRecognitionImage:(NSImage *)image withImageIndex:(NSInteger)imageIndex {
  29. self = [super init];
  30. if (self) {
  31. self.fileName = [self fileNameWithDate];
  32. self.imageIndex = imageIndex;
  33. self.orcImage = image;
  34. self.queuePriority = NSOperationQueuePriorityNormal;
  35. self.name = self.fileName;
  36. self.executing = NO;
  37. self.finished = NO;
  38. }
  39. return self;
  40. }
  41. #pragma mark - overwrite
  42. - (void)start {
  43. if ([self p_checkCancelled]) {
  44. return;
  45. }
  46. self.executing = YES;
  47. [NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
  48. }
  49. - (void)main {
  50. @try {
  51. if ([self p_checkCancelled]) {
  52. return;
  53. }
  54. [self recognitionImage:_orcImage];
  55. while (self.executing) {
  56. if ([self p_checkCancelled]) {
  57. return;
  58. }
  59. }
  60. }
  61. @catch (NSException * e) {
  62. NSLog(@"Exception %@", e);
  63. }
  64. }
  65. - (void)cancel
  66. {
  67. [super cancel];
  68. if (self.task) {
  69. [self.task cancel];
  70. self.task = nil;
  71. }
  72. if ([self.operationDelegate respondsToSelector:@selector(GOCROperation:cancelOCRImageAtIndex:)]) {
  73. [self.operationDelegate GOCROperation:self cancelOCRImageAtIndex:self.imageIndex];
  74. }
  75. if (self.executing) {
  76. self.executing = NO;
  77. self.finished = YES;
  78. } else {
  79. self.finished = NO;
  80. }
  81. self.cancelled = YES;
  82. }
  83. #pragma mark - private methods
  84. - (void)p_done
  85. {
  86. self.executing = NO;
  87. self.finished = YES;
  88. }
  89. - (BOOL)p_checkCancelled
  90. {
  91. if (self.cancelled) {
  92. self.finished = YES;
  93. return YES;
  94. }
  95. return NO;
  96. }
  97. - (void)recognitionImage:(NSImage *)image {
  98. if ([self.operationDelegate respondsToSelector:@selector(GOCROperation:startOCRImageAtIndex:)]) {
  99. [self.operationDelegate GOCROperation:self startOCRImageAtIndex:self.imageIndex];
  100. }
  101. NSString *binaryImageData = [self base64EncodeImage:image];
  102. NSLog(@"binaryImageData == %@",binaryImageData);
  103. if (!binaryImageData) {
  104. if ([self.operationDelegate respondsToSelector:@selector(GOCROperation:failureOCRImageAtIndex:error:)]) {
  105. [self.operationDelegate GOCROperation:self failureOCRImageAtIndex:self.imageIndex error:nil];
  106. }
  107. return;
  108. }
  109. NSString *urlString = [NSString stringWithFormat:@"%@?key=%@",KMGOC_API_URL,KMGOC_API_KEY];
  110. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:urlString]];
  111. [request setHTTPMethod: @"POST"];
  112. [request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
  113. NSDictionary *imageDictionary = @{@"content":binaryImageData};
  114. NSArray *featuresArray = @[@{@"type":@"TEXT_DETECTION",@"maxResults":@10}];
  115. NSDictionary *paramsDictionary = @{@"requests":@[@{@"image":imageDictionary,
  116. @"features":featuresArray}]};
  117. if (self.selectedLanguages && self.selectedLanguages.count>0) {
  118. NSDictionary *imageContextDictionary = @{@"languageHints":self.selectedLanguages};
  119. paramsDictionary = @{@"requests":@[@{@"image":imageDictionary,
  120. @"features":featuresArray,
  121. @"imageContext":imageContextDictionary}]};
  122. }
  123. NSError *error;
  124. NSData *requestData = [NSJSONSerialization dataWithJSONObject:paramsDictionary options:0 error:&error];
  125. [request setHTTPBody:requestData];
  126. NSURLSession *URLSession = [NSURLSession sharedSession];
  127. self.task = [URLSession dataTaskWithRequest:request
  128. completionHandler:^ (NSData *data, NSURLResponse *response, NSError *error) {
  129. if (NSURLErrorCancelled == error.code) {
  130. return ;
  131. }
  132. NSArray *results = nil;
  133. if (!error) {
  134. NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableLeaves error:nil];
  135. results = [self responseDataResults:dictionary];
  136. }
  137. dispatch_async(dispatch_get_main_queue(), ^{
  138. if (error || !results) {
  139. if ([self.operationDelegate respondsToSelector:@selector(GOCROperation:failureOCRImageAtIndex:error:)]) {
  140. [self.operationDelegate GOCROperation:self failureOCRImageAtIndex:self.imageIndex error:error];
  141. }
  142. } else {
  143. if ([self.operationDelegate respondsToSelector:@selector(GOCROperation:finishOCRImageAtIndex:results:)]) {
  144. [self.operationDelegate GOCROperation:self finishOCRImageAtIndex:self.imageIndex results:results];
  145. }
  146. }
  147. });
  148. if (error || !results) {
  149. [self cancel];
  150. } else {
  151. [self p_done];
  152. }
  153. }];
  154. [self.task resume];
  155. }
  156. #pragma mark - Private Methods
  157. - (NSString *)fileNameWithDate
  158. {
  159. NSDateFormatter *formatter = [[NSDateFormatter alloc ] init];
  160. [formatter setDateFormat:@"YYYY-MM-dd-hh-mm-ss-SSS"];
  161. NSString *dateString = [formatter stringFromDate:[NSDate date]];
  162. NSString *fileName = [NSString stringWithFormat:@"%@ %ld",dateString, (long)_imageIndex];
  163. return fileName;
  164. }
  165. - (NSData *)compressImageData:(NSData *)imageData toMaxFileSize:(NSInteger)maxFileSize {
  166. CGFloat compression = 0.9f;
  167. CGFloat maxCompression = 0.1f;
  168. NSData *compressImageData = imageData;
  169. while ([imageData length] > maxFileSize && compression > maxCompression) {
  170. compression -= 0.1;
  171. NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:imageData];
  172. compressImageData = [imageRep representationUsingType:NSBitmapImageFileTypeJPEG
  173. properties:@{NSImageCompressionFactor:@(compression)}];
  174. }
  175. return compressImageData;
  176. }
  177. - (NSString *)base64EncodeImage:(NSImage *)image {
  178. NSData *data = [image TIFFRepresentation];
  179. NSBitmapImageRep *imageRep = [NSBitmapImageRep imageRepWithData:data];
  180. [imageRep setSize:[image size]];
  181. NSData *imagedata = [imageRep representationUsingType:NSBitmapImageFileTypePNG properties:@{}];
  182. // Resize the image if it exceeds the 4MB API limit
  183. if ([imagedata length] > 4194304) {
  184. imagedata = [self compressImageData:imagedata toMaxFileSize:4194304];
  185. }
  186. NSString *base64String = nil;
  187. if ([imagedata respondsToSelector:@selector(base64EncodedStringWithOptions:)]) {
  188. base64String = [imagedata base64EncodedStringWithOptions:NSDataBase64EncodingEndLineWithCarriageReturn];
  189. } else {
  190. base64String = [imagedata base64Encoding];
  191. }
  192. return base64String;
  193. }
  194. - (NSArray *)responseDataResults:(NSDictionary *)dictionary {
  195. NSArray *responses = [dictionary isKindOfClass:[NSDictionary class]] ? [dictionary objectForKey:@"responses"] : nil;
  196. NSDictionary *responseData = [responses isKindOfClass:[NSArray class]] ? [responses firstObject] : nil;
  197. NSDictionary *errorObj = [dictionary isKindOfClass:[NSDictionary class]] ? [dictionary objectForKey:@"error"] : nil;
  198. if (errorObj) {
  199. return nil;
  200. }
  201. NSMutableArray *results = nil;
  202. NSArray *textAnnotations = [responseData isKindOfClass:[NSDictionary class]] ? [responseData objectForKey:@"textAnnotations"] : nil;
  203. if (textAnnotations && [textAnnotations isKindOfClass:[NSArray class]]) {
  204. results = [NSMutableArray array];
  205. for (NSDictionary *annotation in textAnnotations) {
  206. CGRect textBounds = CGRectZero;
  207. NSArray *vertices = annotation[@"boundingPoly"][@"vertices"];
  208. if (vertices && [vertices isKindOfClass:[NSArray class]]) {
  209. CGFloat minX = 0, minY = 0, maxX = 0, maxY = 0;
  210. for (int i=0; i<vertices.count; i++) {
  211. NSDictionary *vertex = vertices[i];
  212. CGFloat x = [vertex[@"x"] floatValue];
  213. CGFloat y = [vertex[@"y"] floatValue];
  214. minX = i == 0 ? x : MIN(x, minX);
  215. minY = i == 0 ? y : MIN(y, minY);
  216. maxX = i == 0 ? x : MAX(x, maxX);
  217. maxY = i == 0 ? y : MAX(y, maxY);
  218. }
  219. textBounds = CGRectMake(minX, minY, maxX-minX, maxY-minY);
  220. }
  221. KMGOCRResult *result = [[KMGOCRResult alloc] init];
  222. result.text = annotation[@"description"];
  223. result.locale = annotation[@"locale"];
  224. result.textBounds = textBounds;
  225. [results addObject:result];
  226. }
  227. }
  228. return results;
  229. }
  230. @end