web-dev-qa-db-ja.com

16ビットRGB565を24ビットRGB888に変換する方法を教えてください。

16ビットのrgb565イメージ(具体的には、Android framebuffer dump))を手に入れました。通常のモニターで表示できるように、24ビットのrgb888に変換したいと思います。

問題は、5ビットまたは6ビットのチャネルを8ビットにどのように変換するかです。明白な答えはそれをシフトすることです。私はこれを書くことから始めました:

puts("P6 320 480 255");
uint16_t buf;
while (read(0, &buf, sizeof buf)) {
    unsigned char red = (buf & 0xf800) >> 11;
    unsigned char green = (buf & 0x07e0) >> 5;
    unsigned char blue = buf & 0x001f;
    putchar(red << 3);
    putchar(green << 2);
    putchar(blue << 3);
}

ただし、これには必要なプロパティが1つありません。これは、0xffff0xffffffではなく0xf8fcf8にマップするためのものです。何らかの方法で価値を拡大する必要がありますが、それがどのように機能するかわかりません。

Android SDKには、スクリーンキャプチャを取得するddms(Dalvik Debug Monitor)と呼ばれるツールが付属しています。 コードの読み取り からわかる限り、それは同じものを実装していますロジックですが、スクリーンショットは異なっており、白は白にマッピングされています。

これが、上記のアルゴリズムによる raw framebuffersmart conversion by ddms、および dumb conversion です。後者はわずかに暗く、緑色であることに注意してください。

(ちなみに、この変換 ffmpegに実装されています ですが、LSBをすべてゼロのままにして、上記のダム変換を実行するだけです。)

私は2つの質問があると思います:

  • Rgb565をrgb888に変換する最も賢明な方法は何ですか?
  • DDMSはスクリーンショットをどのように変換していますか?
20
Josh Lee

シフトしてから、または最上位ビットでシフトできます。つまり.

Red 10101 becomes 10101000 | 101 => 10101101
    12345         12345---   123    12345123

これには求めるプロパティがありますが、あるスペースから別のスペースへの値の最も直線的なマッピングではありません。ただし、高速です。 :)

Cletusの回答はより完全で、おそらくより優れています。 :)

16
Jakob Borg

これらのそれぞれを5/6ビットスペースから8ビットスペースにマッピングする必要があります。

  • 5ビット= 32値
  • 6ビット= 64値
  • 8ビット= 256値

あなたが使用しているコードは、x5 * 256/32 = x8という単純なアプローチを採用しており、256/32 = 8で8を掛けると左シフト3になりますが、これは必ずしも新しい数値空間を埋めるわけではありません。正しく」。最大値の5から8は31から255であり、その中に解決策の手掛かりがあります。

x8 = 255/31 * x5
x8 = 255/63 * x6

どこ x5x6およびx8はそれぞれ5、6、8ビットの値です。

これを実装する最良の方法についての質問があります。これには除算が含まれ、整数除算では残りの結果(基本的に切り捨て)が失われるため、最良の解決策はおそらく浮動小数点演算を行ってから、整数に切り上げて半分にすることです。

これは、この式を使用して5ビットと6ビットの変換ごとにルックアップテーブルを生成するだけで大​​幅に高速化できます。

21
cletus

私の数セント:

あなたが正確なマッピングを気にしているなら、それでも高速なアルゴリズムはこれを考慮することができます:

R8 = ( R5 * 527 + 23 ) >> 6;
G8 = ( G6 * 259 + 33 ) >> 6;
B8 = ( B5 * 527 + 23 ) >> 6;

MUL、ADD、SHRのみを使用しているため、かなり高速です。反対側からは、適切な丸めを使用して浮動小数点マッピングに100%互換性があります。

// R8 = (int) floor( R5 * 255.0 / 31.0 + 0.5);
// G8 = (int) floor( G6 * 255.0 / 63.0 + 0.5);
// B8 = (int) floor( R5 * 255.0 / 31.0 + 0.5);

いくつかの余分なセント:888から565への変換に興味があるなら、これもとてもうまくいきます:

R5 = ( R8 * 249 + 1014 ) >> 11;
G6 = ( G8 * 253 +  505 ) >> 10;
B5 = ( B8 * 249 + 1014 ) >> 11;

定数は、物事を少しスピードアップするために、ソムリの早期拒否によるブルートフォース検索を使用して見つかりました。

19
Anonymous

iOS vImage変換

iOS Accelerate Framework は、vImageConvert_RGB565toARGB8888 関数:

Pixel8 alpha = alpha
Pixel8 red   = (5bitRedChannel   * 255 + 15) / 31
Pixel8 green = (6bitGreenChannel * 255 + 31) / 63
Pixel8 blue  = (5bitBlueChannel  * 255 + 15) / 31

1回限りの変換ではこれで十分高速ですが、多数のフレームを処理する場合は、iOS vImage変換などを使用するか、 NEON組み込み関数 を使用してこれを自分で実装します。

ARMsコミュニティフォーラムチュートリアルから

まず、RGB565からRGB888への変換について見ていきます。レジスタq0に8つの16ビットピクセルがあると想定し、赤、緑、青を3つのレジスタd2〜d4の8ビット要素に分離したいとします。

 vshr.u8      q1, q0, #3      @ shift red elements right by three bits,
                                @  discarding the green bits at the bottom of
                                @  the red 8-bit elements.
vshrn.i16    d2, q1, #5      @ shift red elements right and narrow,
                                @  discarding the blue and green bits.
vshrn.i16    d3, q0, #5      @ shift green elements right and narrow,
                                @  discarding the blue bits and some red bits
                                @  due to narrowing.
vshl.i8      d3, d3, #2      @ shift green elements left, discarding the
                                @  remaining red bits, and placing green bits
                                @  in the correct place.
vshl.i16  q0, q0, #3      @ shift blue elements left to most-significant
                                @  bits of 8-bit color channel.
vmovn.i16    d4, q0          @ remove remaining red and green bits by
                                @  narrowing to 8 bits.

各命令の影響は上記のコメントで説明されていますが、要約すると、各チャネルで実行される操作は次のとおりです。要素のいずれかの端からビットをプッシュするためにシフトを使用して隣接チャネルのカラーデータを削除します。 2番目のシフトを使用してカラーデータを各要素の最上位ビットに配置し、狭くして要素サイズを16ビットから8ビットに減らします。

マスキング操作の一部を実現するために、このシーケンスで要素サイズを使用して8および16ビット要素に対処することに注意してください。

小さな問題

上記のコードを使用してRGB888形式に変換すると、白が完全に白くないことに気付くでしょう。これは、各チャネルの最下位の2ビットまたは3ビットが1ではなく0であるためです。 (0x1F、0x3F、0x1F)としてRGB565で表される白は、RGB888では(0xF8、0xFC、0xF8)になります。これは、shift with insertを使用して、最上位ビットの一部を下位ビットに配置することで修正できます。

Android特定の例については、組み込み関数で記述された YUVからRGBへの変換 を見つけました。

これを試して:

red5 = (buf & 0xF800) >> 11;
red8 = (red5 << 3) | (red5 >> 2);

これは、すべてのゼロをすべてのゼロに、すべての1をすべての1に、そしてその間のすべてをその間のすべてにマッピングします。 1ステップでビットを所定の位置にシフトすることで、より効率的にすることができます。

redmask = (buf & 0xF800);
rgb888 = (redmask << 8) | ((redmask<<3)&0x070000) | /* green, blue */

緑と青についても同様に行います(6ビットの場合は、topメソッドでそれぞれ左2と右4にシフトします)。

4
Rex Kerr

一般的な解決策は、数値を2進数の小数として扱うことです。したがって、6ビットの数値63/63は、8ビットの数値255/255と同じです。他のポスターが示唆するように、最初に浮動小数点演算を使用してこれを計算し、次にルックアップテーブルを計算できます。これには、ビットバッシングソリューションよりも直感的であるという利点もあります。 :)

3
Nick Johnson

エラーjleedevがあります!!!

unsigned char green = (buf & 0x07c0) >> 5;
unsigned char blue = buf & 0x003f;

良いコード

unsigned char green = (buf & 0x07e0) >> 5;
unsigned char blue = buf & 0x001f;

乾杯、アンディ

3
Andy

以下を使用して、良い結果を得ました。私のLogitekカムは16ビットRGB555で、次を使用して24ビットRGB888に変換できることがわかりました。小さい動物のijgを使用してjpegとして保存することができました。stackoverflowにあるヒントをありがとう.

// Convert a 16 bit inbuf array to a 24 bit outbuf array
BOOL JpegFile::ByteConvert(BYTE* inbuf, BYTE* outbuf, UINT width, UINT height)
{     UINT row_cnt, pix_cnt;     
      ULONG off1 = 0, off2 = 0;
      BYTE  tbi1, tbi2, R5, G5, B5, R8, G8, B8;

      if (inbuf==NULL)
          return FALSE;

      for (row_cnt = 0; row_cnt <= height; row_cnt++) 
      {     off1 = row_cnt * width * 2;
            off2 = row_cnt * width * 3;
            for(pix_cnt=0; pix_cnt < width; pix_cnt++)
            {    tbi1 = inbuf[off1 + (pix_cnt * 2)];
                 tbi2 = inbuf[off1 + (pix_cnt * 2) + 1];
                 B5 = tbi1 & 0x1F;
                 G5 = (((tbi1 & 0xE0) >> 5) | ((tbi2 & 0x03) << 3)) & 0x1F;
                 R5 = (tbi2 >> 2) & 0x1F;
                 R8 = ( R5 * 527 + 23 ) >> 6;
                 G8 = ( G5 * 527 + 23 ) >> 6;
                 B8 = ( B5 * 527 + 23 ) >> 6;
                 outbuf[off2 + (pix_cnt * 3)] = R8;
                 outbuf[off2 + (pix_cnt * 3) + 1] = G8;
                 outbuf[off2 + (pix_cnt * 3) + 2] = B8;
            }
       }
       return TRUE;
}        
0
bravo