Skip to content

Commit

Permalink
Added histogram operation.
Browse files Browse the repository at this point in the history
  • Loading branch information
BradLarson committed Apr 24, 2016
1 parent 9d997b0 commit f26626b
Show file tree
Hide file tree
Showing 13 changed files with 206 additions and 1 deletion.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,11 @@ There are currently over 100 operations built into the framework, divided into t
- **ThresholdSobelEdgeDetection**: Performs Sobel edge detection, but applies a threshold instead of giving gradual strength values
- *edgeStrength*: Adjusts the dynamic range of the filter. Higher values lead to stronger edges, but can saturate the intensity colorspace. Default is 1.0.
- *threshold*: Any edge above this threshold will be black, and anything below white. Ranges from 0.0 to 1.0, with 0.8 as the default

- **Histogram**: This analyzes the incoming image and creates an output histogram with the frequency at which each color value occurs. The output of this filter is a 3-pixel-high, 256-pixel-wide image with the center (vertical) pixels containing pixels that correspond to the frequency at which various color values occurred. Each color value occupies one of the 256 width positions, from 0 on the left to 255 on the right. This histogram can be generated for individual color channels (.Red, .Green, .Blue), the luminance of the image (.Luminance), or for all three color channels at once (.RGB).
- *downsamplingFactor*: Rather than sampling every pixel, this dictates what fraction of the image is sampled. 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.

- **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.

- **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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,26 @@ let filterOperations: Array<FilterOperationInterface> = [
sliderUpdateCallback:nil,
filterOperationType:.SingleInput
),
// TODO: Histogram
FilterOperation(
filter:{Histogram(type:.RGB)},
listName:"Histogram",
titleName:"Histogram",
sliderConfiguration:.Enabled(minimumValue:4.0, maximumValue:32.0, initialValue:16.0),
sliderUpdateCallback: {(filter, sliderValue) in
filter.downsamplingFactor = UInt(round(sliderValue))
},
filterOperationType:.Custom(filterSetupFunction: {(camera, filter, outputView) in
let castFilter = filter as! Histogram
let histogramGraph = HistogramDisplay()
histogramGraph.overriddenOutputSize = Size(width:256.0, height:330.0)
let blendFilter = AlphaBlend()
blendFilter.mix = 0.75
camera --> blendFilter
camera --> castFilter --> histogramGraph --> blendFilter --> outputView

return blendFilter
})
),
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 @@ -143,6 +143,7 @@
BCB279EC1C8D11630013E213 /* Framebuffer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCB279EB1C8D11630013E213 /* Framebuffer.swift */; };
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 */; };
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 @@ -403,6 +404,12 @@
BCB279EB1C8D11630013E213 /* Framebuffer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Framebuffer.swift; path = Source/Framebuffer.swift; sourceTree = "<group>"; };
BCB825B51CC9C1F100339790 /* MovieOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MovieOutput.swift; path = Source/Mac/MovieOutput.swift; sourceTree = "<group>"; };
BCB825BA1CC9C96B00339790 /* Timestamp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Timestamp.swift; path = Source/Timestamp.swift; sourceTree = "<group>"; };
BCBEC0C51CCD2E6200B70ED7 /* Histogram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Histogram.swift; path = Source/Operations/Histogram.swift; sourceTree = "<group>"; };
BCBEC0C71CCD2E8900B70ED7 /* HistogramAccumulation_GL.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramAccumulation_GL.fsh; path = Source/Operations/Shaders/HistogramAccumulation_GL.fsh; sourceTree = "<group>"; };
BCBEC0C81CCD2E8900B70ED7 /* HistogramBlueSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramBlueSampling.vsh; path = Source/Operations/Shaders/HistogramBlueSampling.vsh; sourceTree = "<group>"; };
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>"; };
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 @@ -636,6 +643,12 @@
BC4EE1571CB345AE00AD8A65 /* PrewittEdgeDetection_GL.fsh */,
BC4EE15D1CB3481F00AD8A65 /* ThresholdSobelEdgeDetection.swift */,
BC4EE15F1CB3487400AD8A65 /* ThresholdEdgeDetection_GL.fsh */,
BCBEC0C51CCD2E6200B70ED7 /* Histogram.swift */,
BCBEC0CB1CCD2E8900B70ED7 /* HistogramRedSampling.vsh */,
BCBEC0C91CCD2E8900B70ED7 /* HistogramGreenSampling.vsh */,
BCBEC0C81CCD2E8900B70ED7 /* HistogramBlueSampling.vsh */,
BCBEC0CA1CCD2E8900B70ED7 /* HistogramLuminanceSampling.vsh */,
BCBEC0C71CCD2E8900B70ED7 /* HistogramAccumulation_GL.fsh */,
BC4EE1651CB34B3700AD8A65 /* HistogramDisplay.swift */,
BC4EE1681CB34B8900AD8A65 /* HistogramDisplay.vsh */,
BC4EE1671CB34B8900AD8A65 /* HistogramDisplay_GL.fsh */,
Expand Down Expand Up @@ -1031,6 +1044,7 @@
BC7FD09A1CA62ED100037949 /* ExposureAdjustment.swift in Sources */,
BC5B866F1CC07145006CDE75 /* PictureOutput.swift in Sources */,
BC7FD1121CB0789600037949 /* HueBlend.swift in Sources */,
BCBEC0C61CCD2E6200B70ED7 /* Histogram.swift in Sources */,
BCFF46E21CBADB3E00A0C521 /* SingleComponentGaussianBlur.swift in Sources */,
BC7FD0F71CB0620E00037949 /* ChromaKeyBlend.swift in Sources */,
BC7FD0861CA62E1100037949 /* BrightnessAdjustment.swift in Sources */,
Expand Down
14 changes: 14 additions & 0 deletions framework/GPUImage-iOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
BCA4E2551CC3F7A0007B51BA /* ColorLocalBinaryPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCA4E2541CC3F7A0007B51BA /* ColorLocalBinaryPattern.swift */; };
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 */; };
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 @@ -203,6 +204,12 @@
BCA4E2561CC3F7B7007B51BA /* ColorLocalBinaryPattern_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = ColorLocalBinaryPattern_GLES.fsh; path = Source/Operations/Shaders/ColorLocalBinaryPattern_GLES.fsh; sourceTree = "<group>"; };
BCB825BD1CC9CA6B00339790 /* Timestamp.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Timestamp.swift; path = Source/Timestamp.swift; sourceTree = "<group>"; };
BCB825BF1CC9CA8100339790 /* MovieOutput.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MovieOutput.swift; path = Source/iOS/MovieOutput.swift; sourceTree = "<group>"; };
BCBEC0D11CCD404900B70ED7 /* Histogram.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Histogram.swift; path = Source/Operations/Histogram.swift; sourceTree = "<group>"; };
BCBEC0D31CCD407500B70ED7 /* HistogramAccumulation_GLES.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramAccumulation_GLES.fsh; path = Source/Operations/Shaders/HistogramAccumulation_GLES.fsh; sourceTree = "<group>"; };
BCBEC0D41CCD407500B70ED7 /* HistogramBlueSampling.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = HistogramBlueSampling.vsh; path = Source/Operations/Shaders/HistogramBlueSampling.vsh; sourceTree = "<group>"; };
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>"; };
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 @@ -751,6 +758,12 @@
BCFB06E61CBF29F4009B2333 /* PrewittEdgeDetection_GLES.fsh */,
BCFB06A11CBF284A009B2333 /* ThresholdSobelEdgeDetection.swift */,
BCFB06E91CBF29F4009B2333 /* ThresholdEdgeDetection_GLES.fsh */,
BCBEC0D11CCD404900B70ED7 /* Histogram.swift */,
BCBEC0D71CCD407500B70ED7 /* HistogramRedSampling.vsh */,
BCBEC0D51CCD407500B70ED7 /* HistogramGreenSampling.vsh */,
BCBEC0D41CCD407500B70ED7 /* HistogramBlueSampling.vsh */,
BCBEC0D61CCD407500B70ED7 /* HistogramLuminanceSampling.vsh */,
BCBEC0D31CCD407500B70ED7 /* HistogramAccumulation_GLES.fsh */,
BCFB06B71CBF287C009B2333 /* HistogramDisplay.swift */,
BCFB06E01CBF29F4009B2333 /* HistogramDisplay.vsh */,
BCFB06DF1CBF29F4009B2333 /* HistogramDisplay_GLES.fsh */,
Expand Down Expand Up @@ -1012,6 +1025,7 @@
BCA4E2231CC1FB62007B51BA /* PictureOutput.swift in Sources */,
BCFB06AE1CBF284A009B2333 /* NobleCornerDetector.swift in Sources */,
BCFB06CC1CBF2937009B2333 /* CannyEdgeDetection.swift in Sources */,
BCBEC0D21CCD404900B70ED7 /* Histogram.swift in Sources */,
BCFB06601CBF21E1009B2333 /* LuminanceRangeReduction.swift in Sources */,
BCFB05FA1CBF1FF8009B2333 /* ColorBlend.swift in Sources */,
BC6A13AD1CBB503900387779 /* CameraConversion.swift in Sources */,
Expand Down
83 changes: 83 additions & 0 deletions framework/Source/Operations/Histogram.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#if os(Linux)
#if GLES
import COpenGLES.gles2
let GL_DEPTH24_STENCIL8 = GL_DEPTH24_STENCIL8_OES
let GL_TRUE = GLboolean(1)
let GL_FALSE = GLboolean(0)
#else
import COpenGL
#endif
#else
#if GLES
import OpenGLES
#else
import OpenGL.GL3
#endif
#endif

public enum HistogramType {
case Red
case Blue
case Green
case Luminance
case RGB
}

public class Histogram: BasicOperation {
public var downsamplingFactor:UInt = 16

var shader2:ShaderProgram? = nil
var shader3:ShaderProgram? = nil

public init(type:HistogramType) {
switch type {
case .Red: super.init(vertexShader:HistogramRedSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader, numberOfInputs:1)
case .Blue: super.init(vertexShader:HistogramBlueSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader, numberOfInputs:1)
case .Green: super.init(vertexShader:HistogramGreenSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader, numberOfInputs:1)
case .Luminance: super.init(vertexShader:HistogramLuminanceSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader, numberOfInputs:1)
case .RGB:
super.init(vertexShader:HistogramRedSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader, numberOfInputs:1)
shader2 = crashOnShaderCompileFailure("Histogram"){try sharedImageProcessingContext.programForVertexShader(HistogramGreenSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader)}
shader3 = crashOnShaderCompileFailure("Histogram"){try sharedImageProcessingContext.programForVertexShader(HistogramBlueSamplingVertexShader, fragmentShader:HistogramAccumulationFragmentShader)}
}
}

override func renderFrame() {
let inputSize = sizeOfInitialStageBasedOnFramebuffer(inputFramebuffers[0]!)
let inputByteSize = Int(inputSize.width * inputSize.height * 4)
let data = UnsafeMutablePointer<UInt8>.alloc(inputByteSize)
glReadPixels(0, 0, inputSize.width, inputSize.height, GLenum(GL_RGBA), GLenum(GL_UNSIGNED_BYTE), data)

renderFramebuffer = sharedImageProcessingContext.framebufferCache.requestFramebufferWithProperties(orientation:.Portrait, size:GLSize(width:256, height:3), stencil:mask != nil)
releaseIncomingFramebuffers()
renderFramebuffer.activateFramebufferForRendering()

clearFramebufferWithColor(Color.Black)

glBlendEquation(GLenum(GL_FUNC_ADD))
glBlendFunc(GLenum(GL_ONE), GLenum(GL_ONE))
glEnable(GLenum(GL_BLEND))

shader.use()
guard let positionAttribute = shader.attributeIndex("position") else { fatalError("A position attribute was missing from the shader program during rendering.") }
glVertexAttribPointer(positionAttribute, 4, GLenum(GL_UNSIGNED_BYTE), 0, (GLint(downsamplingFactor) - 1) * 4, data)
glDrawArrays(GLenum(GL_POINTS), 0, inputSize.width * inputSize.height / GLint(downsamplingFactor))

if let shader2 = shader2 {
shader2.use()
guard let positionAttribute2 = shader.attributeIndex("position") else { fatalError("A position attribute was missing from the shader program during rendering.") }
glVertexAttribPointer(positionAttribute2, 4, GLenum(GL_UNSIGNED_BYTE), 0, (GLint(downsamplingFactor) - 1) * 4, data)
glDrawArrays(GLenum(GL_POINTS), 0, inputSize.width * inputSize.height / GLint(downsamplingFactor))
}

if let shader3 = shader3 {
shader3.use()
guard let positionAttribute3 = shader.attributeIndex("position") else { fatalError("A position attribute was missing from the shader program during rendering.") }
glVertexAttribPointer(positionAttribute3, 4, GLenum(GL_UNSIGNED_BYTE), 0, (GLint(downsamplingFactor) - 1) * 4, data)
glDrawArrays(GLenum(GL_POINTS), 0, inputSize.width * inputSize.height / GLint(downsamplingFactor))
}

glDisable(GLenum(GL_BLEND))
data.dealloc(inputByteSize)
}
}
Loading

0 comments on commit f26626b

Please sign in to comment.