2017/03/06 公開
・メディアンカット その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型となります。
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にします。
①は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)かを判定する例です。
①はm_data[ dpos ]の値、②は0x80000000を6回右にビットシフトした値、③は①と②の論理積(AND)を計算した値です。この結果、③が0になったので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色(RGB)の混合と、インクの3色(CMY)の混合の考え方を説明しています。 |
|
画素画素とは、デジタル画像データを構成している色情報を持った点のことです。 |
|
while文繰り返し処理に使用するwhile文について解説 |
■新着情報
2022.07.07 | 外部プログラムの実行 | exeファイル実行 |
2022.07.06 | 完全数 | 6=1+2+3 |
■広告