diff --git a/README.md b/README.md index 5b7c5e600..5c096169f 100755 --- a/README.md +++ b/README.md @@ -168,6 +168,14 @@ Documentation is generated from header comments using appledoc. To build the doc - **GPUImageCrosshairGenerator**: This draws a series of crosshairs on an image, most often used for identifying machine vision features. It does not take in a standard image like other filters, but a series of points in its -renderCrosshairsFromArray:count: method, which does the actual drawing. You will need to force this filter to render at the particular output size you need. - *crosshairWidth*: The width, in pixels, of the crosshairs to be drawn onscreen. +- **GPUImageDilationFilter**: This performs an image dilation operation, where the maximum intensity of the red channel in a rectangular neighborhood is used for the intensity of this pixel. The radius of the rectangular area to sample over is specified on initialization, with a range of 1-4 pixels. This is intended for use with grayscale images, and it expands bright regions. + +- **GPUImageErosionFilter**: This performs an image erosion operation, where the minimum intensity of the red channel in a rectangular neighborhood is used for the intensity of this pixel. The radius of the rectangular area to sample over is specified on initialization, with a range of 1-4 pixels. This is intended for use with grayscale images, and it expands dark regions. + +- **GPUImageOpeningFilter**: This performs an erosion on the red channel of an image, followed by a dilation of the same radius. The radius is set on initialization, with a range of 1-4 pixels. This filters out smaller bright regions. + +- **GPUImageClosingFilter**: This performs a dilation on the red channel of an image, followed by an erosion of the same radius. The radius is set on initialization, with a range of 1-4 pixels. This filters out smaller dark regions. + ### Blending modes ### - **GPUImageChromaKeyBlendFilter**: Selectively replaces a color in the first image with the second image diff --git a/examples/FeatureExtractionTest/FeatureExtractionTest/FeatureExtractionAppDelegate.m b/examples/FeatureExtractionTest/FeatureExtractionTest/FeatureExtractionAppDelegate.m index b2f6b14f5..e2519db77 100644 --- a/examples/FeatureExtractionTest/FeatureExtractionTest/FeatureExtractionAppDelegate.m +++ b/examples/FeatureExtractionTest/FeatureExtractionTest/FeatureExtractionAppDelegate.m @@ -33,7 +33,21 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( [blackAndWhiteBoxImage processImage]; UIImage *dilationImage = [dilationFilter imageFromCurrentlyProcessedOutput]; [self saveImage:dilationImage fileName:@"Dilation4.png"]; - + + GPUImageOpeningFilter *openingFilter = [[GPUImageOpeningFilter alloc] initWithRadius:4]; + [blackAndWhiteBoxImage removeAllTargets]; + [blackAndWhiteBoxImage addTarget:openingFilter]; + [blackAndWhiteBoxImage processImage]; + UIImage *openingImage = [openingFilter imageFromCurrentlyProcessedOutput]; + [self saveImage:openingImage fileName:@"Opening4.png"]; + + GPUImageClosingFilter *closingFilter = [[GPUImageClosingFilter alloc] initWithRadius:4]; + [blackAndWhiteBoxImage removeAllTargets]; + [blackAndWhiteBoxImage addTarget:closingFilter]; + [blackAndWhiteBoxImage processImage]; + UIImage *closingImage = [closingFilter imageFromCurrentlyProcessedOutput]; + [self saveImage:closingImage fileName:@"Closing4.png"]; + return YES; } diff --git a/framework/GPUImage.xcodeproj/project.pbxproj b/framework/GPUImage.xcodeproj/project.pbxproj index 32066bbc7..e928531cd 100644 --- a/framework/GPUImage.xcodeproj/project.pbxproj +++ b/framework/GPUImage.xcodeproj/project.pbxproj @@ -39,6 +39,10 @@ BC01E833155CA5E2004C75C3 /* GPUImage3x3TextureSamplingFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BC01E831155CA5E1004C75C3 /* GPUImage3x3TextureSamplingFilter.m */; }; BC0690B8157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = BC0690B6157C0C27009274F9 /* GPUImageTwoPassTextureSamplingFilter.h */; }; BC0690B9157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0690B7157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.m */; }; + BC0690BD157C1B38009274F9 /* GPUImageOpeningFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = BC0690BB157C1B37009274F9 /* GPUImageOpeningFilter.h */; }; + BC0690BE157C1B38009274F9 /* GPUImageOpeningFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0690BC157C1B38009274F9 /* GPUImageOpeningFilter.m */; }; + BC0690C1157C2368009274F9 /* GPUImageClosingFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = BC0690BF157C2366009274F9 /* GPUImageClosingFilter.h */; }; + BC0690C2157C2368009274F9 /* GPUImageClosingFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BC0690C0157C2367009274F9 /* GPUImageClosingFilter.m */; }; BC114898155AF65400F107AF /* GPUImageTwoInputFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = BC114896155AF65400F107AF /* GPUImageTwoInputFilter.h */; }; BC114899155AF65400F107AF /* GPUImageTwoInputFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = BC114897155AF65400F107AF /* GPUImageTwoInputFilter.m */; }; BC1A47F514FC759D00D552E8 /* GPUImageGaussianBlurFilter.m in Sources */ = {isa = PBXBuildFile; fileRef = 0D04CB7B14FB2A29001D6733 /* GPUImageGaussianBlurFilter.m */; }; @@ -273,6 +277,10 @@ BC01E831155CA5E1004C75C3 /* GPUImage3x3TextureSamplingFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUImage3x3TextureSamplingFilter.m; path = Source/GPUImage3x3TextureSamplingFilter.m; sourceTree = SOURCE_ROOT; }; BC0690B6157C0C27009274F9 /* GPUImageTwoPassTextureSamplingFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUImageTwoPassTextureSamplingFilter.h; path = Source/GPUImageTwoPassTextureSamplingFilter.h; sourceTree = SOURCE_ROOT; }; BC0690B7157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUImageTwoPassTextureSamplingFilter.m; path = Source/GPUImageTwoPassTextureSamplingFilter.m; sourceTree = SOURCE_ROOT; }; + BC0690BB157C1B37009274F9 /* GPUImageOpeningFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUImageOpeningFilter.h; path = Source/GPUImageOpeningFilter.h; sourceTree = SOURCE_ROOT; }; + BC0690BC157C1B38009274F9 /* GPUImageOpeningFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUImageOpeningFilter.m; path = Source/GPUImageOpeningFilter.m; sourceTree = SOURCE_ROOT; }; + BC0690BF157C2366009274F9 /* GPUImageClosingFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUImageClosingFilter.h; path = Source/GPUImageClosingFilter.h; sourceTree = SOURCE_ROOT; }; + BC0690C0157C2367009274F9 /* GPUImageClosingFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUImageClosingFilter.m; path = Source/GPUImageClosingFilter.m; sourceTree = SOURCE_ROOT; }; BC114896155AF65400F107AF /* GPUImageTwoInputFilter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUImageTwoInputFilter.h; path = Source/GPUImageTwoInputFilter.h; sourceTree = SOURCE_ROOT; }; BC114897155AF65400F107AF /* GPUImageTwoInputFilter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GPUImageTwoInputFilter.m; path = Source/GPUImageTwoInputFilter.m; sourceTree = SOURCE_ROOT; }; BC1B715514F49DAA00ACA2AB /* GPUImageRawDataOutput.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GPUImageRawDataOutput.h; path = Source/GPUImageRawDataOutput.h; sourceTree = SOURCE_ROOT; }; @@ -749,6 +757,10 @@ BC56D849157ADA4F00CC9C1E /* GPUImageDilationFilter.m */, BC56D84D157ADA6F00CC9C1E /* GPUImageErosionFilter.h */, BC56D84E157ADA6F00CC9C1E /* GPUImageErosionFilter.m */, + BC0690BB157C1B37009274F9 /* GPUImageOpeningFilter.h */, + BC0690BC157C1B38009274F9 /* GPUImageOpeningFilter.m */, + BC0690BF157C2366009274F9 /* GPUImageClosingFilter.h */, + BC0690C0157C2367009274F9 /* GPUImageClosingFilter.m */, ); name = "Image processing"; sourceTree = ""; @@ -942,6 +954,8 @@ BC56D84A157ADA4F00CC9C1E /* GPUImageDilationFilter.h in Headers */, BC56D84F157ADA6F00CC9C1E /* GPUImageErosionFilter.h in Headers */, BC0690B8157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.h in Headers */, + BC0690BD157C1B38009274F9 /* GPUImageOpeningFilter.h in Headers */, + BC0690C1157C2368009274F9 /* GPUImageClosingFilter.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1158,6 +1172,8 @@ BC56D84B157ADA4F00CC9C1E /* GPUImageDilationFilter.m in Sources */, BC56D850157ADA6F00CC9C1E /* GPUImageErosionFilter.m in Sources */, BC0690B9157C0C28009274F9 /* GPUImageTwoPassTextureSamplingFilter.m in Sources */, + BC0690BE157C1B38009274F9 /* GPUImageOpeningFilter.m in Sources */, + BC0690C2157C2368009274F9 /* GPUImageClosingFilter.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/framework/Source/GPUImage.h b/framework/Source/GPUImage.h index 183b54646..6b18188fa 100755 --- a/framework/Source/GPUImage.h +++ b/framework/Source/GPUImage.h @@ -94,3 +94,5 @@ #import "GPUImageShiTomasiFeatureDetectionFilter.h" #import "GPUImageErosionFilter.h" #import "GPUImageDilationFilter.h" +#import "GPUImageOpeningFilter.h" +#import "GPUImageClosingFilter.h" diff --git a/framework/Source/GPUImageClosingFilter.h b/framework/Source/GPUImageClosingFilter.h new file mode 100644 index 000000000..691d9d883 --- /dev/null +++ b/framework/Source/GPUImageClosingFilter.h @@ -0,0 +1,17 @@ +#import "GPUImageFilterGroup.h" + +@class GPUImageErosionFilter; +@class GPUImageDilationFilter; + +// A filter that first performs a dilation on the red channel of an image, followed by an erosion of the same radius. +// This helps to filter out smaller dark elements. + +@interface GPUImageClosingFilter : GPUImageFilterGroup +{ + GPUImageErosionFilter *erosionFilter; + GPUImageDilationFilter *dilationFilter; +} + +- (id)initWithRadius:(NSUInteger)radius; + +@end diff --git a/framework/Source/GPUImageClosingFilter.m b/framework/Source/GPUImageClosingFilter.m new file mode 100644 index 000000000..87e825fa1 --- /dev/null +++ b/framework/Source/GPUImageClosingFilter.m @@ -0,0 +1,41 @@ +#import "GPUImageClosingFilter.h" +#import "GPUImageErosionFilter.h" +#import "GPUImageDilationFilter.h" + +@implementation GPUImageClosingFilter + +- (id)init; +{ + if (!(self = [self initWithRadius:1])) + { + return nil; + } + + return self; +} + +- (id)initWithRadius:(NSUInteger)radius; +{ + if (!(self = [super init])) + { + return nil; + } + + // First pass: dilation + dilationFilter = [[GPUImageDilationFilter alloc] initWithRadius:radius]; + [self addFilter:dilationFilter]; + + // Second pass: erosion + erosionFilter = [[GPUImageErosionFilter alloc] initWithRadius:radius]; + [self addFilter:erosionFilter]; + + [dilationFilter addTarget:erosionFilter]; + + self.initialFilters = [NSArray arrayWithObjects:dilationFilter, nil]; + self.terminalFilter = erosionFilter; + + return self; +} + + +@end diff --git a/framework/Source/GPUImageOpeningFilter.h b/framework/Source/GPUImageOpeningFilter.h new file mode 100644 index 000000000..d3d833563 --- /dev/null +++ b/framework/Source/GPUImageOpeningFilter.h @@ -0,0 +1,17 @@ +#import "GPUImageFilterGroup.h" + +@class GPUImageErosionFilter; +@class GPUImageDilationFilter; + +// A filter that first performs an erosion on the red channel of an image, followed by a dilation of the same radius. +// This helps to filter out smaller bright elements. + +@interface GPUImageOpeningFilter : GPUImageFilterGroup +{ + GPUImageErosionFilter *erosionFilter; + GPUImageDilationFilter *dilationFilter; +} + +- (id)initWithRadius:(NSUInteger)radius; + +@end diff --git a/framework/Source/GPUImageOpeningFilter.m b/framework/Source/GPUImageOpeningFilter.m new file mode 100644 index 000000000..9b81dd613 --- /dev/null +++ b/framework/Source/GPUImageOpeningFilter.m @@ -0,0 +1,40 @@ +#import "GPUImageOpeningFilter.h" +#import "GPUImageErosionFilter.h" +#import "GPUImageDilationFilter.h" + +@implementation GPUImageOpeningFilter + +- (id)init; +{ + if (!(self = [self initWithRadius:1])) + { + return nil; + } + + return self; +} + +- (id)initWithRadius:(NSUInteger)radius; +{ + if (!(self = [super init])) + { + return nil; + } + + // First pass: erosion + erosionFilter = [[GPUImageErosionFilter alloc] initWithRadius:radius]; + [self addFilter:erosionFilter]; + + // Second pass: dilation + dilationFilter = [[GPUImageDilationFilter alloc] initWithRadius:radius]; + [self addFilter:dilationFilter]; + + [erosionFilter addTarget:dilationFilter]; + + self.initialFilters = [NSArray arrayWithObjects:erosionFilter, nil]; + self.terminalFilter = dilationFilter; + + return self; +} + +@end