GPUImageHoughTransformLineDetector.m 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  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. // First pass: do edge detection and threshold that to just have white pixels for edges
  17. // if ([GPUImageContext deviceSupportsFramebufferReads])
  18. // if ([GPUImageContext deviceSupportsFramebufferReads])
  19. // {
  20. // thresholdEdgeDetectionFilter = [[GPUImageThresholdEdgeDetectionFilter alloc] init];
  21. // thresholdEdgeDetectionFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
  22. // [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:0.07];
  23. // [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:0.25];
  24. // [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:1.0];
  25. // thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
  26. // }
  27. // else
  28. // {
  29. thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
  30. // }
  31. [self addFilter:thresholdEdgeDetectionFilter];
  32. // Second pass: extract the white points and draw representative lines in parallel coordinate space
  33. parallelCoordinateLineTransformFilter = [[GPUImageParallelCoordinateLineTransformFilter alloc] init];
  34. [self addFilter:parallelCoordinateLineTransformFilter];
  35. // Third pass: apply non-maximum suppression
  36. if ([GPUImageContext deviceSupportsFramebufferReads])
  37. {
  38. nonMaximumSuppressionFilter = [[GPUImageThresholdedNonMaximumSuppressionFilter alloc] initWithPackedColorspace:YES];
  39. }
  40. else
  41. {
  42. nonMaximumSuppressionFilter = [[GPUImageThresholdedNonMaximumSuppressionFilter alloc] initWithPackedColorspace:NO];
  43. }
  44. [self addFilter:nonMaximumSuppressionFilter];
  45. __unsafe_unretained GPUImageHoughTransformLineDetector *weakSelf = self;
  46. #ifdef DEBUGLINEDETECTION
  47. _intermediateImages = [[NSMutableArray alloc] init];
  48. __unsafe_unretained NSMutableArray *weakIntermediateImages = _intermediateImages;
  49. // __unsafe_unretained GPUImageOutput<GPUImageInput> *weakEdgeDetectionFilter = thresholdEdgeDetectionFilter;
  50. // [thresholdEdgeDetectionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  51. // [weakIntermediateImages removeAllObjects];
  52. // UIImage *intermediateImage = [weakEdgeDetectionFilter imageFromCurrentFramebuffer];
  53. // [weakIntermediateImages addObject:intermediateImage];
  54. // }];
  55. //
  56. // __unsafe_unretained GPUImageOutput<GPUImageInput> *weakParallelCoordinateLineTransformFilter = parallelCoordinateLineTransformFilter;
  57. // [parallelCoordinateLineTransformFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  58. // UIImage *intermediateImage = [weakParallelCoordinateLineTransformFilter imageFromCurrentFramebuffer];
  59. // [weakIntermediateImages addObject:intermediateImage];
  60. // }];
  61. __unsafe_unretained GPUImageOutput<GPUImageInput> *weakNonMaximumSuppressionFilter = nonMaximumSuppressionFilter;
  62. [nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
  63. UIImage *intermediateImage = [weakNonMaximumSuppressionFilter imageFromCurrentFramebuffer];
  64. [weakIntermediateImages addObject:intermediateImage];
  65. [weakSelf extractLineParametersFromImageAtFrameTime:frameTime];
  66. }];
  67. #else
  68. [nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime) {
  69. [weakSelf extractLineParametersFromImageAtFrameTime:frameTime];
  70. }];
  71. #endif
  72. [thresholdEdgeDetectionFilter addTarget:parallelCoordinateLineTransformFilter];
  73. [parallelCoordinateLineTransformFilter addTarget:nonMaximumSuppressionFilter];
  74. self.initialFilters = [NSArray arrayWithObjects:thresholdEdgeDetectionFilter, nil];
  75. // self.terminalFilter = colorPackingFilter;
  76. self.terminalFilter = nonMaximumSuppressionFilter;
  77. // self.edgeThreshold = 0.95;
  78. self.lineDetectionThreshold = 0.12;
  79. return self;
  80. }
  81. - (void)dealloc;
  82. {
  83. free(rawImagePixels);
  84. free(linesArray);
  85. }
  86. #pragma mark -
  87. #pragma mark Corner extraction
  88. - (void)extractLineParametersFromImageAtFrameTime:(CMTime)frameTime;
  89. {
  90. // we need a normal color texture for this filter
  91. NSAssert(self.outputTextureOptions.internalFormat == GL_RGBA, @"The output texture format for this filter must be GL_RGBA.");
  92. NSAssert(self.outputTextureOptions.type == GL_UNSIGNED_BYTE, @"The type of the output texture of this filter must be GL_UNSIGNED_BYTE.");
  93. NSUInteger numberOfLines = 0;
  94. CGSize imageSize = nonMaximumSuppressionFilter.outputFrameSize;
  95. unsigned int imageByteSize = imageSize.width * imageSize.height * 4;
  96. if (rawImagePixels == NULL)
  97. {
  98. rawImagePixels = (GLubyte *)malloc(imageByteSize);
  99. linesArray = calloc(1024 * 2, sizeof(GLfloat));
  100. }
  101. glReadPixels(0, 0, (int)imageSize.width, (int)imageSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);
  102. // CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();
  103. unsigned int imageWidth = imageSize.width * 4;
  104. unsigned int currentByte = 0;
  105. unsigned int cornerStorageIndex = 0;
  106. unsigned long lineStrengthCounter = 0;
  107. while (currentByte < imageByteSize)
  108. {
  109. GLubyte colorByte = rawImagePixels[currentByte];
  110. // NSLog(@"(%d,%d): [%d,%d,%d,%d]", xCoordinate, yCoordinate, rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);
  111. // NSLog(@"[%d,%d,%d,%d]", rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);
  112. if (colorByte > 0)
  113. {
  114. unsigned int xCoordinate = currentByte % imageWidth;
  115. unsigned int yCoordinate = currentByte / imageWidth;
  116. lineStrengthCounter += colorByte;
  117. // NSLog(@"(%d,%d): [%d,%d,%d,%d]", xCoordinate, yCoordinate, rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);
  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. //- (void)setEdgeThreshold:(CGFloat)newValue;
  173. //{
  174. // [(GPUImageCannyEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:newValue];
  175. //}
  176. //
  177. //- (CGFloat)edgeThreshold;
  178. //{
  179. // return [(GPUImageCannyEdgeDetectionFilter *)thresholdEdgeDetectionFilter threshold];
  180. //}
  181. - (void)setLineDetectionThreshold:(CGFloat)newValue;
  182. {
  183. nonMaximumSuppressionFilter.threshold = newValue;
  184. }
  185. - (CGFloat)lineDetectionThreshold;
  186. {
  187. return nonMaximumSuppressionFilter.threshold;
  188. }
  189. #ifdef DEBUGLINEDETECTION
  190. - (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
  191. {
  192. // [thresholdEdgeDetectionFilter useNextFrameForImageCapture];
  193. // [parallelCoordinateLineTransformFilter useNextFrameForImageCapture];
  194. [nonMaximumSuppressionFilter useNextFrameForImageCapture];
  195. [super newFrameReadyAtTime:frameTime atIndex:textureIndex];
  196. }
  197. #endif
  198. @end