RGB(byte)からint(long)型へ
普通の計算の場合
V = R + (G * 256) + (B * 256 * 256)
ビットシフト、ビット演算の場合
V = R | (G << 8) | (B << 16)
int(long)型からRGB(byte型)へ
普通の計算の場合、%(mod)は割り算の余り 10%3=1、10割る3の余りは1
R = V % 256
G = (V / 256) % 256
B = V / 256 / 256
ビット演算、ビットシフトの場合
R = (byte)V
G = (byte)(V >> 8)
B = (byte)(V >>16)
例:RGB=(240,248,255)
普通の計算で
V = R + (G * 256) + (B * 256 * 256)
V = 240+(248*256)+(255*256*256)
V = 240+(63488)+(16711680)
V = 240+(63488)+(16711680)
V = 16775408
RGB(240,248,255) = 16775408
R = V % 256
G = (V / 256) % 256
B = V / 256 / 256
R = 16775408 % 256 = 240
G = (16775408 / 256) % 256
=65528.938 % 256
=248.938、小数点以下切り捨て
=248
B =16775408 / 256 / 256
=255.97241、小数点以下切り捨て
=255
16775408 = RGB(240,248,255)
ビット演算の前に
2進数表記
頭に0bをつける、0b0000、0b1111、0b0011
4桁ごとに_をつけてもいい
0b00000000 は 0b0000_0000と同じ
0b110011 は 0b11_0011と同じ
2進数から10進数
2進数のその桁が1のとき加算する値
桁 87654321
値 128 6432168421
桁→87654321
1101_0110 = 128640160420
=128+64+0+16+0+4+2+0
=214
Windowsの電卓便利
DECが10進数、BINが2進数、HEXは16進数
ビット演算で
V = R | (G << 8) | (B << 16)
10進数2進数
240 = 1111_0000
248 = 1111_1000
255 = 1111_1111
ビットシフト
(G << 8)、Gを左に8シフト
1111_1000これを左に8シフト(<<8)すると
1111_1000_0000_0000こうなる、元の位置のビットは0で埋まる
これを10進数で表すと63488
これは248に256を掛けたのと同じ値、つまり<<8は256(8bit)を掛けるのと同じ効果
(B << 16)、Bを左に16シフト
1111_1111<< 16 すると
1111_1111_0000_0000_0000_0000こう
10進数で表すと16711680は、もとの値255に65536(16bit)を掛けたのと同じ
OR演算
ORは |
どちらかが1だったら1、それいがいは0
R | G | B
R | Gだけをみると
R | G = 1111_0000 | 1111_1000_0000_0000
横に並べるより桁を揃えて縦に並べると見やすい、それぞれの桁を比較、OR演算
0000_0000_1111_0000
1111_1000_0000_0000
結果は
1111_1000_1111_0000
これとBもORで比較してR | G | Bは
0000_0000_1111_1000_1111_0000
1111_1111_0000_0000_0000_0000
結果
1111_1111_1111_1000_1111_0000
になった、この値を10進数で表すと
1111_1111_1111_1000_1111_0000 = 16775408
RGB(240,248,255) = 16775408
さっきの普通の計算のと同じ値
実際に書くときは
普通の計算の場合
V = R + (G * 256) + (B * 256 * 256)
ビットシフト、ビット演算の場合
V = R | (G << 8) | (B << 16)
こうだから、あんまり変わらないかな
けれどもint型からRGBに戻すときはビット演算のほうがわかりやすい
ビット演算でint型からRGB(byte型)に変換
R = (byte)V
G = (byte)(V >> 8)
B = (byte)(V >>16)
もとの値16775408の2進数は
1111_1111_1111_1000_1111_0000
Rの値は下位8bitに収まっている、Gは9~16bit、Bは17~24bit
それぞれを8bitとして取り出せばいい
R、byte型にキャストするだけ
R = (byte)16775408 = 240
byte型にキャストすると下位8桁だけが取り出されるみたい、逆に言うと9bit以上を切り捨てた値になるのでとても都合がいい
1111_1111_1111_1000_1111_0000
これをbyte型にキャストすると
1111_0000
G
1111_1111_1111_1000_1111_0000
これを>> 8で右に8シフトすると
0000_0000_1111_1111_1111_1000
下位8bitにGが来るので
あとはRと同じようにbyte型にキャストするだけ
B
シフトする位置が違うだけどGと同じ、16右にシフトして
0000_0000_0000_0000_1111_1111
あとはキャスト
AND演算を使った本当の方法?
R = (byte)(V & 0xFF)
G = (byte)(V >> 8) & 0xFF
B = (byte)(V >>16) & 0xFF
AND演算
ANDは&
どちらも1なら1、それ以外は0
0と0は0
0と1は0
1と0は0
1と1は1
16進数表記
頭に0xをつける、0x1A、0x25DC
16進数0xFF
10進数255
2進数1111_1111
1111_1111_1111_1000_1111_0000これに0xFFをAND演算すると
1111_1111_1111_1000_1111_0000 & 1111_1111
縦に並べてみると
1111_1111_1111_1000_1111_0000
0000_0000_0000_0000_1111_1111
AND演算はどちらも1なら1なので
0000_0000_0000_0000_1111_0000
これでRの部分1111_0000(240)が取り出せた
GやBは最初にビットシフトしてからは同じAND演算
G
1111_1111_1111_1000_1111_0000右に8シフトで
0000_0000_1111_1111_1111_1000AND
0000_0000_0000_0000_1111_11110xFFで
0000_0000_0000_0000_1111_1000は248
配列の要素数
配列の要素数が色数分用意できれば、そのIndexを利用して色数をカウントできる
24bitカラーの色数は256*256*256= 16,777,216色
32bitカラーの色数は256*256*256*256=4,294,967,296色
32bitカラーの4294967296色ぶんの要素数の配列を作ろうとするとエラー、44行目
少し書き換えてみたけど
32bitの要素数の配列は作れない
24bitぶんならおk
RGBをint型に変換
10進数の20を2進数と16進数で
2010進数
0001_01002進数
0x1416進数
ビットシフト
20をビットシフト
0001_0100 を<< 2すると
0101_0000は80
0001_0100 を>> 2すると
0000_0101は5
OR演算
AND演算
RGBをint型に
int型にしたRGBを元のRGBに変換
//byte型Colorをintに変換
private int RGBtoInt(byte r, byte g, byte b)
{
return r | (g << 8) | (b << 16);
}
//Colorをintに変換
private int RGBtoInt(Color c)
{
return c.R | (c.G << 8) | (c.B << 16);
}
//int型Colorをbyte型に変換
private (byte r, byte g, byte b) IntToRgb(int value)
{
byte r = (byte)value;
byte g = (byte)(value >> 8);
byte b = (byte)(value >> 16);
return (r, g, b);
//1行で書くと
//return ((byte)value, (byte)(value >> 8), (byte)(value >> 16));
}
private Color IntToColor(int value)
{
return Color.FromRgb((byte)value, (byte)(value >> 8), (byte)(value >> 16));
}
//0xFFを使うこっちのほうが正しいかも?
private Color IntToColor2(int value)
{
byte r = (byte)(value & 0xFF);
byte g = (byte)((value >> 8) & 0xFF);
byte b = (byte)(value >> 16);
return Color.FromRgb(r, g, b);
//1行で書くと
//return Color.FromRgb((byte)(value & 0xFF), (byte)((value >> 8) & 0xFF), (byte)(value >> 16));
}
24bitカラー画像の使用色数をカウント
ここからはBitmapSourceクラスのCopyPixelsで取得できる
byte型配列から色数カウントする
//使用色数カウント
//ピクセルフォーマットBgra32のbyte配列に対応
//カウントは透明度を無視して24bitだけ対応、bool型の配列で色の有無だけ確認、速い
private int Count24bit(byte[] pixels)
{
bool[] bColor = new bool[0xFFFFFF + 1];
//RGBをintに変換して、そのインデックスの値をTrueにする
for (int i = 0; i < pixels.Length; i += 4)
{
bColor[pixels[i] | (pixels[i + 1] << 8) | (pixels[i + 2] << 16)] = true;//bgr
}
//Trueの数をカウント
int count = 0;
for (int i = 0; i < bColor.Length; i++)
{
if (bColor[i] == true) { count++; }
}
return count;
}
ピクセルフォーマットBgra32に色の並び順は名前の通りBGRA
今回数えるのは24bitカラーなのでAは無視する、Indexだと3,7,11,15…
RGBからintに変換した値、これに当たるIndexの要素をtrueにしていく、これをすべてのピクセルの数だけ実行、277~280行目
あとはこのtrueの数をカウントすれば、それが使用色数
これでおk
LINQのCountでもカウントできるけど遅い
Whereを省略してもカウントできるけど
今回みたくLINQを1回しか使わない場合なら、0.1秒遅くなるだけなので
for+ifで5行も使うより291行目だけでいいかも
使用色数+色ごとに使われているピクセルの数もカウント
/// <summary>
/// 使用色数と色ごとのピクセル数をカウント、24bit
/// </summary>
/// <param name="pixels">BGRA順のbyte配列、PixelFormats.Bgra32</param>
/// <returns>Indexが色のint、要素の値がピクセル数</returns>
private int[] Count24bitColorAndPixels(byte[] pixels)
{
int[] iColor = new int[256 * 256 * 256];
for (int i = 0; i < pixels.Length; i += 4)
{
iColor[RGBtoInt(pixels[i], pixels[i + 1], pixels[i + 2])]++;
}
return iColor;
}
int型配列で要素数はさっきと同じのを用意
int型に変換した色を対応するIndexの要素にカウントしていく
これは、さっきは対応するIndexの要素をtrueにしていたのを++でカウントに変更しただけ
これで色ごとのピクセル数もわかる
Indexが色で値がピクセル数なので
Index[0]RGB(0,0,0)のピクセルは29946個Index[3]は2673個、int3のRGBは
3は2進数だと
0000_0000_0000_0000_0000_0011
ピクセルフォーマットはBgra32でRGBとは逆並びなので
RGB
0000_0000_0000_0000_0000_0011
こうなっているはず
RとGは0でBの0000_0011は10進数で3
なのでRGB(0,0,3)のピクセルは2673個
32bitカラー画像の使用色数
//byte型Colorをuintに変換
private uint ARGBtoUint(byte a, byte r, byte g, byte b)
{
return (uint)(a | (r << 8) | (g << 16) | (b << 24));
}
ARGB
0000_0000_0000_0000_0000_0000_0000_0000
24bitから8桁増えただけ
/// <summary>
/// 使用色数と色ごとのピクセル数をカウント、32bit
/// </summary>
/// <param name="pixels">BGRA順のbyte配列、PixelFormats.Bgra32</param>
/// <returns>Indexが色のint、要素の値がピクセル数</returns>
private Dictionary<uint, int> Count32bitColorAndPixels2(byte[] pixels)
{
Dictionary<uint, int> table = new Dictionary<uint, int>();
for (int i = 0; i < pixels.Length; i += 4)
{
uint key = ARGBtoUint(pixels[i + 3], pixels[i + 2], pixels[i + 1], pixels[i]);
//今の色のuintがなければ要素のValueを1で追加、すでにあればValueに+1
if (table.ContainsKey(key) == false) table.Add(key, 1);//追加
else { table[key]++; }//+1
}
return table;
}
32bitカラーの最大色数は配列の最大要素数(index)を超えてしまい、24bitと同じ方法は使えないので
Dictionary<key, value>を使う、使ってみた
keyの型はuint、Valueの型はintにしてみた
keyにRGBをint型に変換した値を入れる
Valueにはその色のカウント数
DictionaryのCount366195が使用色数になる
//uint型Colorをbyte型に変換
private (byte a, byte r, byte g, byte b) UintToArgb(uint value)
{
byte a = (byte)value;
byte r = (byte)(value >> 8);
byte g = (byte)(value >> 16);
byte b = (byte)(value >> 24);
return (a, r, g, b);
}
これも24bitのときから8桁増えただけ
627013887 =
0010_0101_0101_1111_0111_1000_1111_1111
0010_0101_= 37
0101_1111_= 95
0111_1000_= 120
1111_1111= 255
参照したところ
カラーコードを素敵に扱うビット演算(JavaScript おれおれ Advent Calendar 2011 – 8日目) | Ginpen.com
https://ginpen.com/2011/12/08/color-code-operatings/
ピクセルでRGB取得について - プログラマ専用SNS ミクプラ
https://dixq.net/forum/viewtopic.php?t=3154
組込み演算子 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C
https://ufcpp.net/study/csharp/st_operator.html
^ 演算子 - C# リファレンス | Microsoft Docs
https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/operators/xor-operator
16進数カラーコードからRGB値を取得したい - euremoのeramemo
https://seesaawiki.jp/eramemo/d/16%BF%CA%BF%F4%A5%AB%A5%E9%A1%BC%A5%B3%A1%BC%A5%C9%A4%AB%A4%E9RGB%C3%CD%A4%F2%BC%E8%C6%C0%A4%B7%A4%BF%A4%A4
ビット演算 (bit 演算) の使い方を総特集! 〜 マスクビットから bit DP まで 〜 - Qiita
https://qiita.com/drken/items/7c6ff2aa4d8fce1c9361
今回のアプリ
ウィンドウに画像ファルドロップで使用色数表示
ギットハブ
関連記事、2019/03/26は昨日
画像の使用色数を表示するアプリその3 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15914719.html