ゆるゆるプログラミング

・N値化

N値化とは、カラーの画像をN個のグレー色(白/灰色/黒)で表現した画像に変換することです。

変換には、2個以上の色を0~255の値で指定します。変換前の画素の色を加重平均法を使って計算したグレー値に最も近い指定値を求めます。この求められた値が変換後のグレー値です。

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

NColor.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 NColor {
007:    	// 最も近い値を格納した配列の添え字を求める
008:    	public static int getNearestValue( int[] list, int v )
009:    	{
010:    		// 変数の宣言
011:    		int i;		// ループ用
012:    		int num;	// 配列の添え字
013:    		int minv;	// 配列値-指定値vの絶対値
014:    
015:    		// 配列の個数が1未満の処理
016:    		if ( 1 >list.length ) return -1;
017:    
018:    		// 指定値と全ての配列値の差を比較
019:    		num = 0;
020:    		minv = Math.abs( list[ 0 ] - v );
021:    		for ( i = 1; i < list.length; ++ i ) {
022:    			if ( Math.abs( list[ i ] - v ) < minv ) {
023:    				num = i;
024:    				minv = Math.abs( list[ i ] - v );
025:    			}
026:    		}
027:    
028:    		return num;
029:    	}
030:    
031:    
032:    	// メイン
033:    	public static void main( String[] args ) {
034:    		// 結果格納フラグ
035:    		boolean result;
036:    		// ファイル名
037:    		String inname, outname;
038:    		// 画像格納クラス
039:    		BufferedImage img = null;
040:    		// 色リスト
041:    		int[] colorlist;
042:    		// 256色→N色変換テーブル
043:    		int[] converttbl;
044:    
045:    		// 入力した引数が4つ以上かを調べる
046:    		if ( 4 > args.length ) {
047:    			// 入力した引数が2つ未満の場合、使用方法を表示する
048:    			System.out.println( "NColor [入力JPEG名] [出力PNG名] [値1] [値2] .." );
049:    			return;
050:    		}
051:    
052:    		// 入力JPEG名をinnameに代入(拡張子".jpg"省略なし)
053:    		inname  = args[ 0 ];
054:    		// 出力PNG名をoutnameに代入(拡張子".png"省略なし)
055:    		outname = args[ 1 ];
056:    
057:    		// 色リストの作成
058:    		colorlist = new int[ args.length - 2 ];
059:    		for ( int i = 2; i < args.length; ++ i ) {
060:    			// 値をcolorlistに代入
061:    			try {
062:    				// 文字列をintに変換して値を代入
063:    				colorlist[ i - 2 ] = Integer.valueOf( args[ i ] );
064:    			}
065:    			catch( NumberFormatException ne )
066:    			{
067:    				System.out.println( "引数が不正です" );
068:    				return;
069:    			}
070:    		}
071:    
072:    		// JPEG画像の読み込み
073:    		try {
074:    			// inname(入力JPEG)を読み込んでimgにセット
075:    			img = ImageIO.read( new File( inname ) );
076:    		} catch (Exception e) {
077:    			// inname(入力JPEG)の読み込みに失敗したときの処理
078:    			 e.printStackTrace();
079:    			return;
080:    		}
081:    
082:    		// 画像の色の持ち方をチェック
083:    		if ( BufferedImage.TYPE_3BYTE_BGR != img.getType() )
084:    		{
085:    			System.out.println( "対応していないカラーモデルです!("
086:    									 + inname +")" );
087:    			return;
088:    		}
089:    
090:    		// N値に変換
091:    		int x, y;
092:    		int width, height;
093:    		int color, r, g, b;
094:    		int p;
095:    		int newcolor;
096:    
097:    		// 画像サイズの取得
098:    		width = img.getWidth();
099:    		height= img.getHeight();
100:    
101:    		// 256色→N色変換テーブル作成
102:    		converttbl = new int[ 256 ];
103:    		for ( int i = 0; i < 256; ++ i ) {
104:    			// iに一番近い値を取得
105:    			int num = getNearestValue( colorlist, i );
106:    			if ( 0 <= num ) {
107:    				// 配列の添え字numから色を取得
108:    				p = colorlist[ num ];
109:    				// pを0~255の範囲に変換
110:    				if ( 0 > p )
111:    					p = 0;
112:    				else {
113:    					if ( 255 < p ) p = 255;
114:    				}
115:    				converttbl[ i ] = p;
116:    			}
117:    		}
118:    
119:    		// N色変換
120:    		for ( y = 0; y < height; ++ y ) {
121:    			for ( x = 0; x < width; ++ x ) {
122:    				// (x,y)の色を取得
123:    				color = img.getRGB( x, y );
124:    
125:    				// 色をr,g,bに分解
126:    				r = ( color >> 16 ) & 0xff;
127:    				g = ( color >> 8 ) & 0xff;
128:    				b = color & 0xff;
129:    
130:    				// rgbに重みを付けてpを計算
131:    				p = (int)( 0.29891 * (double)r
132:    					 + 0.58661 * (double)g
133:    						 + 0.11448 * (double)b );
134:    
135:    				// 256色→N色変換テーブルから変換色を取得
136:    				p = converttbl[ p ];
137:    
138:    				// r,g,bにpを代入
139:    				r = p;
140:    				g = p;
141:    				b = p;
142:    
143:    				// r,g,bの色を合成
144:    				newcolor = ( r << 16 ) + ( g << 8 ) + b;
145:    
146:    				// 合成した色を(x,y)に設定
147:    				img.setRGB( x, y, newcolor );
148:    			}
149:    		}
150:    
151:    		// PNG画像の保存
152:    		try {
153:    			// imgをoutname(出力PNG)に保存
154:    			result = ImageIO.write( img, "png", new File( outname ) );
155:    		} catch ( Exception e ) {
156:    			// outname(出力JPEG)の保存に失敗したときの処理
157:    			e.printStackTrace();
158:    			return;
159:    		}
160:    
161:    		// 正常に終了
162:    		System.out.println( "正常に終了しました" );
163:    	}
164:    }

NColorを2つの値で実行

java NColor sampleimage001_400x320.jpg ncolor2.png 0 255

1つ目の引数で渡したJPEGファイルをN値変換し、2つ目の引数で指定したPNGファイル名で保存します。この場合、3つ目の引数から2つの値を指定しているので2値化変換になります。

実行結果

・元の画像(sampleimage001_400x320.jpg)

元画像

・変換後の画像(ncolor2.png)

2値化画像

NColorを3つの値で実行

java NColor sampleimage001_400x320.jpg ncolor3.png 0 200 255

1つ目の引数で渡したJPEGファイルをN値変換し、2つ目の引数で指定したPNGファイル名で保存します。この場合、3つ目の引数から3つの値を指定しているので3値化変換になります。

実行結果

・元の画像(sampleimage001_400x320.jpg)

元画像

・変換後の画像(ncolor3.png)

3値化画像

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

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 NColor {

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

007:    	// 最も近い値を格納した配列の添え字を求める
008:    	public static int getNearestValue( int[] list, int v )

指定値(v)に1番近い値を持つ配列(list[])の添え字を求めるメソッドです。

010:    		// 変数の宣言
011:    		int i;		// ループ用
012:    		int num;	// 配列の添え字
013:    		int minv;	// 配列値-指定値vの絶対値

メソッドで使う変数を宣言しています。

015:    		// 配列の個数が1未満の処理
016:    		if ( 1 >list.length ) return -1;

配列(list[])に格納されている値が1個未満の場合、配列添え字を求めることができないので、結果として-1を返しています。

018:    		// 指定値と全ての配列値の差を比較
019:    		num = 0;
020:    		minv = Math.abs( list[ 0 ] - v );

求める値の候補(初期値)として配列の先頭(list[0])の情報を変数numと変数minvに代入しています。numには配列の先頭の添え字である0、minvには指定値vと配列の先頭の値(list[0])との差の絶対値を代入しています。

021:    		for ( i = 1; i < list.length; ++ i ) {
022:    			if ( Math.abs( list[ i ] - v ) < minv ) {
023:    				num = i;
024:    				minv = Math.abs( list[ i ] - v );
025:    			}
026:    		}

指定値と配列の2番目の値から最後の値の差を計算していき、その差がminvより小さくなるときにnumとminvの値を変更していきます。

028:    		return num;

for文を抜けたときのnumの値が求めたい配列添え字になっています。その値をreturn文で返します。

032:    	// メイン
033:    	public static void main( String[] args ) {

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

034:    		// 結果格納フラグ
035:    		boolean result;
036:    		// ファイル名
037:    		String inname, outname;
038:    		// 画像格納クラス
039:    		BufferedImage img = null;
040:    		// 色リスト
041:    		int[] colorlist;
042:    		// 256色→N色変換テーブル
043:    		int[] converttbl;

このプログラムで使う変数を宣言しています。

045:    		// 入力した引数が4つ以上かを調べる
046:    		if ( 4 > args.length ) {
047:    			// 入力した引数が2つ未満の場合、使用方法を表示する
048:    			System.out.println( "NColor [入力JPEG名] [出力PNG名] [値1] [値2] .." );
049:    			return;
050:    		}

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

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

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

057:    		// 色リストの作成
058:    		colorlist = new int[ args.length - 2 ];
059:    		for ( int i = 2; i < args.length; ++ i ) {
060:    			// 値をcolorlistに代入
061:    			try {
062:    				// 文字列をintに変換して値を代入
063:    				colorlist[ i - 2 ] = Integer.valueOf( args[ i ] );
064:    			}
065:    			catch( NumberFormatException ne )
066:    			{
067:    				System.out.println( "引数が不正です" );
068:    				return;
069:    			}
070:    		}

与えられた引数の3番目以降がN値化するための値です。与えられた値の個数は引数から2を引いたもので、この個数で配列colorlistを作成し、値を代入しています。

072:    		// JPEG画像の読み込み
073:    		try {
074:    			// inname(入力JPEG)を読み込んでimgにセット
075:    			img = ImageIO.read( new File( inname ) );
076:    		} catch (Exception e) {
077:    			// inname(入力JPEG)の読み込みに失敗したときの処理
078:    			 e.printStackTrace();
079:    			return;
080:    		}

入力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メソッドを抜けるようにしています。

082:    		// 画像の色の持ち方をチェック
083:    		if ( BufferedImage.TYPE_3BYTE_BGR != img.getType() )
084:    		{
085:    			System.out.println( "対応していないカラーモデルです!("
086:    									 + inname +")" );
087:    			return;
088:    		}

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

BufferedImage.getTypeメソッド

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

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

090:    		// N値に変換
091:    		int x, y;
092:    		int width, height;
093:    		int color, r, g, b;
094:    		int p;
095:    		int newcolor;

N値変換で使う変数を宣言しています。

097:    		// 画像サイズの取得
098:    		width = img.getWidth();
099:    		height= img.getHeight();

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

101:    		// 256色→N色変換テーブル作成
102:    		converttbl = new int[ 256 ];
103:    		for ( int i = 0; i < 256; ++ i ) {
104:    			// iに一番近い値を取得
105:    			int num = getNearestValue( colorlist, i );
106:    			if ( 0 <= num ) {
107:    				// 配列の添え字numから色を取得
108:    				p = colorlist[ num ];
109:    				// pを0~255の範囲に変換
110:    				if ( 0 > p )
111:    					p = 0;
112:    				else {
113:    					if ( 255 < p ) p = 255;
114:    				}
115:    				converttbl[ i ] = p;
116:    			}
117:    		}

0~255の値に最も近い値をgetNearestValueメソッドで求め、その値を配列converttblに格納しています。値が0未満になった場合には0を、255を超えた場合は255を格納しています。

119:    		// N色変換
120:    		for ( y = 0; y < height; ++ y ) {
121:    			for ( x = 0; x < width; ++ x ) {
122:    				// (x,y)の色を取得
123:    				color = img.getRGB( x, y );
124:    
125:    				// 色をr,g,bに分解
126:    				r = ( color >> 16 ) & 0xff;
127:    				g = ( color >> 8 ) & 0xff;
128:    				b = color & 0xff;
129:    
130:    				// rgbに重みを付けてpを計算
131:    				p = (int)( 0.29891 * (double)r
132:    					 + 0.58661 * (double)g
133:    						 + 0.11448 * (double)b );
134:    
135:    				// 256色→N色変換テーブルから変換色を取得
136:    				p = converttbl[ p ];
137:    
138:    				// r,g,bにpを代入
139:    				r = p;
140:    				g = p;
141:    				b = p;
142:    
143:    				// r,g,bの色を合成
144:    				newcolor = ( r << 16 ) + ( g << 8 ) + b;
145:    
146:    				// 合成した色を(x,y)に設定
147:    				img.setRGB( x, y, newcolor );
148:    			}
149:    		}

画像の中の全てのピクセルの座標を参照するループをつくり、その座標の色情報を取得しています。具体的には、変数yを0~height-1、変数xを0~width-1に変化させながら計算したグレー値をカラーテーブル(配列converttbl)で変換しています。

BufferedImage.getRGBメソッド

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

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

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

151:    		// PNG画像の保存
152:    		try {
153:    			// imgをoutname(出力PNG)に保存
154:    			result = ImageIO.write( img, "png", new File( outname ) );
155:    		} catch ( Exception e ) {
156:    			// outname(出力JPEG)の保存に失敗したときの処理
157:    			e.printStackTrace();
158:    			return;
159:    		}

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を返します。

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

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

■関連コンテンツ

テーブルによる色変換 その2 テーブルによる色の変換方法を解説(その2)
テーブルによる色変換 その3 テーブルによる色の変換方法を解説(その3)
光と色の3原色 光と色の3原色の考え方を解説

■新着情報

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

■広告

法人向けのETC専用カード

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

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

Topへ