8

私は、ディザリングを使用して 24 ビット RGB ビットマップを 16 ビット (RGB565) ビットマップに変換する、最適化された (つまり、迅速な) アルゴリズムを探していました。ディザリングの適用方法を実際に制御できる C/C++ の何かを探しています。GDI+ はいくつかのメソッドを提供しているようですが、それらがディザリングされているかどうかはわかりません。また、ディザリングを行う場合、どのようなメカニズムを使用していますか (Floyd-Steinberg?)

ディザリングによるビットマップの色深度変換の良い例はありますか?

4

4 に答える 4

6

おっしゃる通り、フロイド・スタインバーグのディザ法は簡単で早いので人気があります。24 ビット カラーと 16 ビット カラーの微妙な違いについては、視覚的にほぼ最適な結果が得られます。

サンプル画像のレナを使用するよう提案されましたが、使用しないことにしました。テスト画像としての長い歴史にもかかわらず、現代の感性には性差別的すぎると思います. 代わりに、私は自分の写真を提示します。最初はオリジナルで、ディザリングされた RGB565 への変換が続きます (表示用に 24 ビットに変換されます)。

オリジナル Floyd-Steinberg ディザ RGB565

そして、C++ でのコード:

inline BYTE Clamp(int n)
{
    n = n>255 ? 255 : n;
    return n<0 ? 0 : n;
}

struct RGBTriplet
{
    int r;
    int g;
    int b;
    RGBTriplet(int _r = 0, int _g = 0, int _b = 0) : r(_r), g(_g), b(_b) {};
};

void RGB565Dithered(const BYTE * pIn, int width, int height, int strideIn, BYTE * pOut, int strideOut)
{
    std::vector<RGBTriplet> oldErrors(width + 2);
    for (int y = 0;  y < height;  ++y)
    {
        std::vector<RGBTriplet> newErrors(width + 2);
        RGBTriplet errorAhead;
        for (int x = 0;  x < width;  ++x)
        {
            int b = (int)(unsigned int)pIn[3*x] + (errorAhead.b + oldErrors[x+1].b) / 16;
            int g = (int)(unsigned int)pIn[3*x + 1] + (errorAhead.g + oldErrors[x+1].g) / 16;
            int r = (int)(unsigned int)pIn[3*x + 2] + (errorAhead.r + oldErrors[x+1].r) / 16;
            int bAfter = Clamp(b) >> 3;
            int gAfter = Clamp(g) >> 2;
            int rAfter = Clamp(r) >> 3;
            int pixel16 = (rAfter << 11) | (gAfter << 5) | bAfter;
            pOut[2*x] = (BYTE) pixel16;
            pOut[2*x + 1] = (BYTE) (pixel16 >> 8);
            int error = r - ((rAfter * 255) / 31);
            errorAhead.r = error * 7;
            newErrors[x].r += error * 3;
            newErrors[x+1].r += error * 5;
            newErrors[x+2].r = error * 1;
            error = g - ((gAfter * 255) / 63);
            errorAhead.g = error * 7;
            newErrors[x].g += error * 3;
            newErrors[x+1].g += error * 5;
            newErrors[x+2].g = error * 1;
            error = b - ((bAfter * 255) / 31);
            errorAhead.b = error * 7;
            newErrors[x].b += error * 3;
            newErrors[x+1].b += error * 5;
            newErrors[x+2].b = error * 1;
        }
        pIn += strideIn;
        pOut += strideOut;
        oldErrors.swap(newErrors);
    }
}

このコードが完璧であることは保証しません。別のコメントでほのめかした微妙なエラーの 1 つを修正する必要がありました。しかし、それは上記の結果を生成しました。これは、Windows で使用される BGR 順序で 24 ビット ピクセルを取り、リトル エンディアン順序で R5G6B5 16 ビット ピクセルを生成します。

于 2012-07-25T13:27:29.537 に答える
2

Floyd–Steinberg ディザリング

for each y from top to bottom
   for each x from left to right
      oldpixel := pixel[x][y]
      newpixel := find_closest_palette_color(oldpixel)
      pixel[x][y] := newpixel
      quant_error := oldpixel - newpixel
      pixel[x+1][y] := pixel[x+1][y] + 7/16 * quant_error
      pixel[x-1][y+1] := pixel[x-1][y+1] + 3/16 * quant_error
      pixel[x][y+1] := pixel[x][y+1] + 5/16 * quant_error
      pixel[x+1][y+1] := pixel[x+1][y+1] + 1/16 * quant_error

これを簡単に実装できると思います!

于 2012-07-24T22:19:50.667 に答える