ゆるゆるプログラミング

・メディアンカット その2

フルカラー画像の色数を減らす処理の1つのメディアンカットのJavaのソースプログラムを説明していきます。全ソースの内容は、「減色処理(メディアンカット)」を参照してください。

007:    // 元画像のRGBを格納するクラス(bitの3次元配列)
008:    class ColorCube
009:    {
010:    	private int m_data[];
011:    
012:    	// 初期化
013:    	public ColorCube()
014:    	{
015:    		// intは32bitなので、256x256x256を32で割っている
016:    		m_data = new int[ 256 * 256 * 256 / 32 ];
017:    		// 全データを0にする
018:    		for ( int i = 0; i < m_data.length; ++ i ) m_data[ i ] = 0;
019:    	}
020:    
021:    	
022:    	// RGBの値から配列の添え字を計算
023:    	public int data_position( int r, int g, int b )
024:    	{
025:    		if ( ( 0 > r ) || ( 255 < r ) ) return -1;
026:    		if ( ( 0 > g ) || ( 255 < g ) ) return -1;
027:    		if ( ( 0 > b ) || ( 255 < b ) ) return -1;
028:    
029:    		return ( ( r * 256 + g ) * 256 + b ) / 32;
030:    	}
031:    	
032:    
033:    	// 指定のRGBの位置にbit=1を設定
034:    	public boolean set( int r, int g, int b )
035:    	{
036:    		int dpos;
037:    
038:    		dpos = data_position( r, g, b );
039:    		if ( 0 > dpos ) return false;
040:    
041:    		//
042:    		m_data[ dpos ] = m_data[ dpos ] |
043:    				 ( (int)0x80000000 >> ( b % 32 ) );
044:    
045:    		return true;
046:    	}
047:    	
048:    
049:    	// 指定のRGBの位置のbitが1かを判定
050:    	public boolean get( int r, int g, int b )
051:    	{
052:    		int dpos;
053:    
054:    		dpos = data_position( r, g, b );
055:    		if ( 0 > dpos ) return false;
056:    
057:    		//
058:    		if ( 0 != ( m_data[ dpos ] &
059:    			 ( (int)0x80000000 >> ( b % 32 ) ) ) ) return true;
060:    
061:    		return false;
062:    	}
063:    
064:    
065:    	// 指定のRGBの範囲のbit=1の個数を返す
066:    	public int pixel_number( int rmin, int rmax, int gmin, int gmax,
067:    								 int bmin, int bmax )
068:    	{
069:    		int r, g, b;
070:    		int pixnum;
071:    
072:    		pixnum = 0;
073:    		for ( r = rmin; r <= rmax; ++ r ) {
074:    			for ( g = gmin; g <= gmax; ++ g ) {
075:    				for ( b = bmin; b <= bmax; ++ b ) {
076:    					if ( true == get( r, g, b ) ) ++ pixnum;
077:    				}
078:    			}
079:    		}
080:    
081:    		return pixnum;
082:    	}

画像で使われている全てのRGBの値(0~255)を格納するクラスColorCubeを作りました。このクラスRGBを軸にした三次元配列を扱うもので、任意のRGB値を有(ON)に設定、RGB値の有無(ON/OFF)の判定、指定したRGBの範囲にあるを有(ON)の数をカウントすることができます。

010:    	private int m_data[];
011:    
012:    	// 初期化
013:    	public ColorCube()
014:    	{
015:    		// intは32bitなので、256x256x256を32で割っている
016:    		m_data = new int[ 256 * 256 * 256 / 32 ];
017:    		// 全データを0にする
018:    		for ( int i = 0; i < m_data.length; ++ i ) m_data[ i ] = 0;
019:    	}

実装は3次元配列を使わずに、int型の1次元配列を使い、色の有無(ON/OFF)はint型ビットに割り当てています。

Javaで扱えるデータ型のうち、boolean型(論理値型)が「true」か「false」の2つの値だけを持てるので、色の有無(ON/OFF)を判定するのには最適ですが、今回は使用するデータサイズ(byte)を減らすためにビットを使いました。

boolean型のサイズは、実装はJava仮想マシンに依存するとのことなので1ビット、1バイト(8ビット)、4バイト(8ビット)なのかは不明ですが、意図的に有無(ON/OFF)の情報を1ビットで持つことで、使用するデータ量を最小にしています。RGB値の組み合わせ256×256×256をint型ビット数32で割った、524,288がint型の個数になり、この値にint型のバイト数4を掛けた値、524,288x4=2,097,152バイト(約2メガバイト)が使用するデータ量です。

下図は、G(緑)とB(青)成分を2048個のint型配列で表したものです。この塊が、R(赤)成分(0~255の256個)に対応する数だけ必要なので、256×2048=52,488が必要なint型となります。

G成分とB成分の格納配列

022:    	// RGBの値から配列の添え字を計算
023:    	public int data_position( int r, int g, int b )
024:    	{
025:    		if ( ( 0 > r ) || ( 255 < r ) ) return -1;
026:    		if ( ( 0 > g ) || ( 255 < g ) ) return -1;
027:    		if ( ( 0 > b ) || ( 255 < b ) ) return -1;
028:    
029:    		return ( ( r * 256 + g ) * 256 + b ) / 32;
030:    	}

data_positionメソッドは、RGBの値が格納されているint型配列の位置を返すメソッドです。指定したr,g,bの値のいずれかが、0~255の範囲にいない場合、-1を返しエラーとしています。このプログラムでは範囲外になることはありませんが、エラー処理としてソースに書いています。

( ( r * 256 + g ) * 256 + b ) / 32は戻り値で、r,g,bからint型配列添え字を計算しています。上図(G成分とB成分の格納配列)の考え方からは、r * 2048 + g * 8 + b / 32の式のほうがわかりやすいかもしれません。どちらも同じ計算結果になります。

033:    	// 指定のRGBの位置にbit=1を設定
034:    	public boolean set( int r, int g, int b )
035:    	{
036:    		int dpos;
037:    
038:    		dpos = data_position( r, g, b );
039:    		if ( 0 > dpos ) return false;
040:    
041:    		//
042:    		m_data[ dpos ] = m_data[ dpos ] |
043:    				 ( (int)0x80000000 >> ( b % 32 ) );
044:    
045:    		return true;
046:    	}

setメソッドは、r,g,bの位置に有(ON)を設定するメソッドです。r,g,bでdata_positionメソッドを実行し、配列添え字dposを取得します。この添え字から取得したm_data[ dpos ]には、32個の情報(32ビット)が含まれているので、この中に必要な情報(1ビット)をセットします。

m_data[ dpos ]の左から( b % 32 ) + 1番目がセットするビットです。0x80000000(2進数で'1000 0000 0000 0000 0000 0000 0000 0000')を( b % 32 )回分だけ右にビットシフトした値とm_data[ dpos ]論理和(OR)を計算することで、特定のビットを1(ON)にしています。

ここから、B成分=70を有(ON)にする例を説明します。下図の青線で囲まれた位置は、B=64~95の色の情報を持っているint型配列の位置を示しています。この中のB=70に対応するビットを1にします。

B成分=70のintの位置B成分=70のintの位置  

B成分=70にビットを設定 

①はm_data[ dpos ]の値、②は0x80000000を6回右にビットシフトした値、③は①と②の論理和(OR)を計算した値です。この結果、B成分=70の位置にビット1が設定されたことがわかります。

049:    	// 指定のRGBの位置のbitが1かを判定
050:    	public boolean get( int r, int g, int b )
051:    	{
052:    		int dpos;
053:    
054:    		dpos = data_position( r, g, b );
055:    		if ( 0 > dpos ) return false;
056:    
057:    		//
058:    		if ( 0 != ( m_data[ dpos ] &
059:    			 ( (int)0x80000000 >> ( b % 32 ) ) ) ) return true;
060:    
061:    		return false;
062:    	}

getメソッドは、r,g,bの位置が有(ON)が無(OFF)かを判定するメソッドです。r,g,bでdata_positionメソッドを実行し、配列添え字dposを取得します。この添え字から取得したm_data[ dpos ]には、32個の情報(32ビット)が含まれています。

setメソッドと同様に、0x80000000(2進数で'1000 0000 0000 0000 0000 0000 0000 0000')を( b % 32 )回分だけ右にビットシフトした値を求め、その値とm_data[ dpos ]論理積(AND)の計算を行い、その値が0でなければ有(ON)と判定し、0なら無(OFF)と判定しています。

以下の例は、B成分=70が有(ON)か無(OFF)かを判定する例です。

B成分=70にビットを設定 

①はm_data[ dpos ]の値、②は0x80000000を6回右にビットシフトした値、③は①と②の論理積(AND)を計算した値です。この結果、③が0になったのでB成分=70の位置にビットが無いと判定されます。

B成分=70にビットを設定 

これは、元データのB成分=70のビットが1の場合の結果です。③の結果が0ではないのでB成分=70の位置にビットが有ると判定されます。

065:    	// 指定のRGBの範囲のbit=1の個数を返す
066:    	public int pixel_number( int rmin, int rmax, int gmin, int gmax,
067:    								 int bmin, int bmax )
068:    	{
069:    		int r, g, b;
070:    		int pixnum;
071:    
072:    		pixnum = 0;
073:    		for ( r = rmin; r <= rmax; ++ r ) {
074:    			for ( g = gmin; g <= gmax; ++ g ) {
075:    				for ( b = bmin; b <= bmax; ++ b ) {
076:    					if ( true == get( r, g, b ) ) ++ pixnum;
077:    				}
078:    			}
079:    		}

pixel_numberメソッドは、rmin~rmax,gmin~gmax,bmin~bmaxの範囲で設定されている色(ビットが1のもの)の数を取得するメソッドです。

RGBの範囲のColorCubeを参照するループをつくっています。具体的には、変数rをrmin~rmax、変数gをgmin~gmax、変数bをbmin~bmax1に変化させ、getメソッドがtrueの個数を数えます。

ここから先の説明は次回に続きます。

■関連コンテンツ

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

■新着情報

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

■広告

法人向けのETC専用カード

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

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

Topへ