Skip to content

Commit

Permalink
Improved the Hough transform line detector, as suggested by mabuchner…
Browse files Browse the repository at this point in the history
… in Issue BradLarson#1871. Also expanded the search radius for the non-maximum suppression filter.
  • Loading branch information
BradLarson committed Dec 17, 2014
1 parent b228793 commit 37d5ac6
Show file tree
Hide file tree
Showing 9 changed files with 171 additions and 86 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@
isa = PBXProject;
attributes = {
CLASSPREFIX = Showcase;
LastUpgradeCheck = 0500;
LastUpgradeCheck = 0610;
ORGANIZATIONNAME = "Cell Phone";
TargetAttributes = {
BC982B0114F046AC0001FF6F = {
Expand Down Expand Up @@ -368,9 +368,11 @@
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
Expand Down Expand Up @@ -408,9 +410,11 @@
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0500"
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0600"
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -39,6 +39,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BC552B361558C6F4001F3FFA"
BuildableName = "Documentation"
BlueprintName = "Documentation"
ReferencedContainer = "container:GPUImage.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0600"
LastUpgradeVersion = "0610"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -49,6 +49,15 @@
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BCF1A33314DDB1EC00852800"
BuildableName = "libGPUImage.a"
BlueprintName = "GPUImage"
ReferencedContainer = "container:GPUImage.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0600"
LastUpgradeVersion = "0610"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
Expand Down Expand Up @@ -40,6 +40,15 @@
debugDocumentVersioning = "YES"
debugXPCServices = "NO"
allowLocationSimulation = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "BCE209E41943F20C002FEED8"
BuildableName = "GPUImage.framework"
BlueprintName = "GPUImageFramework"
ReferencedContainer = "container:GPUImage.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
Expand Down
94 changes: 51 additions & 43 deletions framework/Source/GPUImageHoughTransformLineDetector.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,27 @@ - (id)init;
return nil;
}

#ifdef DEBUGLINEDETECTION
_intermediateImages = [[NSMutableArray alloc] init];
#endif

// First pass: do edge detection and threshold that to just have white pixels for edges
// if ([GPUImageContext deviceSupportsFramebufferReads])
// if ([GPUImageContext deviceSupportsFramebufferReads])
// {
thresholdEdgeDetectionFilter = [[GPUImageThresholdEdgeDetectionFilter alloc] init];
// thresholdEdgeDetectionFilter = [[GPUImageThresholdEdgeDetectionFilter alloc] init];
// thresholdEdgeDetectionFilter = [[GPUImageSobelEdgeDetectionFilter alloc] init];
[(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:0.4];
// [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:0.07];
// [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:0.25];
[(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:1.0];
// [(GPUImageThresholdEdgeDetectionFilter *)thresholdEdgeDetectionFilter setEdgeStrength:1.0];
// thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
// }
// else
// {
// thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
thresholdEdgeDetectionFilter = [[GPUImageCannyEdgeDetectionFilter alloc] init];
// }
[self addFilter:thresholdEdgeDetectionFilter];

#ifdef DEBUGLINEDETECTION
__unsafe_unretained NSMutableArray *weakIntermediateImages = _intermediateImages;
__unsafe_unretained GPUImageOutput<GPUImageInput> *weakFilter = thresholdEdgeDetectionFilter;
[thresholdEdgeDetectionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
[weakIntermediateImages removeAllObjects];
UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
[weakIntermediateImages addObject:intermediateImage];
}];
#endif

// Second pass: extract the white points and draw representative lines in parallel coordinate space
parallelCoordinateLineTransformFilter = [[GPUImageParallelCoordinateLineTransformFilter alloc] init];
[self addFilter:parallelCoordinateLineTransformFilter];

#ifdef DEBUGLINEDETECTION
weakFilter = parallelCoordinateLineTransformFilter;
[parallelCoordinateLineTransformFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
[weakIntermediateImages addObject:intermediateImage];
}];
#endif

// Third pass: apply non-maximum suppression
if ([GPUImageContext deviceSupportsFramebufferReads])
{
Expand All @@ -76,12 +54,27 @@ - (id)init;

__unsafe_unretained GPUImageHoughTransformLineDetector *weakSelf = self;
#ifdef DEBUGLINEDETECTION
weakFilter = nonMaximumSuppressionFilter;
[nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
_intermediateImages = [[NSMutableArray alloc] init];
__unsafe_unretained NSMutableArray *weakIntermediateImages = _intermediateImages;

UIImage *intermediateImage = [weakFilter imageFromCurrentlyProcessedOutput];
// __unsafe_unretained GPUImageOutput<GPUImageInput> *weakEdgeDetectionFilter = thresholdEdgeDetectionFilter;
// [thresholdEdgeDetectionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
// [weakIntermediateImages removeAllObjects];
// UIImage *intermediateImage = [weakEdgeDetectionFilter imageFromCurrentFramebuffer];
// [weakIntermediateImages addObject:intermediateImage];
// }];
//
// __unsafe_unretained GPUImageOutput<GPUImageInput> *weakParallelCoordinateLineTransformFilter = parallelCoordinateLineTransformFilter;
// [parallelCoordinateLineTransformFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
// UIImage *intermediateImage = [weakParallelCoordinateLineTransformFilter imageFromCurrentFramebuffer];
// [weakIntermediateImages addObject:intermediateImage];
// }];

__unsafe_unretained GPUImageOutput<GPUImageInput> *weakNonMaximumSuppressionFilter = nonMaximumSuppressionFilter;
[nonMaximumSuppressionFilter setFrameProcessingCompletionBlock:^(GPUImageOutput *filter, CMTime frameTime){
UIImage *intermediateImage = [weakNonMaximumSuppressionFilter imageFromCurrentFramebuffer];
[weakIntermediateImages addObject:intermediateImage];

[weakSelf extractLineParametersFromImageAtFrameTime:frameTime];
}];
#else
Expand All @@ -98,7 +91,7 @@ - (id)init;
self.terminalFilter = nonMaximumSuppressionFilter;

// self.edgeThreshold = 0.95;
self.lineDetectionThreshold = 0.8;
self.lineDetectionThreshold = 0.12;

return self;
}
Expand Down Expand Up @@ -137,15 +130,21 @@ - (void)extractLineParametersFromImageAtFrameTime:(CMTime)frameTime;

unsigned int currentByte = 0;
unsigned int cornerStorageIndex = 0;
unsigned long lineStrengthCounter = 0;
while (currentByte < imageByteSize)
{
GLubyte colorByte = rawImagePixels[currentByte];
// NSLog(@"(%d,%d): [%d,%d,%d,%d]", xCoordinate, yCoordinate, rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);
// NSLog(@"[%d,%d,%d,%d]", rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);

if (colorByte > 0)
{
unsigned int xCoordinate = currentByte % imageWidth;
unsigned int yCoordinate = currentByte / imageWidth;

lineStrengthCounter += colorByte;
// NSLog(@"(%d,%d): [%d,%d,%d,%d]", xCoordinate, yCoordinate, rawImagePixels[currentByte], rawImagePixels[currentByte+1], rawImagePixels[currentByte+2], rawImagePixels[currentByte+3]);

CGFloat normalizedXCoordinate = -1.0 + 2.0 * (CGFloat)(xCoordinate / 4) / imageSize.width;
CGFloat normalizedYCoordinate = -1.0 + 2.0 * (CGFloat)(yCoordinate) / imageSize.height;

Expand Down Expand Up @@ -192,7 +191,7 @@ - (void)extractLineParametersFromImageAtFrameTime:(CMTime)frameTime;

// CFAbsoluteTime currentFrameTime = (CFAbsoluteTimeGetCurrent() - startTime);
// NSLog(@"Processing time : %f ms", 1000.0 * currentFrameTime);

if (linesDetectedBlock != NULL)
{
linesDetectedBlock(linesArray, numberOfLines, frameTime);
Expand All @@ -208,17 +207,15 @@ - (BOOL)wantsMonochromeInput;
#pragma mark -
#pragma mark Accessors

/*
- (void)setEdgeThreshold:(CGFloat)newValue;
{
thresholdEdgeDetectionFilter.threshold = newValue;
}
- (CGFloat)edgeThreshold;
{
return thresholdEdgeDetectionFilter.threshold;
}
*/
//- (void)setEdgeThreshold:(CGFloat)newValue;
//{
// [(GPUImageCannyEdgeDetectionFilter *)thresholdEdgeDetectionFilter setThreshold:newValue];
//}
//
//- (CGFloat)edgeThreshold;
//{
// return [(GPUImageCannyEdgeDetectionFilter *)thresholdEdgeDetectionFilter threshold];
//}

- (void)setLineDetectionThreshold:(CGFloat)newValue;
{
Expand All @@ -230,4 +227,15 @@ - (CGFloat)lineDetectionThreshold;
return nonMaximumSuppressionFilter.threshold;
}

#ifdef DEBUGLINEDETECTION
- (void)newFrameReadyAtTime:(CMTime)frameTime atIndex:(NSInteger)textureIndex;
{
// [thresholdEdgeDetectionFilter useNextFrameForImageCapture];
// [parallelCoordinateLineTransformFilter useNextFrameForImageCapture];
[nonMaximumSuppressionFilter useNextFrameForImageCapture];

[super newFrameReadyAtTime:frameTime atIndex:textureIndex];
}
#endif

@end
12 changes: 6 additions & 6 deletions framework/Source/GPUImageNonMaximumSuppressionFilter.m
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ void main()

// Use a tiebreaker for pixels to the left and immediately above this one
lowp float multiplier = 1.0 - step(centerColor.r, topColor);
multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
multiplier = multiplier * (1.0 - step(centerColor.r, topLeftColor));
multiplier = multiplier * (1.0 - step(centerColor.r, leftColor));
multiplier = multiplier * (1.0 - step(centerColor.r, bottomLeftColor));

lowp float maxValue = max(centerColor.r, bottomColor);
maxValue = max(maxValue, bottomRightColor);
Expand Down Expand Up @@ -74,9 +74,9 @@ void main()

// Use a tiebreaker for pixels to the left and immediately above this one
float multiplier = 1.0 - step(centerColor.r, topColor);
multiplier = multiplier * 1.0 - step(centerColor.r, topLeftColor);
multiplier = multiplier * 1.0 - step(centerColor.r, leftColor);
multiplier = multiplier * 1.0 - step(centerColor.r, bottomLeftColor);
multiplier = multiplier * (1.0 - step(centerColor.r, topLeftColor));
multiplier = multiplier * (1.0 - step(centerColor.r, leftColor));
multiplier = multiplier * (1.0 - step(centerColor.r, bottomLeftColor));

float maxValue = max(centerColor.r, bottomColor);
maxValue = max(maxValue, bottomRightColor);
Expand Down
29 changes: 21 additions & 8 deletions framework/Source/GPUImageParallelCoordinateLineTransformFilter.m
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ void main()
// NOTE: See below for where I'm tacking on the required extension as a prefix
NSString *const kGPUImageHoughAccumulationFBOReadFragmentShaderString = SHADER_STRING
(
// const lowp float scalingFactor = 0.004;
const lowp float scalingFactor = 0.1;
const lowp float scalingFactor = 0.004;
// const lowp float scalingFactor = 0.1;

void main()
{
Expand Down Expand Up @@ -149,10 +149,12 @@ - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates
return;
}

[GPUImageContext useImageProcessingContext];

// Grab the edge points from the previous frame and create the parallel coordinate lines for them
// This would be a great place to have a working histogram pyramid implementation

[GPUImageContext useImageProcessingContext];
[firstInputFramebuffer activateFramebuffer];

glFinish();
glReadPixels(0, 0, inputTextureSize.width, inputTextureSize.height, GL_RGBA, GL_UNSIGNED_BYTE, rawImagePixels);

Expand Down Expand Up @@ -223,9 +225,15 @@ - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates

outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:[self sizeOfFBO] textureOptions:self.outputTextureOptions onlyTexture:NO];
[outputFramebuffer activateFramebuffer];


if (usingNextFrameForImageCapture)
{
[outputFramebuffer lock];
}

[GPUImageContext setActiveShaderProgram:filterProgram];

[self setUniformsForProgramAtIndex:0];

glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);

Expand All @@ -237,9 +245,10 @@ - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates
}
else
{
glLineWidth(1);
}


glLineWidth(1);

glVertexAttribPointer(filterPositionAttribute, 2, GL_FLOAT, 0, 0, lineCoordinates);
glDrawArrays(GL_LINES, 0, (linePairsToRender * 4));

Expand All @@ -248,6 +257,10 @@ - (void)renderToTextureWithVertices:(const GLfloat *)vertices textureCoordinates
glDisable(GL_BLEND);
}
[firstInputFramebuffer unlock];
if (usingNextFrameForImageCapture)
{
dispatch_semaphore_signal(imageCaptureSemaphore);
}
}

@end
Loading

0 comments on commit 37d5ac6

Please sign in to comment.