Самая быстрая библиотека дизеринга/полутонов в C

Я разрабатываю собственный сервер тонкого клиента, который обслуживает отображаемые веб-страницы для своих клиентов. Сервер работает на многоядерном Linux-боксе, а Webkit предоставляет механизм рендеринга html.

Единственная проблема заключается в том, что дисплей клиентов ограничен 4-битной (16 цветов) палитрой оттенков серого. В настоящее время я использую LibGraphicsMagick для сглаживания изображений (RGB-> 4-битные оттенки серого), что является очевидным узким местом в производительности сервера. Профилирование показывает, что более 70% времени тратится на выполнение функций сглаживания GraphicsMagick.

Я изучил stackoverflow и Interwebs в поисках хорошего высокопроизводительного решения, но, похоже, никто не проводил тестов для различных библиотек обработки изображений и решений для сглаживания.

Я был бы более чем счастлив узнать:

  1. Каковы библиотеки с наивысшей производительностью в отношении дизеринга / полутонового изображения / квантования изображений RGB в 4-битные оттенки серого.
  2. Есть ли какие-либо специализированные библиотеки дизеринга или какие-либо общедоступные фрагменты кода, на которые вы могли бы мне указать?
  3. Какие библиотеки вы предпочитаете для работы с графикой с точки зрения высокой производительности?

Предпочтение отдается библиотекам языка C.


person Community    schedule 28.09.2009    source источник


Ответы (5)


Дизеринг займет довольно много времени в зависимости от выбранного алгоритма.

Реализовать Байера (матрицу) и Дизеринг Флойда-Стейнберга (диффузионный).

Фильтрацию Байера можно выполнить очень быстро, если закодировать ее с помощью MMX/SSE для обработки параллельных пикселей. Вы также можете выполнять сглаживание/преобразование с помощью шейдера графического процессора.

FWIW, вы уже используете GraphicsMagick, но есть полный список графических библиотек OSS здесь

person Adisak    schedule 28.09.2009
comment
Спасибо за огромный список, я просто хотел бы, чтобы там уже были какие-то тесты. - person ; 29.09.2009
comment
@Jamie: отредактированные ссылки - старая ссылка (scien.stanford.edu /class/psych221/projects/02/mdeleon/) не работал. - person Adisak; 06.10.2010

Из списка, предоставленного Adisak, без какого-либо тестирования я бы поставил на AfterImage. Люди Afterstep помешаны на скорости, а также описали хитрый алгоритм.

Вы можете использовать альтернативный подход, если ваш сервер может быть оснащен приличной видеокартой PCI-express с OpenGL. Вот приведены некоторые спецификации от Nvidia. Найдите «индексный режим». Что вы можете сделать, так это выбрать режим отображения 16 или 256 цветов, визуализировать изображение в виде текстуры на плоском многоугольнике (например, на стороне куба), а затем прочитать кадр обратно.

При чтении кадра с карты OpenGL важно, чтобы пропускная способность от карты была хорошей, поэтому необходим PCI-express. Как говорится в документации, вам также нужно выбирать цвета в индексированном режиме для приличных эффектов.

person Prof. Falken    schedule 27.10.2009
comment
@ Spike0xff, на самом деле, даже не обязательно, чтобы это был графический процессор общего назначения (программируемый), достаточно всего, что работает с OpenGL или DirectX. - person Prof. Falken; 13.10.2010

Я знаю, что это не библиотека C, но мне стало любопытно, что доступно для .NET для распространения ошибок, которое я использовал около 20 лет назад в проекте. Я нашел это и конкретно это метод.

Но чтобы попытаться быть полезным :) Я нашел эту библиотеку C.

person kenny    schedule 02.10.2009
comment
Ссылка «Библиотека C» в настоящее время не работает. - person Jamie; 06.10.2010

Вот реализация метода Флойда-Стейнберга для полутонирования:

#include <opencv2/opencv.hpp>

using namespace cv;

int main(){

uchar scale = 128; // change this parameter to 8, 32, 64, 128 to change the dot size
Mat img = imread("../halftone/tom.jpg", CV_LOAD_IMAGE_GRAYSCALE);
for (int r=1; r<img.rows-1; r++) {
    for (int c=1; c<img.cols-1; c++) {
        uchar oldPixel = img.at<uchar>(r,c);
        uchar newPixel = oldPixel / scale * scale;
        img.at<uchar>(r,c) = newPixel;
        int quantError = oldPixel - newPixel;
        img.at<uchar>(r+1,c)   +=  7./16. * quantError;
        img.at<uchar>(r-1,c+1) +=  3./16. * quantError;
        img.at<uchar>(r,c+1) +=  5./16. * quantError;
        img.at<uchar>(r+1,c+1) +=  1./16. * quantError;
    }
}
imshow("halftone", img);
waitKey();
}
person RawMean    schedule 27.07.2013

Вот решение, которое вы ищете. Это функция C, которая выполняет упорядоченный дизеринг (байеровский) с параметром для цветов. Это достаточно быстро, чтобы использовать его в обработке в реальном времени.

#ifndef MIN
#define MIN(a,b)            (((a) < (b)) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a,b)            (((a) > (b)) ? (a) : (b))
#endif

#ifndef CLAMP
//  This produces faster code without jumps
#define     CLAMP( x, xmin, xmax )      (x) = MAX( (xmin), (x) );   \
                                        (x) = MIN( (xmax), (x) )
#define     CLAMPED( x, xmin, xmax )    MAX( (xmin), MIN( (xmax), (x) ) )
#endif

const   int BAYER_PATTERN_16X16[16][16] =   {   //  16x16 Bayer Dithering Matrix.  Color levels: 256
                                                {     0, 191,  48, 239,  12, 203,  60, 251,   3, 194,  51, 242,  15, 206,  63, 254  }, 
                                                {   127,  64, 175, 112, 139,  76, 187, 124, 130,  67, 178, 115, 142,  79, 190, 127  },
                                                {    32, 223,  16, 207,  44, 235,  28, 219,  35, 226,  19, 210,  47, 238,  31, 222  },
                                                {   159,  96, 143,  80, 171, 108, 155,  92, 162,  99, 146,  83, 174, 111, 158,  95  },
                                                {     8, 199,  56, 247,   4, 195,  52, 243,  11, 202,  59, 250,   7, 198,  55, 246  },
                                                {   135,  72, 183, 120, 131,  68, 179, 116, 138,  75, 186, 123, 134,  71, 182, 119  },
                                                {    40, 231,  24, 215,  36, 227,  20, 211,  43, 234,  27, 218,  39, 230,  23, 214  },
                                                {   167, 104, 151,  88, 163, 100, 147,  84, 170, 107, 154,  91, 166, 103, 150,  87  },
                                                {     2, 193,  50, 241,  14, 205,  62, 253,   1, 192,  49, 240,  13, 204,  61, 252  },
                                                {   129,  66, 177, 114, 141,  78, 189, 126, 128,  65, 176, 113, 140,  77, 188, 125  },
                                                {    34, 225,  18, 209,  46, 237,  30, 221,  33, 224,  17, 208,  45, 236,  29, 220  },
                                                {   161,  98, 145,  82, 173, 110, 157,  94, 160,  97, 144,  81, 172, 109, 156,  93  },
                                                {    10, 201,  58, 249,   6, 197,  54, 245,   9, 200,  57, 248,   5, 196,  53, 244  },
                                                {   137,  74, 185, 122, 133,  70, 181, 118, 136,  73, 184, 121, 132,  69, 180, 117  },
                                                {    42, 233,  26, 217,  38, 229,  22, 213,  41, 232,  25, 216,  37, 228,  21, 212  },
                                                {   169, 106, 153,  90, 165, 102, 149,  86, 168, 105, 152,  89, 164, 101, 148,  85  }
                                            };

//  This is the ultimate method for Bayer Ordered Diher with 16x16 matrix
//  ncolors - number of colors diapazons to use. Valid values 0..255, but interesed are 0..40
//  1       - color (1 bit per color plane,  3 bits per pixel)
//  3       - color (2 bit per color plane,  6 bits per pixel)
//  7       - color (3 bit per color plane,  9 bits per pixel)
//  15      - color (4 bit per color plane, 12 bits per pixel)
//  31      - color (5 bit per color plane, 15 bits per pixel)
void    makeDitherBayerRgbNbpp( BYTE* pixels, int width, int height, int ncolors )  noexcept
{
    int divider = 256 / ncolors;

    for( int y = 0; y < height; y++ )
    {
        const int   row = y & 15;   //  y % 16
        
        for( int x = 0; x < width; x++ )
        {
            const int   col = x & 15;   //  x % 16

            const int   t       = BAYER_PATTERN_16X16[col][row];
            const int   corr    = (t / ncolors);

            const int   blue    = pixels[x * 3 + 0];
            const int   green   = pixels[x * 3 + 1];
            const int   red     = pixels[x * 3 + 2];
    
            int i1  = (blue  + corr) / divider; CLAMP( i1, 0, ncolors );
            int i2  = (green + corr) / divider; CLAMP( i2, 0, ncolors );
            int i3  = (red   + corr) / divider; CLAMP( i3, 0, ncolors );

            //  If you want to compress the image, use the values of i1,i2,i3
            //  they have values in the range 0..ncolors
            //  So if the ncolors is 4 - you have values: 0,1,2,3 which is encoded in 2 bits
            //  2 bits for 3 planes == 6 bits per pixel

            pixels[x * 3 + 0]   = CLAMPED( i1 * divider, 0, 255 );  //  blue
            pixels[x * 3 + 1]   = CLAMPED( i2 * divider, 0, 255 );  //  green
            pixels[x * 3 + 2]   = CLAMPED( i3 * divider, 0, 255 );  //  red
        }

        pixels  += width * 3;
    }
}

В вашем случае вам нужно вызвать функцию с параметром ncolors=4 Это означает, что каждая цветовая плоскость (для оттенков серого это 1 плоскость) будет использовать 4 бита на пиксель.

Итак, вы должны позвонить:

makeDitherBayerRgbNbpp( pixels, width, height, 4 );

Входные пиксели имеют формат BGR. Выходные пиксели также имеют формат BGR для целей визуализации. Чтобы получить биты, вы должны заменить этот код:

pixels[x * 3 + 0]   = CLAMPED( i1 * divider, 0, 255 );  //  blue
pixels[x * 3 + 1]   = CLAMPED( i2 * divider, 0, 255 );  //  green
pixels[x * 3 + 2]   = CLAMPED( i3 * divider, 0, 255 );  //  red

Что-то вроде этого:

out.writeBit( i1 ); // blue
out.writeBit( i2 ); // green
out.writeBit( i3 ); // red

Вот пример изображения с вашими параметрами (4-битные оттенки серого) введите здесь описание изображения

Дополнительные сведения о дизеринге исходного кода и демонстрационном приложении см. на странице здесь

person Svetlyo    schedule 01.07.2021