~aquaratixc/otsu-binarization-example

311762c06f6ff3c2c3a70ed7d516f432403e4496 — Oleg Bakharev 6 years ago f4ffe5e master
Created projects
5 files changed, 126 insertions(+), 0 deletions(-)

A Lenna.png
A Lenna_binarizated.png
A dub.sdl
A dub.selections.json
A source/app.d
A Lenna.png => Lenna.png +0 -0
A Lenna_binarizated.png => Lenna_binarizated.png +0 -0
A dub.sdl => dub.sdl +6 -0
@@ 0,0 1,6 @@
name "otsu_experiment"
description "Experiment with Otsu binarization"
authors "Oleg Bakharew"
copyright "Copyright © 2018, Oleg Bakharew"
license "ESL 1.0"
dependency "dlib" version="~>0.13.0"
\ No newline at end of file

A dub.selections.json => dub.selections.json +6 -0
@@ 0,0 1,6 @@
{
	"fileVersion": 1,
	"versions": {
		"dlib": "0.13.0"
	}
}

A source/app.d => source/app.d +114 -0
@@ 0,0 1,114 @@
import std.algorithm;
import std.stdio;

import dlib.image;

// создать гистограмму
auto createHistogram(SuperImage superImage)
{
	int[256] histogram;

	foreach (x; 0..superImage.width)
	{
		foreach (y; 0..superImage.height)
		{
			int instensity = cast(int) (superImage[x,y].luminance * 255);
			histogram[instensity] += 1; 
		}
	}

	return histogram;
}

// посчитать порог по Оцу
auto calculateOtsuThreshold(SuperImage superImage)
{
	// вычисляем гистограмму
	auto histogram = createHistogram(superImage);

	// аккумулятор суммы яркостей
	int sumOfLuminances;

	// вычисляем сумму яркостей
	foreach (x; 0..superImage.width)
	{
		foreach (y; 0..superImage.height)
		{
			sumOfLuminances += cast(int) (superImage[x,y].luminance * 255); 
		}
	}

	// общее количество пикселей
	auto allPixelCount = cast(double) (superImage.width * superImage.height);
	
	// оптимальный порог
	int bestThreshold = 0;
	// количество полезных пикселей
    int firstClassPixelCount = 0;
    // суммарная яркость полезных пикселей
    int firstClassLuminanceSum = 0;
    
    // оптимальный разброс яркостей
    double bestSigma = 0.0;

    for (int threshold = 0; threshold < 255; threshold++)
    {
    	firstClassPixelCount += histogram[threshold];
        firstClassLuminanceSum += threshold * histogram[threshold];
    
    	// доля полезных пикселей
        double firstClassProbability = firstClassPixelCount / allPixelCount;
        // доля фоновых пикселей
        double secondClassProbability = 1.0 - firstClassProbability;

        // средняя доля полезных пикселей
        double firstClassMean = (firstClassPixelCount == 0) ? 0 : firstClassLuminanceSum / firstClassPixelCount;
        // средняя доля фоновых пикселей
        double secondClassMean = (sumOfLuminances - firstClassLuminanceSum) / (allPixelCount - firstClassPixelCount);
        // величина разброса 
        double meanDelta = firstClassMean - secondClassMean;
        // общий разброс
        double sigma = firstClassProbability * secondClassProbability * meanDelta * meanDelta;

        // находим оптимальный разброс
        if (sigma > bestSigma) 
        {
            bestSigma = sigma;
            bestThreshold = threshold;
        }
    }

    return bestThreshold;
}

// бинаризация по Оцу
auto otsuBinarization(SuperImage superImage)
{
	SuperImage newImage = image(superImage.width, superImage.height);
	auto threshold = calculateOtsuThreshold(superImage);

	foreach (x; 0..superImage.width)
	{
		foreach (y; 0..superImage.height)
		{
			auto luminance = cast(int) (superImage[x,y].luminance * 255);

			if (luminance > threshold)
			{
				newImage[x, y] = Color4f(1.0f, 1.0f, 1.0f);
			}
			else
			{
				newImage[x, y] = Color4f(0.0f, 0.0f, 0.0f);
			} 
		}
	}

	return newImage; 
}

void main()
{
	auto img = load("Lenna.png");
	img.otsuBinarization.savePNG("Lenna_binarizated.png");
}