人工知能作っちゃう?

画像処理を中心に人工知能を作っていきます。

ニ値化 判別分析法

10/19 コードに訂正する点がありました。すみません。
コンピューターが輪郭を検出したりする際に必要となる技術です。お馴染みの(?)ニ値化トランプのような画像が作れます。f:id:manato2cc:20161017170939p:plain

さて、ニ値化にもたくさん方法があるのですがヒストグラムによってそこまで影響しない判別分析法(大津のニ値化)を使っていきます。ニ値化をするときに極めて重要な閾値というものがあるのですが、判別分析法はそれを自動で出すことができます! 計算は少し複雑ですが見ていきましょう。

概要

ある閾値tでヒストグラムを分けたとき左側を暗い方、右側を明るい方とします。
そして、暗い方の画素数をΩ1、平均をm1、分離をσ1とし、明るい方も同じく画素数をΩ2、平均をm2、分離をσ2とします。それを表したのが下の写真です。
f:id:manato2cc:20161017201356p:plain

判別分析法で閾値を決めるものは分離度です。分離度が最大になる閾値を見つければ良いのです。
分離度はクラス間分散クラス内分散との比で求める事ができます。

ではまず、クラス内分散σwを出していきたいと思います。
f:id:manato2cc:20161017183522g:plain
また、クラス間分散σbは
f:id:manato2cc:20161017183601g:plain
次に全分散を以下で表すことができることから、
f:id:manato2cc:20161017184049g:plain
分離度は
f:id:manato2cc:20161017184109g:plain
と表せます。
ここで全分散σtは閾値に関係なく一定なので省きます。さらにクラス間分散の式の分母も閾値に関係ないので最終的に
f:id:manato2cc:20161017184507g:plain
が最大となる閾値を探せば良いことになります。
ここまで短くなるとコードもだいぶ減ってきます。

package Imany.util.algorithm;

import java.awt.image.BufferedImage;

public class Binarize extends Algorithm
{
	private static final int BLACK = 0;
	private static final int WHITE = 16777215;
	public static BufferedImage algorithm(BufferedImage img, boolean gaussian)
	{
		if(gaussian) img = Gaussian.algorithm(img, true);
		BufferedImage re = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
		re.setData(img.getData());
		int[] histogram = Histogram.histogram(img);//ヒストグラムを取得
		int max = Integer.MIN_VALUE;
		int s = 0;//閾値
		int res_b;//暗い方の合計
		int count_b;//暗い方の画素数
		int res_w;//明るい方の合計
		int count_w;//明るい方の画素値
		double m_b;//平均
		double m_w;//平均
		double res1;//合計
		for (int t = 2; t < 256; t++) {
			res_b = 0;
			count_b = 0;
			res_w = 0;
			count_w = 0;
			m_b = 0;//平均
			m_w = 0;//平均
			res1 = 0.0;
			for (int i = 0; i < t; i++) {
				res_b += histogram[i] * i;//輝度値*画素数
				count_b += histogram[i];
			}
			for (int i = t; i < 256; i++) {
				res_w += histogram[i] * i;//輝度値*画素数
				count_w += histogram[i];
			}
			if(count_b == 0) continue;
			else res_b /= count_b;
			if(count_w == 0) continue;
			else res_w /= count_w;
			res1 = count_b * count_w * (res_b - res_w) * (res_b - res_w);
			if(res1 > max){
				s=t;
				max = (int)res1;
			}
		}

		for (int x = 0; x < img.getWidth(); x++) {
			for (int y = 0; y < img.getHeight(); y++) {
				if((img.getRGB(x,y) & 0xff) > s)
				{
					re.setRGB(x,y,WHITE);
				}else
				{
					re.setRGB(x,y,BLACK);
				}
			}
		}
		return re;
	}

ずいぶん短いコードで実装することができました!

結果

上のニ値化トランプをご覧ください。

広告を非表示にする