GPUImageHoughTransformLineDetector.m 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. #import "GPUImageHoughTransformLineDetector.h"
  2. @interface GPUImageHoughTransformLineDetector()
  3. - (void)extractLineParametersFromImageAtFrameTime:(CMTime)frameTime;
  4. @end
  5. @implementation GPUImageHoughTransformLineDetector
  6. @synthesize linesDetectedBlock;
  7. @synthesize edgeThreshold;
  8. @synthesize lineDetectionThreshold;
  9. @synthesize intermediateImages = _intermediateImages;
  10. - (id)init;
  11. {
  12. if (!(self = [super init]))
  13. {
  14. return nil;
  15. }
  16. #ifdef DEBUGLINEDETECTION
  17. _intermediateImages = [[NSMutableArray alloc] init];
  18. #endif
  19. // First pass: do edge detection and threshold that to just have white pixels for edges
  20. // if ([GPUImageContext deviceSupportsFramebufferReads])
  21. // if ([GPUImageContext deviceSupportsFramebufferReads])
  22. // {
  23. thresholdEdgeDetectionFilter = [[GPUImageThresholdEdgeDetectionFilter alloc] init];
  24. // thresholdEdgeDetectionFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
  25. [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:0.4];
  26. // [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:0.25];
  27. [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:1.0];
  28. // thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
  29. // }
  30. // else
  31. // {
  32. // thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
  33. // }
  34. [self addFilter:thresholdEdgeDetectionFilter];
  35. #ifdef DEBUGLINEDETECTION
  36. __unsafe_unretained NSMutableArray *weakIntermediateImages = _intermediateImages;
  37. __unsafe_unretained GPUImageOutput<GPUImageInput> *weakFilter = thresholdEdgeDetectionFilter;
  38. [thresholdEdgeDetectionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  39. [weakIntermediateImages removeAllObjects];
  40. UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
  41. [weakIntermediateImages addObject:intermediateImage];
  42. }];
  43. #endif
  44. // Second pass: extract the white points and draw representative lines in parallel coordinate space
  45. parallelCoordinateLineTransformFilter = [[GPUImageParallelCoordinateLineTransformFilter alloc] init];
  46. [self addFilter:parallelCoordinateLineTransformFilter];
  47. #ifdef DEBUGLINEDETECTION
  48. weakFilter = parallelCoordinateLineTransformFilter;
  49. [parallelCoordinateLineTransformFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  50. UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
  51. [weakIntermediateImages addObject:intermediateImage];
  52. }];
  53. #endif
  54. // Third pass: apply non-maximum suppression
  55. if ([GPUImageContext deviceSupportsFramebufferReads])
  56. {
  57. nonMaximumSuppressionFilter = [[GPUImageThresholdedNonMaximumSuppressionFilter alloc] initWithPackedColorspace:YES];
  58. }
  59. else
  60. {
  61. nonMaximumSuppressionFilter = [[GPUImageThresholdedNonMaximumSuppressionFilter alloc] initWithPackedColorspace:NO];
  62. }
  63. [self addFilter:nonMaximumSuppressionFilter];
  64. __unsafe_unretained GPUImageHoughTransformLineDetector *weakSelf = self;
  65. #ifdef DEBUGLINEDETECTION
  66. weakFilter = nonMaximumSuppressionFilter;
  67. [nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  68. UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
  69. [weakIntermediateImages addObject:intermediateImage];
  70. [weakSelf extractLineParametersFromImageAtFrameTime:frameTime];
  71. }];
  72. #else
  73. [nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
  74. [weakSelf extractLineParametersFromImageAtFrameTime:frameTime];
  75. }];
  76. #endif
  77. [thresholdEdgeDetectionFilter addTarget:parallelCoordinateLineTransformFilter];
  78. [parallelCoordinateLineTransformFilter addTarget:nonMaximumSuppressionFilter];
  79. self.initialFilters = [NSArray arrayWithObjects:thresholdEdgeDetectionFilter, nil];
  80. // self.terminalFilter = colorPackingFilter;
  81. self.terminalFilter = nonMaximumSuppressionFilter;
  82. // self.edgeThreshold = 0.95;
  83. self.lineDetectionThreshold = 0.8;
  84. return self;
  85. }
  86. - (void)dealloc;
  87. {
  88. free(rawImagePixels);
  89. free(linesArray);
  90. }
  91. #pragma mark -
  92. #pragma mark Corner extraction
  93. - (void)extractLineParametersFromImageAtFrameTime:(CMTime)frameTime;
  94. {
  95. // we need a normal color texture for this filter
  96. NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture format for this filter must be GL_RGBA.");
  97. NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
  98. NSUInteger numberOfLines = 0;
  99. CGSize imageSize = nonMaximumSuppressionFilter.outputFrameSize;
  100. unsigned int imageByteSize = imageSize.width * imageSize.height * 4;
  101. if (rawImagePixels == NULL)
  102. {
  103. rawImagePixels = (GLubyte *)malloc(imageByteSize);
  104. linesArray = calloc(1024 * 2, sizeof(GLfloat));
  105. }
  106. glReadPixels(0, 0, (int)imageSize.width, (int)imageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
  107. // CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
  108. unsigned int imageWidth = imageSize.width * 4;
  109. unsigned int currentByte = 0;
  110. unsigned int cornerStorageIndex = 0;
  111. while (currentByte < imageByteSize)
  112. {
  113. GLubyte colorByte = rawImagePixels[currentByte];
  114. if (colorByte > 0)
  115. {
  116. unsigned int xCoordinate = currentByte % imageWidth;
  117. unsigned int yCoordinate = currentByte / imageWidth;
  118. CGFloat normalizedXCoordinate = -1.0 + 2.0 * (CGFloat)(xCoordinate / 4) / imageSize.width;
  119. CGFloat normalizedYCoordinate = -1.0 + 2.0 * (CGFloat)(yCoordinate) / imageSize.height;
  120. if (normalizedXCoordinate < 0.0)
  121. {
  122. // T space
  123. // m = -1 - d/u
  124. // b = d * v/u
  125. if (normalizedXCoordinate > -0.05) // Test for the case right near the X axis, stamp the X intercept instead of the Y
  126. {
  127. linesArray[cornerStorageIndex++] = 100000.0;
  128. linesArray[cornerStorageIndex++] = normalizedYCoordinate;
  129. }
  130. else
  131. {
  132. linesArray[cornerStorageIndex++] = -1.0 - 1.0 / normalizedXCoordinate;
  133. linesArray[cornerStorageIndex++] = 1.0 * normalizedYCoordinate / normalizedXCoordinate;
  134. }
  135. }
  136. else
  137. {
  138. // S space
  139. // m = 1 - d/u
  140. // b = d * v/u
  141. if (normalizedXCoordinate < 0.05) // Test for the case right near the X axis, stamp the X intercept instead of the Y
  142. {
  143. linesArray[cornerStorageIndex++] = 100000.0;
  144. linesArray[cornerStorageIndex++] = normalizedYCoordinate;
  145. }
  146. else
  147. {
  148. linesArray[cornerStorageIndex++] = 1.0 - 1.0 / normalizedXCoordinate;
  149. linesArray[cornerStorageIndex++] = 1.0 * normalizedYCoordinate / normalizedXCoordinate;
  150. }
  151. }
  152. numberOfLines++;
  153. numberOfLines = MIN(numberOfLines, 1023);
  154. cornerStorageIndex = MIN(cornerStorageIndex, 2040);
  155. }
  156. currentByte +=4;
  157. }
  158. // CFAbsoluteTime currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime);
  159. // NSLog(@"Processing time : %f ms", 1000.0 * currentFrameTime);
  160. if (linesDetectedBlock != NULL)
  161. {
  162. linesDetectedBlock(linesArray, numberOfLines, frameTime);
  163. }
  164. }
  165. - (BOOL)wantsMonochromeInput;
  166. {
  167. // return YES;
  168. return NO;
  169. }
  170. #pragma mark -
  171. #pragma mark Accessors
  172. /*
  173. - (void)setEdgeThreshold:(CGFloat)newValue;
  174. {
  175. thresholdEdgeDetectionFilter.threshold = newValue;
  176. }
  177. - (CGFloat)edgeThreshold;
  178. {
  179. return thresholdEdgeDetectionFilter.threshold;
  180. }
  181. */
  182. - (void)setLineDetectionThreshold:(CGFloat)newValue;
  183. {
  184. nonMaximumSuppressionFilter.threshold = newValue;
  185. }
  186. - (CGFloat)lineDetectionThreshold;
  187. {
  188. return nonMaximumSuppressionFilter.threshold;
  189. }
  190. @end