MatQuickLook.mm 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. //
  2. // MatQuickLook.mm
  3. //
  4. // Created by Giles Payne on 2021/07/18.
  5. //
  6. #import "MatQuickLook.h"
  7. #import "Rect2i.h"
  8. #import "Core.h"
  9. #import "Imgproc.h"
  10. #import <opencv2/imgcodecs/ios.h>
  11. #define SIZE 20
  12. static UIFont* getCMU() {
  13. return [UIFont fontWithName:@"CMU Serif" size:SIZE];
  14. }
  15. static UIFont* getBodoni72() {
  16. return [UIFont fontWithName:@"Bodoni 72" size:SIZE];
  17. }
  18. static UIFont* getAnySerif() {
  19. #if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 130000
  20. if (@available(iOS 13.0, *)) {
  21. return [UIFont fontWithDescriptor:[[UIFontDescriptor preferredFontDescriptorWithTextStyle:UIFontTextStyleBody] fontDescriptorWithDesign:UIFontDescriptorSystemDesignSerif] size:SIZE];
  22. } else {
  23. return nil;
  24. }
  25. #else
  26. return nil;
  27. #endif
  28. }
  29. static UIFont* getSystemFont() {
  30. return [UIFont systemFontOfSize:SIZE];
  31. }
  32. typedef UIFont* (*FontGetter)();
  33. @implementation MatQuickLook
  34. + (NSString*)makeLabel:(BOOL)isIntType val:(NSNumber*)num {
  35. if (isIntType) {
  36. return [NSString stringWithFormat:@"%d", num.intValue];
  37. } else {
  38. int exponent = 1 + (int)log10(abs(num.doubleValue));
  39. if (num.doubleValue == (double)num.intValue && num.doubleValue < 10000 && num.doubleValue > -10000) {
  40. return [NSString stringWithFormat:@"%d", num.intValue];;
  41. } else if (exponent <= 5 && exponent >= -1) {
  42. return [NSString stringWithFormat:[NSString stringWithFormat:@"%%%d.%df", 6, MIN(5 - exponent, 4)], num.doubleValue];
  43. } else {
  44. return [[[NSString stringWithFormat:@"%.2e", num.doubleValue] stringByReplacingOccurrencesOfString:@"e+0" withString:@"e"] stringByReplacingOccurrencesOfString:@"e-0" withString:@"e-"];
  45. }
  46. }
  47. }
  48. + (void)relativeLine:(UIBezierPath*)path relX:(CGFloat)x relY:(CGFloat)y {
  49. CGPoint curr = path.currentPoint;
  50. [path addLineToPoint:CGPointMake(curr.x + x, curr.y + y)];
  51. }
  52. + (id)matDebugQuickLookObject:(Mat*)mat {
  53. if ([mat dims] == 2 && [mat rows] <= 10 && [mat cols] <= 10 && [mat channels] == 1) {
  54. FontGetter fontGetters[] = { getCMU, getBodoni72, getAnySerif, getSystemFont };
  55. UIFont* font = nil;
  56. for (int fontGetterIndex = 0; font==nil && fontGetterIndex < (sizeof(fontGetters)) / (sizeof(fontGetters[0])); fontGetterIndex++) {
  57. font = fontGetters[fontGetterIndex]();
  58. }
  59. int elements = [mat rows] * [mat cols];
  60. NSDictionary<NSAttributedStringKey,id>* textFontAttributes = @{ NSFontAttributeName: font, NSForegroundColorAttributeName: UIColor.blackColor };
  61. NSMutableArray<NSNumber*>* rawData = [NSMutableArray new];
  62. for (int dataIndex = 0; dataIndex < elements; dataIndex++) {
  63. [rawData addObject:[NSNumber numberWithDouble:0]];
  64. }
  65. [mat get:0 col: 0 data: rawData];
  66. BOOL isIntType = [mat depth] <= CV_32S;
  67. NSMutableArray<NSString*>* labels = [NSMutableArray new];
  68. NSMutableDictionary<NSString*, NSValue*>* boundingRects = [NSMutableDictionary dictionaryWithCapacity:elements];
  69. int maxWidth = 0, maxHeight = 0;
  70. for (NSNumber* number in rawData) {
  71. NSString* label = [MatQuickLook makeLabel:isIntType val:number];
  72. [labels addObject:label];
  73. CGRect boundingRect = [label boundingRectWithSize:CGSizeMake(CGFLOAT_MAX, CGFLOAT_MAX) options:NSStringDrawingUsesLineFragmentOrigin attributes:textFontAttributes context:nil];
  74. if (boundingRect.size.width > maxWidth) {
  75. maxWidth = boundingRect.size.width;
  76. }
  77. if (boundingRect.size.height > maxHeight) {
  78. maxHeight = boundingRect.size.height;
  79. }
  80. boundingRects[label] = [NSValue valueWithCGRect:boundingRect];
  81. }
  82. int rowGap = 6;
  83. int colGap = 6;
  84. int borderGap = 8;
  85. int lineThickness = 3;
  86. int lipWidth = 6;
  87. int imageWidth = 2 * (borderGap + lipWidth) + maxWidth * [mat cols] + colGap * ([mat cols] - 1);
  88. int imageHeight = 2 * (borderGap + lipWidth) + maxHeight * [mat rows] + rowGap * ([mat rows] - 1);
  89. UIBezierPath* leftBracket = [UIBezierPath new];
  90. [leftBracket moveToPoint:CGPointMake(borderGap, borderGap)];
  91. [MatQuickLook relativeLine:leftBracket relX:0 relY:imageHeight - 2 * borderGap];
  92. [MatQuickLook relativeLine:leftBracket relX:lineThickness + lipWidth relY:0];
  93. [MatQuickLook relativeLine:leftBracket relX:0 relY:-lineThickness];
  94. [MatQuickLook relativeLine:leftBracket relX:-lipWidth relY:0];
  95. [MatQuickLook relativeLine:leftBracket relX:0 relY:-(imageHeight - 2 * (borderGap + lineThickness))];
  96. [MatQuickLook relativeLine:leftBracket relX:lipWidth relY:0];
  97. [MatQuickLook relativeLine:leftBracket relX:0 relY:-lineThickness];
  98. [leftBracket closePath];
  99. CGAffineTransform reflect = CGAffineTransformConcat(CGAffineTransformMakeTranslation(-imageWidth, 0), CGAffineTransformMakeScale(-1, 1));
  100. UIBezierPath* rightBracket = [leftBracket copy];
  101. [rightBracket applyTransform:reflect];
  102. CGRect rect = CGRectMake(0, 0, imageWidth, imageHeight);
  103. UIGraphicsBeginImageContextWithOptions(rect.size, false, 0.0);
  104. [UIColor.whiteColor setFill];
  105. UIRectFill(rect);
  106. [UIColor.blackColor setFill];
  107. [leftBracket fill];
  108. [rightBracket fill];
  109. [labels enumerateObjectsUsingBlock:^(id label, NSUInteger index, BOOL *stop)
  110. {
  111. CGRect boundingRect = boundingRects[label].CGRectValue;
  112. int row = (int)index / [mat cols];
  113. int col = (int)index % [mat cols];
  114. int x = borderGap + lipWidth + col * (maxWidth + colGap) + (maxWidth - boundingRect.size.width) / 2;
  115. int y = borderGap + lipWidth + row * (maxHeight + rowGap) + (maxHeight - boundingRect.size.height) / 2;
  116. CGRect textRect = CGRectMake(x, y, boundingRect.size.width, boundingRect.size.height);
  117. [label drawInRect:textRect withAttributes:textFontAttributes];
  118. }];
  119. UIImage* image = UIGraphicsGetImageFromCurrentImageContext();
  120. UIGraphicsEndImageContext();
  121. return image;
  122. } else if (([mat dims] == 2) && ([mat type] == CV_8U || [mat type] == CV_8UC3 || [mat type] == CV_8UC4)) {
  123. return [mat toUIImage];
  124. } else if ([mat dims] == 2 && [mat channels] == 1) {
  125. Mat* normalized = [Mat new];
  126. [Core normalize:mat dst:normalized alpha:0 beta:255 norm_type:NORM_MINMAX dtype:CV_8U];
  127. Mat* normalizedKey = [[Mat alloc] initWithRows:[mat rows] + 10 cols:[mat cols] type:CV_8U];
  128. std::vector<char> key;
  129. for (int index = 0; index < [mat cols]; index++) {
  130. key.push_back((char)(index * 256 / [mat cols]));
  131. }
  132. for (int index = 0; index < 10; index++) {
  133. [normalizedKey put:@[[NSNumber numberWithInt:index], [NSNumber numberWithInt:0]] count:[mat cols] byteBuffer:key.data()];
  134. }
  135. [normalized copyTo:[normalizedKey submatRoi:[[Rect2i alloc] initWithX:0 y:10 width:[mat cols] height:[mat rows]]]];
  136. Mat* colorMap = [Mat new];
  137. [Imgproc applyColorMap:normalizedKey dst:colorMap colormap:COLORMAP_JET];
  138. [Imgproc cvtColor:colorMap dst:colorMap code:COLOR_BGR2RGB];
  139. return [colorMap toUIImage];
  140. }
  141. return [mat description];
  142. }
  143. @end