色の出現度数を調べる

 ビットマップの減色や圧縮処理を行う時には、どの色がどの程度の頻度で含まれるか、といった色の度数分布表が欲しくなる時があります。今回は、こうした画像処理の前処理として色の度数分布を求め出現頻度が多い順に整列してみる事にしましょう。つまり、ビットマップに含まれる色(ピクセルの値)を多い順に並べてみるわけです。

画像内の色の度数分布を作成する

 色の度数を調べるには「どの色が」「何回」出てきたか、という2つの情報が必要です。そこで、今回は


  typedef struct {

      DWORD dwRGB,dwNum;

  } sRGB;

 というsRGB構造体を定義して、この構造体でdwRGB色の出現回数dwNumを記録するようにしました。度数分布を作るアルゴリズムは、

  1. ビットマップに含まれる色数を調べる
  2. 色数分のsRGB配列を作成する
  3. sRGB配列のdwRGBにビットマップ内の色を記録
  4. 各ピクセルの色を調べ、一致するdwRGBを持つsRGBdwNumを加算
  5. sRGBdwNumで降順にソート

 という感じでよいでしょう。この内、ビットマップ内の色数を調べる処理はすでに以前やった事があるので、それを使います。

  fColors=GlobalAlloc(GPTR,256*256*256/8);

  for (i=0;i<dwHeight;i++)
      for (j=0;j<dwWidth;j++) {

          /* (j,i)の色をdwPixelに32ビットで格納 */
          dwR=lpBMP[j*3+i*dwLength+2];
          dwG=lpBMP[j*3+i*dwLength+1];
          dwB=lpBMP[j*3+i*dwLength];
          dwPixel=(dwR << 16)+(dwG << 8)+dwB;

          /* dwPixelビット目の位置を計算 */
          dwByte=dwPixel/8;
          dwBit=dwPixel%8;

          /* フラグ領域のdwPixelビットを検査 */
          if ((fColors[dwByte] & (1 << dwBit))==0) {

              /* フラグ領域のdwPixelビット目を立てる */
              fColors[dwByte]=fColors[dwByte] | (1 << dwBit);
              dwCount++; /* 色数カウンタ更新 */

          }

      }

  lpData=(sRGB*)GlobalAlloc(GPTR,dwCount*sizeof(sRGB));

 これで、ビットマップに含まれる色数がdwCountに、またどの色が含まれるかという情報がfColors配列に求まり、色数分のsRGB配列が出来ました(今回は、色をRGB各8ビット・0~256*256*256-1までの24ビット情報として扱っています)。

続いて、fColors配列でどの色が含まれるかを調べその色をsRGB配列のdwRGBに入れて行きましょう。この処理は、fColorsの各ビットが1色に対応するので、fColorsを1ビットずつ検査しビットが立っていれば新しい色としてsRGBdwRGBに記録していけば良いですね。

  j=0;

  /* ビットマップ内の色をdwRGBに記録 */
  for (i=0;i<256*256*256;i++)
      /* fColorsのiビット目を検査 */
      if((fColors[i/8] & (1 << (i % 8)))!=0)
          lpData[j++].dwRGB=i;

 これでビットマップに含まれる色をsRGB配列のdwRGBに記録する事が出来ました。後は、ビットマップの各ピクセルの色を調べて、その色に対応する(ピクセルの色とdwRGBが一致する)sRGBdwNumを増やしていけばsRGBがビットマップに含まれる色の「度数分布表」になります。

  for (i=0;i<dwHeight;i++)
      for (j=0;j<dwWidth;j++) {

          /* (j,i)の色をdwPixelに32ビットで格納 */
          dwR=lpBMP[j*3+i*dwLength+2];
          dwG=lpBMP[j*3+i*dwLength+1];
          dwB=lpBMP[j*3+i*dwLength];
          dwPixel=(dwR << 16)+(dwG << 8)+dwB;

          for (k=0;k<dwCount;k++)
              if (dwPixel==lpData[k].dwRGB) {

                  lpData[k].dwNum++;
                  break;

              }

      }

 度数分布表が出来たら、dwNumをキーにソートしてsRGBを「出現度数」が多い順に並び替えます。

  for (i=0;i<dwCount;i++)
      for (j=i+1;j<dwCount;j++)
          if (lpData[j].dwNum>lpData[i].dwNum) {

              wData=lpData[j];
              lpData[j]=lpData[i];
              lpData[i]=wData;

          }

 これで、sRGBに出現度数順の色度数分布が出来ました。例えば、sRGB[0]dwRGBを見ればビットマップの中で最も多い色が、dwNumを見ればその色のピクセル数が得られます。

プログラム

プログラムソース表示

「読み込み」ボタンかドラッグ&ドロップで24ビットフルカラーBMPを読み込んだら、「度数表作成」ボタンで度数分布表を作成してみてください。処理が終わると、度数分布の上位8色を色と度数で表示します。
 ただし、大きな画像や色数の多い画像では膨大な時間がかかるので数百から数千色の小さ目のビットマップで試してみた方が良いでしょう。