Skip to content

Commit

Permalink
Added the histogram equalization filter.
Browse files Browse the repository at this point in the history
  • Loading branch information
BradLarson committed Apr 24, 2016
1 parent f26626b commit 805bf5d
Show file tree
Hide file tree
Showing 21 changed files with 261 additions and 8 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,9 @@ There are currently over 100 operations built into the framework, divided into t

- **HistogramDisplay**: This is a special filter, in that it's primarily intended to work with the Histogram. It generates an output representation of the color histograms generated by Histogram, but it could be repurposed to display other kinds of values. It takes in an image and looks at the center (vertical) pixels. It then plots the numerical values of the RGB components in separate colored graphs in an output texture. You may need to force a size for this filter in order to make its output visible.

- **HistogramEqualization**: This takes a image, analyzes its histogram, and equalizes the outbound image based on that.
- *downsamplingFactor*: Rather than sampling every pixel, this dictates what fraction of the image is sampled by the histogram. By default, this is 16 with a minimum of 1. This is needed to keep from saturating the histogram, which can only record 256 pixels for each color value before it becomes overloaded.

- **CannyEdgeDetection**: This uses the full Canny process to highlight one-pixel-wide edges
- *blurRadiusInPixels*: The underlying blur radius for the Gaussian blur. Default is 2.0.
- *upperThreshold*: Any edge with a gradient magnitude above this threshold will pass and show up in the final result. Default is 0.4.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,14 @@ let filterOperations: Array<FilterOperationInterface> = [
return blendFilter
})
),
FilterOperation (
filter:{HistogramEqualization(type:.RGB)},
listName:"Histogram equalization",
titleName:"Histogram Equalization",
sliderConfiguration:.Disabled,
sliderUpdateCallback:nil,
filterOperationType:.SingleInput
),
FilterOperation(
filter:{AverageColorExtractor()},
listName:"Average color",
Expand Down
14 changes: 14 additions & 0 deletions framework/GPUImage-Mac.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@
BCB825B61CC9C1F100339790 /* MovieOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB825B51CC9C1F100339790 /* MovieOutput.swift */; };
BCB825BB1CC9C96B00339790 /* Timestamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB825BA1CC9C96B00339790 /* Timestamp.swift */; };
BCBEC0C61CCD2E6200B70ED7 /* Histogram.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCBEC0C51CCD2E6200B70ED7 /* Histogram.swift */; };
BCBEC0E01CCD492D00B70ED7 /* HistogramEqualization.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCBEC0DF1CCD492D00B70ED7 /* HistogramEqualization.swift */; };
BCD1B14A1C66AE00001F2BDC /* SerialDispatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1B1491C66AE00001F2BDC /* SerialDispatch.swift */; };
BCD1B14C1C66B225001F2BDC /* Pipeline.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1B14B1C66B225001F2BDC /* Pipeline.swift */; };
BCE111A51CBC94FD005293A4 /* AverageLuminanceExtractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE111A41CBC94FD005293A4 /* AverageLuminanceExtractor.swift */; };
Expand Down Expand Up @@ -410,6 +411,12 @@
BCBEC0C91CCD2E8900B70ED7 /* HistogramGreenSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramGreenSampling.vsh; path = Source/Operations/Shaders/HistogramGreenSampling.vsh; sourceTree = "<group>"; };
BCBEC0CA1CCD2E8900B70ED7 /* HistogramLuminanceSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramLuminanceSampling.vsh; path = Source/Operations/Shaders/HistogramLuminanceSampling.vsh; sourceTree = "<group>"; };
BCBEC0CB1CCD2E8900B70ED7 /* HistogramRedSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramRedSampling.vsh; path = Source/Operations/Shaders/HistogramRedSampling.vsh; sourceTree = "<group>"; };
BCBEC0DF1CCD492D00B70ED7 /* HistogramEqualization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HistogramEqualization.swift; path = Source/Operations/HistogramEqualization.swift; sourceTree = "<group>"; };
BCBEC0E11CCD496B00B70ED7 /* HistogramEqualizationBlue_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationBlue_GL.fsh; path = Source/Operations/Shaders/HistogramEqualizationBlue_GL.fsh; sourceTree = "<group>"; };
BCBEC0E21CCD496B00B70ED7 /* HistogramEqualizationGreen_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationGreen_GL.fsh; path = Source/Operations/Shaders/HistogramEqualizationGreen_GL.fsh; sourceTree = "<group>"; };
BCBEC0E31CCD496B00B70ED7 /* HistogramEqualizationLuminance_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationLuminance_GL.fsh; path = Source/Operations/Shaders/HistogramEqualizationLuminance_GL.fsh; sourceTree = "<group>"; };
BCBEC0E41CCD496B00B70ED7 /* HistogramEqualizationRed_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationRed_GL.fsh; path = Source/Operations/Shaders/HistogramEqualizationRed_GL.fsh; sourceTree = "<group>"; };
BCBEC0E51CCD496B00B70ED7 /* HistogramEqualizationRGB_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationRGB_GL.fsh; path = Source/Operations/Shaders/HistogramEqualizationRGB_GL.fsh; sourceTree = "<group>"; };
BCD1B1491C66AE00001F2BDC /* SerialDispatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = SerialDispatch.swift; path = Source/SerialDispatch.swift; sourceTree = "<group>"; };
BCD1B14B1C66B225001F2BDC /* Pipeline.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Pipeline.swift; path = Source/Pipeline.swift; sourceTree = "<group>"; };
BCE111A41CBC94FD005293A4 /* AverageLuminanceExtractor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AverageLuminanceExtractor.swift; path = Source/Operations/AverageLuminanceExtractor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -652,6 +659,12 @@
BC4EE1651CB34B3700AD8A65 /* HistogramDisplay.swift */,
BC4EE1681CB34B8900AD8A65 /* HistogramDisplay.vsh */,
BC4EE1671CB34B8900AD8A65 /* HistogramDisplay_GL.fsh */,
BCBEC0DF1CCD492D00B70ED7 /* HistogramEqualization.swift */,
BCBEC0E11CCD496B00B70ED7 /* HistogramEqualizationBlue_GL.fsh */,
BCBEC0E21CCD496B00B70ED7 /* HistogramEqualizationGreen_GL.fsh */,
BCBEC0E31CCD496B00B70ED7 /* HistogramEqualizationLuminance_GL.fsh */,
BCBEC0E41CCD496B00B70ED7 /* HistogramEqualizationRed_GL.fsh */,
BCBEC0E51CCD496B00B70ED7 /* HistogramEqualizationRGB_GL.fsh */,
BC4EE1731CB3711600AD8A65 /* GaussianBlur.swift */,
BCFF46E11CBADB3E00A0C521 /* SingleComponentGaussianBlur.swift */,
BC8B7A8E1CB5A84C00ACF7AA /* BoxBlur.swift */,
Expand Down Expand Up @@ -1111,6 +1124,7 @@
BC77295C1CB30CD2004C9E0B /* TextureSamplingOperation.swift in Sources */,
BC1E13291C9F6282008F844F /* PictureInput.swift in Sources */,
BC7FD10E1CB0783500037949 /* ExclusionBlend.swift in Sources */,
BCBEC0E01CCD492D00B70ED7 /* HistogramEqualization.swift in Sources */,
BC7FD0F91CB063B700037949 /* Color.swift in Sources */,
BCFF46DF1CBAB66F00A0C521 /* CannyEdgeDetection.swift in Sources */,
BCFF46BC1CB8AB1B00A0C521 /* TiltShift.swift in Sources */,
Expand Down
24 changes: 24 additions & 0 deletions framework/GPUImage-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@
BCB825BE1CC9CA6B00339790 /* Timestamp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB825BD1CC9CA6B00339790 /* Timestamp.swift */; };
BCB825C01CC9CA8100339790 /* MovieOutput.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB825BF1CC9CA8100339790 /* MovieOutput.swift */; };
BCBEC0D21CCD404900B70ED7 /* Histogram.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCBEC0D11CCD404900B70ED7 /* Histogram.swift */; };
BCBEC0EC1CCD499D00B70ED7 /* HistogramEqualization.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCBEC0EB1CCD499D00B70ED7 /* HistogramEqualization.swift */; };
BCBEC0F21CCD49B800B70ED7 /* HistogramEqualizationBlue_GLES.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCBEC0ED1CCD49B800B70ED7 /* HistogramEqualizationBlue_GLES.fsh */; };
BCBEC0F31CCD49B800B70ED7 /* HistogramEqualizationGreen_GLES.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCBEC0EE1CCD49B800B70ED7 /* HistogramEqualizationGreen_GLES.fsh */; };
BCBEC0F41CCD49B800B70ED7 /* HistogramEqualizationLuminance_GLES.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCBEC0EF1CCD49B800B70ED7 /* HistogramEqualizationLuminance_GLES.fsh */; };
BCBEC0F51CCD49B800B70ED7 /* HistogramEqualizationRed_GLES.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCBEC0F01CCD49B800B70ED7 /* HistogramEqualizationRed_GLES.fsh */; };
BCBEC0F61CCD49B800B70ED7 /* HistogramEqualizationRGB_GLES.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCBEC0F11CCD49B800B70ED7 /* HistogramEqualizationRGB_GLES.fsh */; };
BCD03F0F1CA23D6300271751 /* Camera.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD03F0E1CA23D6300271751 /* Camera.swift */; };
BCD1B1301C66A262001F2BDC /* GPUImage.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCD1B1251C66A262001F2BDC /* GPUImage.framework */; };
BCD1B1351C66A262001F2BDC /* GPUImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD1B1341C66A262001F2BDC /* GPUImageTests.swift */; };
Expand Down Expand Up @@ -210,6 +216,12 @@
BCBEC0D51CCD407500B70ED7 /* HistogramGreenSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramGreenSampling.vsh; path = Source/Operations/Shaders/HistogramGreenSampling.vsh; sourceTree = "<group>"; };
BCBEC0D61CCD407500B70ED7 /* HistogramLuminanceSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramLuminanceSampling.vsh; path = Source/Operations/Shaders/HistogramLuminanceSampling.vsh; sourceTree = "<group>"; };
BCBEC0D71CCD407500B70ED7 /* HistogramRedSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramRedSampling.vsh; path = Source/Operations/Shaders/HistogramRedSampling.vsh; sourceTree = "<group>"; };
BCBEC0EB1CCD499D00B70ED7 /* HistogramEqualization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HistogramEqualization.swift; path = Source/Operations/HistogramEqualization.swift; sourceTree = "<group>"; };
BCBEC0ED1CCD49B800B70ED7 /* HistogramEqualizationBlue_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationBlue_GLES.fsh; path = Source/Operations/Shaders/HistogramEqualizationBlue_GLES.fsh; sourceTree = "<group>"; };
BCBEC0EE1CCD49B800B70ED7 /* HistogramEqualizationGreen_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationGreen_GLES.fsh; path = Source/Operations/Shaders/HistogramEqualizationGreen_GLES.fsh; sourceTree = "<group>"; };
BCBEC0EF1CCD49B800B70ED7 /* HistogramEqualizationLuminance_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationLuminance_GLES.fsh; path = Source/Operations/Shaders/HistogramEqualizationLuminance_GLES.fsh; sourceTree = "<group>"; };
BCBEC0F01CCD49B800B70ED7 /* HistogramEqualizationRed_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationRed_GLES.fsh; path = Source/Operations/Shaders/HistogramEqualizationRed_GLES.fsh; sourceTree = "<group>"; };
BCBEC0F11CCD49B800B70ED7 /* HistogramEqualizationRGB_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramEqualizationRGB_GLES.fsh; path = Source/Operations/Shaders/HistogramEqualizationRGB_GLES.fsh; sourceTree = "<group>"; };
BCD03F0E1CA23D6300271751 /* Camera.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Camera.swift; path = Source/iOS/Camera.swift; sourceTree = "<group>"; };
BCD1B1251C66A262001F2BDC /* GPUImage.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GPUImage.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BCD1B12F1C66A262001F2BDC /* GPUImageTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GPUImageTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -767,6 +779,12 @@
BCFB06B71CBF287C009B2333 /* HistogramDisplay.swift */,
BCFB06E01CBF29F4009B2333 /* HistogramDisplay.vsh */,
BCFB06DF1CBF29F4009B2333 /* HistogramDisplay_GLES.fsh */,
BCBEC0EB1CCD499D00B70ED7 /* HistogramEqualization.swift */,
BCBEC0ED1CCD49B800B70ED7 /* HistogramEqualizationBlue_GLES.fsh */,
BCBEC0EE1CCD49B800B70ED7 /* HistogramEqualizationGreen_GLES.fsh */,
BCBEC0EF1CCD49B800B70ED7 /* HistogramEqualizationLuminance_GLES.fsh */,
BCBEC0F01CCD49B800B70ED7 /* HistogramEqualizationRed_GLES.fsh */,
BCBEC0F11CCD49B800B70ED7 /* HistogramEqualizationRGB_GLES.fsh */,
BCFB06931CBF284A009B2333 /* GaussianBlur.swift */,
BCFB069F1CBF284A009B2333 /* SingleComponentGaussianBlur.swift */,
BCFB06B91CBF2895009B2333 /* BoxBlur.swift */,
Expand Down Expand Up @@ -974,6 +992,11 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BCBEC0F21CCD49B800B70ED7 /* HistogramEqualizationBlue_GLES.fsh in Resources */,
BCBEC0F31CCD49B800B70ED7 /* HistogramEqualizationGreen_GLES.fsh in Resources */,
BCBEC0F61CCD49B800B70ED7 /* HistogramEqualizationRGB_GLES.fsh in Resources */,
BCBEC0F41CCD49B800B70ED7 /* HistogramEqualizationLuminance_GLES.fsh in Resources */,
BCBEC0F51CCD49B800B70ED7 /* HistogramEqualizationRed_GLES.fsh in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1092,6 +1115,7 @@
BCD03F0F1CA23D6300271751 /* Camera.swift in Sources */,
BCFB05FD1CBF1FF8009B2333 /* DarkenBlend.swift in Sources */,
BC6A13AF1CBB52F000387779 /* SaturationAdjustment.swift in Sources */,
BCBEC0EC1CCD499D00B70ED7 /* HistogramEqualization.swift in Sources */,
BCFB07471CBF2BC1009B2333 /* SwirlDistortion.swift in Sources */,
BCFB060A1CBF1FF8009B2333 /* SoftLightBlend.swift in Sources */,
BCFB06091CBF1FF8009B2333 /* ScreenBlend.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion framework/Source/Framebuffer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public class Framebuffer {
if (!textureOverride) {
var mutableTexture = texture
glDeleteTextures(1, &mutableTexture)
print("Delete texture")
print("Delete texture at size: \(size)")
}

if let framebuffer = framebuffer {
Expand Down
11 changes: 11 additions & 0 deletions framework/Source/Operations/Histogram.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,17 @@ let GL_FALSE = GLboolean(0)
#endif
#endif

/* Unlike other filters, this one uses a grid of GL_POINTs to sample the incoming image in a grid. A custom vertex shader reads the color in the texture at its position
and outputs a bin position in the final histogram as the vertex position. That point is then written into the image of the histogram using translucent pixels.
The degree of translucency is controlled by the scalingFactor, which lets you adjust the dynamic range of the histogram. The histogram can only be generated for one
color channel or luminance value at a time.

This is based on this implementation: http://www.shaderwrangler.com/publications/histogram/histogram_cameraready.pdf

Or at least that's how it would work if iOS could read from textures in a vertex shader, which it can't. Therefore, I read the texture data down from the
incoming frame and process the texture colors as vertices.
*/

public enum HistogramType {
case Red
case Blue
Expand Down
57 changes: 57 additions & 0 deletions framework/Source/Operations/HistogramEqualization.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
public class HistogramEqualization: OperationGroup {
public var downsamplingFactor: UInt = 16 { didSet { histogram.downsamplingFactor = downsamplingFactor } }

let histogram:Histogram
let rawDataInput = RawDataInput()
let rawDataOutput = RawDataOutput()
let equalizationFilter:BasicOperation

public init(type:HistogramType) {

self.histogram = Histogram(type:type)
switch type {
case .Red: self.equalizationFilter = BasicOperation(fragmentShader:HistogramEqualizationRedFragmentShader, numberOfInputs:2)
case .Blue: self.equalizationFilter = BasicOperation(fragmentShader:HistogramEqualizationBlueFragmentShader, numberOfInputs:2)
case .Green: self.equalizationFilter = BasicOperation(fragmentShader:HistogramEqualizationGreenFragmentShader, numberOfInputs:2)
case .Luminance: self.equalizationFilter = BasicOperation(fragmentShader:HistogramEqualizationLuminanceFragmentShader, numberOfInputs:2)
case .RGB: self.equalizationFilter = BasicOperation(fragmentShader:HistogramEqualizationRGBFragmentShader, numberOfInputs:2)
}

super.init()

({downsamplingFactor = 16})()

self.configureGroup{input, output in
self.rawDataOutput.dataAvailableCallback = {data in
var redHistogramBin = [Int](count:256, repeatedValue:0)
var greenHistogramBin = [Int](count:256, repeatedValue:0)
var blueHistogramBin = [Int](count:256, repeatedValue:0)

let rowWidth = 256 * 4
redHistogramBin[0] = Int(data[rowWidth])
greenHistogramBin[1] = Int(data[rowWidth + 1])
blueHistogramBin[2] = Int(data[rowWidth + 2])

for dataIndex in 1..<256 {
redHistogramBin[dataIndex] = redHistogramBin[dataIndex - 1] + Int(data[rowWidth + (dataIndex * 4)])
greenHistogramBin[dataIndex] = greenHistogramBin[dataIndex - 1] + Int(data[rowWidth + (dataIndex * 4) + 1])
blueHistogramBin[dataIndex] = blueHistogramBin[dataIndex - 1] + Int(data[rowWidth + (dataIndex * 4) + 2])
}

var equalizationLookupTable = [UInt8](count:256 * 4, repeatedValue:0)
for binIndex in 0..<256 {
equalizationLookupTable[binIndex * 4] = UInt8((((redHistogramBin[binIndex] - redHistogramBin[0]) * 255) / redHistogramBin[255]))
equalizationLookupTable[(binIndex * 4) + 1] = UInt8((((greenHistogramBin[binIndex] - greenHistogramBin[0]) * 255) / greenHistogramBin[255]))
equalizationLookupTable[(binIndex * 4) + 2] = UInt8((((blueHistogramBin[binIndex] - blueHistogramBin[0]) * 255) / blueHistogramBin[255]))
equalizationLookupTable[(binIndex * 4) + 3] = 255
}

self.rawDataInput.uploadBytes(equalizationLookupTable, size:Size(width:256, height:1), pixelFormat:.RGBA)
}

input --> self.histogram --> self.rawDataOutput
input --> self.equalizationFilter --> output
self.rawDataInput --> self.equalizationFilter
}
}
}
Loading

0 comments on commit 805bf5d

Please sign in to comment.