ゆるゆるプログラミング

・グラデーション画像(4隅の色)

グラデーションとは色が段階的に変化する様子のことです。ここでは、画像の4隅だけに色が与えられた時に画像全体のグラデーションがどのようになるかを考えてみます。

以下の図は、矩形の4隅に値(V1~V4)が与えられた時、任意座標(a,b)のVの値を求める問題のイメージです。V1~V4を色の輝度とし、作成する画像全ての座標でVを計算することでグラデーション画像が求められます。座標aとbの範囲は0.0から1.0で、画像全体のグラデーションを作成するときはa=0.0はx=0、a=1.0はx=画像の幅pixel-1、b=0.0はy=0、b=1.0はx=画像の高さpixel-1となるようにします。

任意座標の値計算任意座標の値計算

では、V1・V2・V3・V4・a・bからVを計算する式を導き出しましょう。

2つの色とその間の距離が与えられた場合にその間の任意距離での色を計算することが出来ます。

■関連コンテンツ

グラデーション(3) グラデーションを作成する方法を解説

この方法によって画像の上辺と下辺のそれぞれのaでのVが計算できます。下の式がその計算式でV12がaに対する上辺の値、V34がaに対する下辺の値です。

画像の上辺と下辺の値計算

任意座標の値計算(上辺と下辺)任意座標の値計算(上辺と下辺)

次に、V12とV34の間の値を計算する式は以下のようになります。b=0.0のときV=V12、b=1.0のときV=V34となるようにしています。

上辺と下辺の間の値計算

これらの式をまとめると以下のようになります。

値の補間式(代入後)

さらにまとめると以下のようになります。

値の補間式(代入後)

この式は、左上(a=0.0,b=0.0)のときV=V1、右上(a=1.0,b=0.0)のときV=V2、左下(a=0.0,b=1.0)のときV=V3、右下(a=1.0,b=1.0)のときV=V4になります。V1・V2・V3・V4にRGBのそれぞれの輝度、a・bに画像の左上からのx・yの距離を0.0~1.0にした値を代入すると任意座標の色を取得することができます。

ここからは画像サイズと4隅の色を与えてグラデーション画像を作るソースコードを解説します。

Gradation4.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 Gradation4 {
007:    	public static void main( String[] args ) {
008:    		// 変数宣言
009:    		int w, h;		// 画像サイズ
010:    		int r1, g1, b1;	// 左上の色
011:    		int r2, g2, b2;	// 右上の色
012:    		int r3, g3, b3;	// 左下の色
013:    		int r4, g4, b4;	// 右下の色
014:    		String outname;	// 出力ファイル名
015:    		BufferedImage img = null;	// 画像格納クラス
016:    
017:    		// 入力した引数が15以上かを調べる
018:    		if ( 15 > args.length ) {
019:    			// 入力した引数が15未満の場合、使用方法を表示する
020:    			System.out.println( 
021:    				"Gradation4 [PNG名][幅][高][R1][G1][B1]…[R4][G4][B4]" );
022:    			return;
023:    		}
024:    
025:    		try {
026:    			// 引数を変換し、画像サイズに代入
027:    			w  =  Integer.valueOf( args[  1 ] );
028:    			h  =  Integer.valueOf( args[  2 ] );
029:    			// 引数を変換し、左上の色に代入
030:    			r1 =  Integer.valueOf( args[  3 ] );
031:    			g1 =  Integer.valueOf( args[  4 ] );
032:    			b1 =  Integer.valueOf( args[  5 ] );
033:    			// 引数を変換し、右上の色に代入
034:    			r2 =  Integer.valueOf( args[  6 ] );
035:    			g2 =  Integer.valueOf( args[  7 ] );
036:    			b2 =  Integer.valueOf( args[  8 ] );
037:    			// 引数を変換し、左下の色に代入
038:    			r3 =  Integer.valueOf( args[  9 ] );
039:    			g3 =  Integer.valueOf( args[ 10 ] );
040:    			b3 =  Integer.valueOf( args[ 11 ] );
041:    			// 引数を変換し、右下の色に代入
042:    			r4 =  Integer.valueOf( args[ 12 ] );
043:    			g4 =  Integer.valueOf( args[ 13 ] );
044:    			b4 =  Integer.valueOf( args[ 14 ] );
045:    		}
046:    		catch( NumberFormatException ne )
047:    		{
048:    			System.out.println( "引数が不正です" );
049:    			return;
050:    		}
051:    
052:    		// 出力PNG名をoutnameに代入(拡張子".png"省略なし)
053:    		outname = args[ 0 ];
054:    
055:    		// 新しい画像を作成
056:    		// 24ビットカラーの画像を作成
057:    		try {
058:    			img = new BufferedImage( w, h,
059:    						 BufferedImage.TYPE_INT_RGB );
060:    		} catch ( Exception e ) {
061:    			// 画像作成に失敗したときの処理
062:    			e.printStackTrace();
063:    			return;
064:    		}
065:    
066:    		// グラデーション作成
067:    		int    x, y;
068:    		int    color, rv, gv, bv;	// 計算した色
069:    		double a,b;			// 距離		
070:    
071:    		for ( y = 0; y < h; ++ y ) {
072:    			// 距離bを計算
073:    			b = (double)y / (double)( h - 1 );
074:    			for ( x = 0; x < w; ++ x ) {
075:    				// 距離aを計算
076:    				a = (double)x / (double)( w - 1 );
077:    
078:    				// 色を計算
079:    				rv = (int)( (double)( r1 - r2 - r3 + r4 ) * a * b
080:    					- (double)( r1 - r2 ) * a
081:    					- (double)( r1 - r3 ) * b + (double)r1 );
082:    				gv = (int)( (double)( g1 - g2 - g3 + g4 ) * a * b
083:    					- (double)( g1 - g2 ) * a
084:    					- (double)( g1 - g3 ) * b + (double)g1 );
085:    				bv = (int)( (double)( b1 - b2 - b3 + b4 ) * a * b
086:    					- (double)( b1 - b2 ) * a
087:    					- (double)( b1 - b3 ) * b + (double)b1 );
088:    
089:    				// rv,gv,bvの色を合成
090:    				color = ( rv << 16 ) + ( gv << 8 ) + bv;
091:    
092:    				// 合成した色を(x,y)に設定
093:    				img.setRGB( x, y, color );
094:    			}
095:    		}
096:    
097:    		try {
098:    			// imgをoutname(出力PNG)に保存
099:    			boolean result;
100:    			result = ImageIO.write( img, "PNG", new File( outname ) );
101:    		} catch ( Exception e ) {
102:    			// outname(出力PNG)の保存に失敗したときの処理
103:    			e.printStackTrace();
104:    			return;
105:    		}
106:    
107:    		// 正常に終了
108:    		System.out.println( "正常に終了しました" );
109:    	}
110:    }

Gradation4を実行

C:\talavax\javasample>java Gradation4 gradation4.png 256 128 255 0 0 0 255 0 0 0 255
0 0 0

引数2と3で作成する画像の幅=256・高さ=128(pixel)を渡し、引数4と5と6で左上の色R1=255・G1=0・B1=0、引数7と8と9で右上の色R2=0・G2=255・B2=0、引数10と11と12で左下の色R3=0・G3=0・B3=255、引数13と14と15で右下の色R4=0・G4=0・B4=0を渡して、引数1のPNGファイル名でグラデーション画像を保存します。

実行結果

・作成したグラデーション画像(gradation4.png)

グラデーション画像(4隅の色)グラデーション画像(4隅の色)

左上が赤、右上が緑、左下が青、右下が黒のグラデーション画像が出力されました。

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

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

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

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

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

008:    		// 変数宣言
009:    		int w, h;		// 画像サイズ
010:    		int r1, g1, b1;	// 左上の色
011:    		int r2, g2, b2;	// 右上の色
012:    		int r3, g3, b3;	// 左下の色
013:    		int r4, g4, b4;	// 右下の色
014:    		String outname;	// 出力ファイル名
015:    		BufferedImage img = null;	// 画像格納クラス

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

017:    		// 入力した引数が15以上かを調べる
018:    		if ( 15 > args.length ) {
019:    			// 入力した引数が15未満の場合、使用方法を表示する
020:    			System.out.println( 
021:    				"Gradation4 [PNG名][幅][高][R1][G1][B1]…[R4][G4][B4]" );
022:    			return;
023:    		}

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

025:    		try {
026:    			// 引数を変換し、画像サイズに代入
027:    			w  =  Integer.valueOf( args[  1 ] );
028:    			h  =  Integer.valueOf( args[  2 ] );
029:    			// 引数を変換し、左上の色に代入
030:    			r1 =  Integer.valueOf( args[  3 ] );
031:    			g1 =  Integer.valueOf( args[  4 ] );
032:    			b1 =  Integer.valueOf( args[  5 ] );
033:    			// 引数を変換し、右上の色に代入
034:    			r2 =  Integer.valueOf( args[  6 ] );
035:    			g2 =  Integer.valueOf( args[  7 ] );
036:    			b2 =  Integer.valueOf( args[  8 ] );
037:    			// 引数を変換し、左下の色に代入
038:    			r3 =  Integer.valueOf( args[  9 ] );
039:    			g3 =  Integer.valueOf( args[ 10 ] );
040:    			b3 =  Integer.valueOf( args[ 11 ] );
041:    			// 引数を変換し、右下の色に代入
042:    			r4 =  Integer.valueOf( args[ 12 ] );
043:    			g4 =  Integer.valueOf( args[ 13 ] );
044:    			b4 =  Integer.valueOf( args[ 14 ] );
045:    		}
046:    		catch( NumberFormatException ne )
047:    		{
048:    			System.out.println( "引数が不正です" );
049:    			return;
050:    		}
051:    
052:    		// 出力PNG名をoutnameに代入(拡張子".png"省略なし)
053:    		outname = args[ 0 ];

与えられた引数をそれぞれ代入しています。

055:    		// 新しい画像を作成
056:    		// 24ビットカラーの画像を作成
057:    		try {
058:    			img = new BufferedImage( w, h,
059:    						 BufferedImage.TYPE_INT_RGB );
060:    		} catch ( Exception e ) {
061:    			// 画像作成に失敗したときの処理
062:    			e.printStackTrace();
063:    			return;
064:    		}

BufferedImageクラスコンストラクタで、新しいBufferedImageを構築しています。

BufferedImageコンストラクタ

BufferedImage( int width, int height, int imageType )
■新しい BufferedImage を構築します。
  パラメータ width     : 構築する画像の横ピクセル
        height    : 構築する画像の縦ピクセル
        imageType : 構築する画像のイメージ形式

imgTypeで指定しているTYPE_INT_RGBは、整数ピクセルにパックされた 8 ビット RGB 色成分によるイメージを表します。これを指定することで24ビットの画像を作成できます。

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

066:    		// グラデーション作成
067:    		int    x, y;
068:    		int    color, rv, gv, bv;	// 計算した色
069:    		double a,b;			// 距離		

グラデーション画像作成で使う変数を宣言しています。

071:    		for ( y = 0; y < h; ++ y ) {
072:    			// 距離bを計算
073:    			b = (double)y / (double)( h - 1 );
074:    			for ( x = 0; x < w; ++ x ) {
075:    				// 距離aを計算
076:    				a = (double)x / (double)( w - 1 );

画像の中の全てのピクセルの座標を参照するループをつくっています。具体的には、変数yを0~h-1、変数xを0~w-1に変化させています。このループの中で変数y=0のときb=0.0、y=h-1のときb=1.0、変数x=0のときa=0.0、x=w-1のときa=1.0になるような距離を計算しています。

078:    				// 色を計算
079:    				rv = (int)( (double)( r1 - r2 - r3 + r4 ) * a * b
080:    					- (double)( r1 - r2 ) * a
081:    					- (double)( r1 - r3 ) * b + (double)r1 );
082:    				gv = (int)( (double)( g1 - g2 - g3 + g4 ) * a * b
083:    					- (double)( g1 - g2 ) * a
084:    					- (double)( g1 - g3 ) * b + (double)g1 );
085:    				bv = (int)( (double)( b1 - b2 - b3 + b4 ) * a * b
086:    					- (double)( b1 - b2 ) * a
087:    					- (double)( b1 - b3 ) * b + (double)b1 );

距離aとbを使って色を計算しています。

値の補間式(代入後)

089:    				// rv,gv,bvの色を合成
090:    				color = ( rv << 16 ) + ( gv << 8 ) + bv;

計算したRGB合成して、色(変数color)を作成しています。

092:    				// 合成した色を(x,y)に設定
093:    				img.setRGB( x, y, color );

色(変数color)を(x,y)に代入しています。

097:    		try {
098:    			// imgをoutname(出力PNG)に保存
099:    			boolean result;
100:    			result = ImageIO.write( img, "PNG", new File( outname ) );
101:    		} catch ( Exception e ) {
102:    			// outname(出力PNG)の保存に失敗したときの処理
103:    			e.printStackTrace();
104:    			return;
105:    		}

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

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

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

■関連コンテンツ

グラデーション グラデーションを作成する方法を解説
光と色の3原色 光と色の3原色の考え方を解説
画像の色 画像の色について解説
画像ファイル形式 画像ファイル形式について解説
for文 繰り返し処理に使用するfor文について解説
コマンドライン引数 外部からの値を受け取る方法について解説

■新着情報

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

■広告

法人向けのETC専用カード

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

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

Topへ