ゆるゆるプログラミング

・2値化

2値化とは、カラーの画像を2つの色だけで表現した画像に変換することです。一般的には白と黒だけで表現しますが、2色であればどんな組み合わせでも良いです。

元の画像元の画像      2値画像2値画像

光の三原色の赤(R)、緑(G)、青(B)の輝度の平均値がある値(閾値)以上で白、未満で黒になるようにすることで2値化画像を作成することができます。

次に、フルカラー(24bit)画像を2値画像に変換するソースコードを解説します。以下が、そのソースコード例です。

Binarization.java
001:    import java.awt.image.BufferedImage;
002:    import java.io.File;
003:    import javax.imageio.ImageIO;
004:    import java.io.IOException;
005:    
006:    public class Binarization {
007:    	public static void main( String[] args ) {
008:    		// 閾値
009:    		int tv;
010:    		// ファイル名
011:    		String inname, outname;
012:    		// 画像格納クラス
013:    		BufferedImage img = null;
014:    
015:    		// 入力した引数が3つ以上かを調べる
016:    		if ( 3 > args.length ) {
017:    			// 入力した引数が3つ未満の場合、使用方法を表示する
018:    			System.out.println(
019:    				 "Binarization [入力JPEG名]  [出力PNG名] [閾(しきい)値]" );
020:    			return;
021:    		}
022:    
023:    		// 入力JPEG名をinnameに代入(拡張子".jpg"省略なし)
024:    		inname  = args[ 0 ];
025:    		// 出力PNG名をoutnameに代入(拡張子".png"省略なし)
026:    		outname = args[ 1 ];
027:    
028:    		// 閾値をtvに代入
029:    		try {
030:    			// 閾値を代入
031:    			tv = Integer.valueOf( args[ 2 ] );
032:    		}
033:    		catch( NumberFormatException ne )
034:    		{
035:    			System.out.println( "引数が不正です" );
036:    			return;
037:    		}
038:    
039:    		// JPEG画像の読み込み
040:    		try {
041:    			// inname(入力JPEG)を読み込んでimgにセット
042:    			img = ImageIO.read( new File( inname ) );
043:    		} catch (Exception e) {
044:    			// inname(入力JPEG)の読み込みに失敗したときの処理
045:    			 e.printStackTrace();
046:    			return;
047:    		}
048:    
049:    		// 画像の色の持ち方をチェック
050:    		if ( BufferedImage.TYPE_3BYTE_BGR != img.getType() )
051:    		{
052:    			System.out.println( "対応していないカラーモデルです!("
053:    									 + inname +")" );
054:    			return;
055:    		}
056:    
057:    		// 2値化
058:    		int x, y;
059:    		int width, height;
060:    		int color, r, g, b;
061:    		int p;
062:    		int newcolor;
063:    
064:    		// 画像サイズの取得
065:    		width = img.getWidth();
066:    		height= img.getHeight();
067:    
068:    		for ( y = 0; y < height; ++ y ) {
069:    			for ( x = 0; x < width; ++ x ) {
070:    				// (x,y)の色を取得
071:    				color = img.getRGB( x, y );
072:    
073:    				// 色をr,g,bに分解
074:    				r = ( color >> 16 ) & 0xff;
075:    				g = ( color >> 8 ) & 0xff;
076:    				b = color & 0xff;
077:    
078:    				// rgbの平均値を計算
079:    				p = ( r + g + b ) / 3;
080:    				
081:    				// 2値化
082:    				if ( tv <= p ) {
083:    					// 閾値tv以上なら白
084:    					r = 255;
085:    					g = 255;
086:    					b = 255;
087:    				}
088:    				else {
089:    					// 閾値tv未満なら黒
090:    					r = 0;
091:    					g = 0;
092:    					b = 0;
093:    				}
094:    
095:    				// r,g,bの色を合成
096:    				newcolor = ( r << 16 ) + ( g << 8 ) + b;
097:    
098:    				// 合成した色を(x,y)に設定
099:    				img.setRGB( x, y, newcolor );
100:    			}
101:    		}
102:    
103:    		try {
104:    			boolean result;
105:    			// imgをoutname(出力PNG)に保存
106:    			result = ImageIO.write( img, "png", new File( outname ) );
107:    		} catch ( Exception e ) {
108:    			// outname(出力PNG)の保存に失敗したときの処理
109:    			e.printStackTrace();
110:    			return;
111:    		}
112:    
113:    		// 正常に終了
114:    		System.out.println( "正常に終了しました" );
115:    	}
116:    }

Binarizationを実行

C:\talavax\javasample>java Binarization sampleimage001_400x320.jpg binarization.png 127

1つ目の引数で渡したJPEGファイルグレースケールの画像に変換し、2つ目の引数で指定したPNGファイル名で保存します。

実行結果

・元の画像(sampleimage001_400x320.jpg)

元画像

・変換後の2値画像(binarization_127.png) 閾値:127

2値画像 閾値127

・変換後の2値画像(binarization_100.png) 閾値:100

2値画像 閾値100

・変換後の2値画像(binarization_80.png) 閾値:80

2値画像 閾値80

画像が、白と黒の2色で表現されました。

ここからは、このソースコードを上から順番に解説していきます。

001:    import java.awt.image.BufferedImage;
002:    import java.io.File;
003:    import javax.imageio.ImageIO;
004:    import java.io.IOException;

Javaのクラスライブラリの中から「java.awt.image.BufferedImage」と「java.io.File」と「javax.imageio.ImageIO」と「java.io.IOException」というパッケージにあるクラスを、このプログラム内で使うために記述します。 この記述により、ImageIOクラスBufferedImageクラスが利用できるようになります。

006:    public class Binarization {

クラス名を、Binarizationとしています。

007:    	public static void main( String[] args ) {

このmainメソッドからプログラムを実行します。

008:    		// 閾値
009:    		int tv;
010:    		// ファイル名
011:    		String inname, outname;
012:    		// 画像格納クラス
013:    		BufferedImage img = null;

このプログラムで使う変数を宣言しています。どのように使われているかは、後ろのソースコードで。

015:    		// 入力した引数が3つ以上かを調べる
016:    		if ( 3 > args.length ) {
017:    			// 入力した引数が3つ未満の場合、使用方法を表示する
018:    			System.out.println(
019:    				 "Binarization [入力JPEG名]  [出力PNG名] [閾(しきい)値]" );
020:    			return;
021:    		}

3つ以上の引数が与えられたかをチェックし、3つ未満の場合に、使い方のメッセージを表示し、returnによってmainメソッドを抜けています。

023:    		// 入力JPEG名をinnameに代入(拡張子".jpg"省略なし)
024:    		inname  = args[ 0 ];
025:    		// 出力PNG名をoutnameに代入(拡張子".png"省略なし)
026:    		outname = args[ 1 ];

与えられた引数をそれぞれ、入力JPEG名の変数(inname)、出力PNG名の変数(outname)代入しています。

028:    		// 閾値をtvに代入
029:    		try {
030:    			// 閾値を代入
031:    			tv = Integer.valueOf( args[ 2 ] );
032:    		}
033:    		catch( NumberFormatException ne )
034:    		{
035:    			System.out.println( "引数が不正です" );
036:    			return;
037:    		}

3番目に与えられた引数を閾値の変数(tv)に代入しています。

039:    		// JPEG画像の読み込み
040:    		try {
041:    			// inname(入力JPEG)を読み込んでimgにセット
042:    			img = ImageIO.read( new File( inname ) );
043:    		} catch (Exception e) {
044:    			// inname(入力JPEG)の読み込みに失敗したときの処理
045:    			 e.printStackTrace();
046:    			return;
047:    		}

入力JPEG名の変数(inname)を読み込んで、BufferedImageクラスのimgに格納しています。この処理には、ImageIOクラスreadメソッドを使います。

ImageIO.readメソッド

public static BufferedImage read( File input ) throws IOException
■Fileオブジェクトを復元した結果をBufferedImageに格納します。

  パラメータ input : Fileオブジェクト

  戻り値     inputを復元したBufferedImageaを返します。

try { ~ } catchは、失敗する可能性がある処理を波括弧で囲み、その処理に失敗したときにcatch { ~ }の波括弧で囲まれた処理を実行するということです。この場合は、JPEGファイル名が不正であったり、存在していなかったり、フォーマットが違っているなどが原因で処理が失敗する可能性があります。処理が失敗するとreturnによってmainメソッドを抜けるようにしています。

049:    		// 画像の色の持ち方をチェック
050:    		if ( BufferedImage.TYPE_3BYTE_BGR != img.getType() )
051:    		{
052:    			System.out.println( "対応していないカラーモデルです!("
053:    									 + inname +")" );
054:    			return;
055:    		}

BufferedImageクラスgetTypeメソッドで画像のイメージ型を取得しています。

BufferedImage.getTypeメソッド

public static int getType()
■イメージ型を返します。
  パラメータ なし

  戻り値     BufferedImage のイメージ型を返します。

057:    		// 2値化
058:    		int x, y;
059:    		int width, height;
060:    		int color, r, g, b;
061:    		int p;
062:    		int newcolor;

グレースケール変換で使う変数を宣言しています。

064:    		// 画像サイズの取得
065:    		width = img.getWidth();
066:    		height= img.getHeight();

widthに画像の幅(ピクセル)、heightに画像の高さ(ピクセル)を代入しています。

068:    		for ( y = 0; y < height; ++ y ) {
069:    			for ( x = 0; x < width; ++ x ) {
070:    				// (x,y)の色を取得
071:    				color = img.getRGB( x, y );

画像の中の全てのピクセルの座標を参照するループをつくり、その座標の色情報を取得しています。具体的には、変数yを0~height-1、変数xを0~width-1に変化させながら、BufferedImageクラスgetRGBメソッドで、(x,y)の色を変数colorに代入しています。

BufferedImage.getRGBメソッド

public static int getRGB( int x, int y )
■(x,y)で指定した画像座標の色情報を取得します。

  パラメータ x : 画像のx座標(単位ピクセル)
        y : 画像のy座標(単位ピクセル)

  戻り値     (x,y)の色情報

073:    				// 色をr,g,bに分解
074:    				r = ( color >> 16 ) & 0xff;
075:    				g = ( color >> 8 ) & 0xff;
076:    				b = color & 0xff;

変数colorに入っている色情報を赤(R)、緑(G)画、青(B)の成分に分解し、それぞれ変数a、b、cに代入しています。

078:    				// rgbの平均値を計算
079:    				p = ( r + g + b ) / 3;

赤(R)、緑(G)、青(B)の成分の合計値を3で割り平均値を求め、その値を変数pに代入しています。

081:    				// 2値化
082:    				if ( tv <= p ) {
083:    					// 閾値tv以上なら白
084:    					r = 255;
085:    					g = 255;
086:    					b = 255;
087:    				}
088:    				else {
089:    					// 閾値tv未満なら黒
090:    					r = 0;
091:    					g = 0;
092:    					b = 0;
093:    				}

赤(R)、緑(G)、青(B)の成分の平均値(変数p)が閾値以上であれば白(r=255,g=255,b=255)とし、未満であれば黒(r=0,g=0,b=0)にしています。

095:    				// r,g,bの色を合成
096:    				newcolor = ( r << 16 ) + ( g << 8 ) + b;

色の成分を合成して、新しい色(変数newcolor)を作成しています。

098:    				// 合成した色を(x,y)に設定
099:    				img.setRGB( x, y, newcolor );

新しい色(変数newcolor)を(x,y)に代入して、カラーの色情報を2値に変換しています。

103:    		try {
104:    			boolean result;
105:    			// imgをoutname(出力PNG)に保存
106:    			result = ImageIO.write( img, "png", new File( outname ) );
107:    		} catch ( Exception e ) {
108:    			// outname(出力PNG)の保存に失敗したときの処理
109:    			e.printStackTrace();
110:    			return;
111:    		}

BufferedImageクラスのimgのメモリ内のデータを、出力PNG名の変数(outname)に格納されているファイル名で保存します。この場合は、PNGファイル名が不正であったり、保存先のHDDなどが存在していなかったり、空き容量が少ないなどが原因で処理が失敗する可能性があります。

ImageIO.writeメソッド

public static boolean write( RenderedImage im, String formatName, File output ) throws IOException
■BufferedImageを画像ファイルに保存します。

  パラメータ RenderedImage : 保存するRenderedImage
                  formatName     : 画像ファイルのフォーマット(png/jpeg/bmp/gifなど)
                  output             : Fileオブジェクト

  戻り値     保存に成功するとtrue、失敗するとfalseを返します。

113:    		// 正常に終了
114:    		System.out.println( "正常に終了しました" );

全ての処理が正常終了すると、ここまで処理が実行されます。

■関連コンテンツ

グレースケール変換 カラー画像をグレースケールに変換する方法について解説
画像の色 画像の色について解説
画像ファイル形式 画像ファイル形式について解説
for文 繰り返し処理に使用するfor文について解説
光と色の3原色 光と色の3原色の考え方を解説
N値化 カラー画像をN値化する方法について解説

■新着情報

2017.11.17 N値化 カラー画像をN値化する方法について解説
2017.11.16 最も近い値の取得 指定値に最も近い配列の値を取得する方法を解説
2017.10.02 アルファ値(透過) アルファ値(透過)について

■広告

法人向けのETC専用カード

~約8,000名の受講生と80社以上の導入実績~ 企業向けプログラミング研修ならCodeCamp

日本最大級ショッピングサイト!お買い物なら楽天市場

Topへ