Following on from my recent post on Shinpuru Image, my Swift syntactic sugar for Core Image and vImage filters, here's a new addition: SIHistogramCalculation().

SIHistogramCalculation() returns a tuple containing four arrays of 256 unsigned integers which can be used to create an image histogram representing the tonal distribution of an image. It's based on the vImage vImageHistogramCalculation_ARGB8888() function and is implemented as easily as:

        let histogram = UIImage(named: "glass.jpg")?.SIHistogramCalculation()

I've created a demonstration class, Histogram, which uses Daniel Cohen Gindi's ios-charts project to display a nice looking histogram chart. The demo uses three filters, SIWhitePointAdjust(), SIColorControls() and SIGammaAdjust(), which are chained together with the output image populating a UIImageView and acting as the source to a SIHistogramCalculator():

    let targetColor = UIColor(red: CGFloat(redSlider.value),
        green: CGFloat(greenSlider.value),
        blue: CGFloat(blueSlider.value),
        alpha: CGFloat(1.0))
    
    let image = UIImage(named: "glass.jpg")?
        .SIWhitePointAdjust(color: targetColor)
        .SIColorControls(saturation: saturationSlider.value, brightness: brightnessSlider.value, contrast: contrastSlider.value)
        .SIGammaAdjust(power: gammaSlider.value)
    
    let histogram = image?.SIHistogramCalculation()
    

    imageView.image = image

I then iterate over the histogram object building three arrays of ChartDataEntry:

    var redChartData = [ChartDataEntry](count: 256, repeatedValue: ChartDataEntry())
    var greenChartData = [ChartDataEntry](count: 256, repeatedValue: ChartDataEntry())

    var blueChartData = [ChartDataEntry](count: 256, repeatedValue: ChartDataEntry())

    for i: Int in 0 ... 255
    {
        redChartData[i] = ( ChartDataEntry(value: Float(histogram!.red[i]), xIndex: i) )
        greenChartData[i] = ( ChartDataEntry(value: Float(histogram!.green[i]), xIndex: i) )
        blueChartData[i] = ( ChartDataEntry(value: Float(histogram!.blue[i]), xIndex: i) )

    }

...which in turn are used to create LineChartDataSet instances which are passed to the LineChartView's data:

    let redChartDataSet = LineChartDataSet(yVals: redChartData, label: "red")
    let greenChartDataSet = LineChartDataSet(yVals: greenChartData, label: "green")

    let blueChartDataSet = LineChartDataSet(yVals: blueChartData, label: "blue")

    let lineChartData = LineChartData(xVals: foo, dataSets: [redChartDataSet, greenChartDataSet, blueChartDataSet])
    
    chart.data = lineChartData

...and that's all there is to the implementation! All the boiler plate code of wrestling with vImage's strange syntax and wrestling with UnsafeMutablePointers is hidden away inside Shinpuru Image, leaving you to create beautiful histograms!

One caveat - these filters are horribly slow in the iOS simulator, but this demo runs beautifully smoothly on my iPad Air 2. My plan is to add easy asynchronous support to Shinpuru Image to make running filters in the background really easy to add. 

Shinpuru Image is open source and the project lives in my GitHub repository here
1

View comments

  1. Hi,

    I need an image Tonality Histogram. How I can get it?

    Thank you,

    A

    ReplyDelete
About Me
About Me
Labels
Labels
Blog Archive
Loading