He estado buscando una óptima (es decir, más rápido), algoritmo que convierte un RGB de 24 bits de mapa de bits a 16 bits (RGB565) mapa de bits usando el difuminado. Estoy buscando algo en C/C++ en el que realmente puede controlar cómo la interpolación se aplica. GDI+ parece proporcionar algunos de los métodos, pero no puedo decir si la tramado o no. Y, si no la trama, el mecanismo que se está utilizando (Floyd-Steinberg?)

¿Alguien tiene un buen ejemplo de mapa de bits de profundidad de color de la conversión con el tramado?

  • 565 seguirá siendo un aspecto terrible, incluso con un óptimo sistema de tramado. Sólo por curiosidad, ¿por qué hacen esto?
  • No estoy de acuerdo, véase este ejemplo: stackoverflow.com/a/3963150/5987
  • es porque estoy saliendo a una pieza de hardware que sólo admite RGB565. No aparecerá en el monitor.
  • Me doy cuenta de que es una sola imagen de color.
  • Estoy feliz de repetir el experimento en cualquier imagen que te gustaría.
  • Me imagino que Lena tiene que ser el estándar de color de 24 bits de la imagen.
  • mi tramado código no está hecho todavía, pero he intentado una conversión directa de Lena a 565 y no puedo decir la diferencia entre el antes y el después. Sin duda no peor que el típico artefactos JPEG. Me di cuenta de la misma con uno de mis propias fotos. Supongo que una imagen natural tiene la suficiente variación y el ruido de trabajo sin titubeos de la mayor parte del tiempo.
  • al final decidí usar una imagen diferente de Lena. Ver mi respuesta, espero que usted encuentre el ejemplo convincente.

InformationsquelleAutor JacobJ | 2012-07-24

3 Comentarios

  1. 5

    Como usted ha mencionado, el Floyd-Steinberg método es popular porque es simple y rápido. Para las sutiles diferencias entre 24-bit y 16-bit de color, los resultados serán casi óptimo visualmente.

    Fue sugerido que el uso de la imagen de ejemplo Lena pero me decidí en contra de ella; a pesar de su larga historia como una imagen de prueba, considero que es demasiado sexista para la sensibilidad moderna. En lugar quiero presentar una imagen de mi propio. El primero es el original, seguido por la conversión a interpolado RGB565 (y convertida de nuevo a 24-bit para la pantalla).

    ¿Qué es una buena, optimizado C/C++ del algoritmo para la conversión de 24 bits de mapa de bits a 16 bits, con un tramado?
    ¿Qué es una buena, optimizado C/C++ del algoritmo para la conversión de 24 bits de mapa de bits a 16 bits, con un tramado?

    Y el código en 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);
    }
    }

    No voy a garantizar este código es perfecto, yo ya tuve que arreglar uno de esos errores sutiles que he mencionado en otro comentario. Sin embargo sí generar los resultados anteriores. Se tarda de 24 bits píxeles en BGR orden como el usado por Windows, y produce R5G6B5 de 16 bits píxeles en little endian orden.

  2. 7

    Me sugirió utilizar ordenó el tramado (http://en.wikipedia.org/wiki/Ordered_dithering), ya que Floyd-Steinberg necesitan más procesamiento y calcular y sólo funciona en imagen /no funciona bien para la animación o el cambio constante en la pantalla.

    He Creado mi propia optimizado ordenó el tramado de 24/32 bits de color RGB de 16 bits RGB565 de color, que separada tresshold en subpíxel (Utilizado en mi AROMA proyecto). Era el camino más rápido, a continuación, Floyd-Steinberg, porque no es costosa (especialmente no se multiplica y divs cálculos), y capaces de utilizar en animaciones porque fijos tresshold.

    Es de calidad también, así que mucho mejor de lo ordenado algoritmo de tramado que se define en el wiki.

    Aquí un ejemplo de un tramado resultado:

    ¿Qué es una buena, optimizado C/C++ del algoritmo para la conversión de 24 bits de mapa de bits a 16 bits, con un tramado?

    Y aquí la fuente. ¡A disfrutar!

    /* Dither Tresshold for Red Channel */
    static const BYTE dither_tresshold_r[64] = {
    1, 7, 3, 5, 0, 8, 2, 6,
    7, 1, 5, 3, 8, 0, 6, 2,
    3, 5, 0, 8, 2, 6, 1, 7,
    5, 3, 8, 0, 6, 2, 7, 1,
    0, 8, 2, 6, 1, 7, 3, 5,
    8, 0, 6, 2, 7, 1, 5, 3,
    2, 6, 1, 7, 3, 5, 0, 8,
    6, 2, 7, 1, 5, 3, 8, 0
    };
    /* Dither Tresshold for Green Channel */
    static const BYTE dither_tresshold_g[64] = {
    1, 3, 2, 2, 3, 1, 2, 2,
    2, 2, 0, 4, 2, 2, 4, 0,
    3, 1, 2, 2, 1, 3, 2, 2,
    2, 2, 4, 0, 2, 2, 0, 4,
    1, 3, 2, 2, 3, 1, 2, 2,
    2, 2, 0, 4, 2, 2, 4, 0,
    3, 1, 2, 2, 1, 3, 2, 2,
    2, 2, 4, 0, 2, 2, 0, 4
    };
    /* Dither Tresshold for Blue Channel */
    static const BYTE dither_tresshold_b[64] = {
    5, 3, 8, 0, 6, 2, 7, 1,
    3, 5, 0, 8, 2, 6, 1, 7,
    8, 0, 6, 2, 7, 1, 5, 3,
    0, 8, 2, 6, 1, 7, 3, 5,
    6, 2, 7, 1, 5, 3, 8, 0,
    2, 6, 1, 7, 3, 5, 0, 8,
    7, 1, 5, 3, 8, 0, 6, 2,
    1, 7, 3, 5, 0, 8, 2, 6
    };
    /* Get 16bit closest color */
    BYTE closest_rb(BYTE c) { 
    return (c >> 3 << 3); /* red & blue */
    }
    BYTE closest_g(BYTE c) {
    return (c >> 2 << 2); /* green */
    }
    /* RGB565 */
    WORD RGB16BIT(BYTE r, BYTE g, BYTE b) {
    return ((WORD)((r>>3)<<11)|((g>>2)<<5)|(b>>3));
    }
    /* Dithering by individual subpixel */
    WORD dither_xy(
    int x, 
    int y, 
    BYTE r, 
    BYTE g, 
    BYTE b
    ){
    /* Get Tresshold Index */
    BYTE tresshold_id = ((y & 7) << 3) + (x & 7);
    r = closest_rb(
    MIN(r + dither_tresshold_r[tresshold_id], 0xff)
    );
    g = closest_g(
    MIN(g + dither_tresshold_g[tresshold_id], 0xff)
    );
    b = closest_rb(
    MIN(b + dither_tresshold_b[tresshold_id], 0xff)
    );
    return RGB16BIT(r, g, b);
    }
    /* Dithering Pixel from 32/24bit RGB 
    *
    * GetR, GetG, GetB -> Function to get individual color in pixel
    *
    */
    WORD dither_color_xy(int x, int y, DWORD col) {
    return dither_xy(x, y, GetR(col), GetG(col), GetB(col));
    }
    /* EXAMPLES */
    void ExampleDither1(WORD * dest, DWORD * src, int width, int height){
    int x, y;
    for (y=0; y<height; y++){
    for (x=0; x<width; x++){
    int pos = y * width + x;
    dest[pos] = dither_color_xy(x,y,src[pos]);
    }
    }
    }
    void ExampleDither2(WORD * dest, BYTE * src, int width, int height){
    int x, y;
    for (y=0; y<height; y++){
    for (x=0; x<width; x++){
    int pos = y * width + x;
    dest[pos] = dither_xy(x,y,src[pos*3],src[pos*3+1],src[pos*3+2]);
    }
    }
    }

    Otro Resultado (parte Superior 24bit – parte Inferior Ordenó RGB565-16bit):
    ¿Qué es una buena, optimizado C/C++ del algoritmo para la conversión de 24 bits de mapa de bits a 16 bits, con un tramado?
    Vista de imagen de máxima resolución

  3. 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

    Apuesto un dólar puede implementar fácilmente!

    • Sí, vi que el algoritmo en el artículo de la wikipedia así. Pero, mi instinto me dice que no puede ser más óptimo RGB565 algoritmos de dithering que hay (incluso si no son de Floyd-Steinberg). Este parecía un poco caro. Si nadie puede venir para arriba con uno, voy a marcar esto como una respuesta.
    • Este es un algoritmo donde es fácil hacer pequeños errores. @JacobJ, voy a tratar de conseguir una C/C++ aplicación para usted esta noche.
    • Gracias! Estoy haciendo mi propia implementación de ahora, pero me gustaría comparar en contra de lo que vienen! Me acabo de dar cuenta de que en realidad estoy pasando de un 32-bit ARGB de búfer (UINT32*) para un RGB565 buffer (UINT16*). Los últimos 4 líneas son lo que parecen los más difíciles. Necesito a poco-shift cuidadosamente. Si usted hace esto, esta noche, por favor enviar una respuesta! 😉
    • Me pregunto si usted necesita tener cuidado de hacer los cálculos lineales, RGB y, a continuación, convertir a sRGB al final. ¿Crees que se podría hacer una gran diferencia?
    • cualquiera de los cálculos en RGB que impliquen la suma o la resta será la más correcta en un espacio lineal, pero he encontrado que trabajan directamente con sRGB es «lo suficientemente cerca» en la mayoría de los casos.

Dejar respuesta

Please enter your comment!
Please enter your name here