Как оптимизировать программу пикселизации изображения - PullRequest
1 голос
/ 16 апреля 2019

Я написал некоторый последовательный код, который хотел бы максимально оптимизировать, прежде чем я распараллелю его с помощью OpenMP.Программа читает файл PPM, просматривая данные пикселей в ячейках 4x4 (переменная c), затем находит среднее значение RGB для каждой из этих ячеек 4x4 и, наконец, записывает в новый файл, выводя средний цветзначение, опять же каждой из 4х4 ячеек.Это создает своего рода эффект мозаики / пикселизации.

После определения производительности моего кода основными узкими местами являются fscanf и fprintf.Я игнорирую время выполнения для чтения / записи на диск, поэтому эти две функции не имеют значения.

Мои попытки оптимизировать до сих пор:

  • Замыкание цикла: в коде есть два вложенных цикла FOR, которые имеют точно такие же условия цикла.Однако второй набор вложенных циклов FOR требует, чтобы функции, необходимые для вычисления среднего значения RGB, оставались за пределами этого конкретного набора.Затем необходимы вычисления среднего значения RGB для использования в третьем наборе вложенных циклов FOR (которые имеют те же условия цикла, что и второй набор).Из-за этого я изо всех сил пытался объединить второй и третий наборы вложенных циклов FOR, несмотря на их сходство.

  • Вычисления, инвариантные к циклу: я пытался переместить некоторые операции за пределы цикла, где это возможно, но это оказалось трудным.

Подводя итог: Как я могу оптимизировать эту программу, чтобы максимально сократить время выполнения?

Мой код:

typedef struct {                                //struct holding RGB type int
    int r, g, b;    //12 bytes
} pixelInt;
typedef struct {                                //struct holding RGB type unsigned char
    unsigned char r, g, b;  //3 bytes
} pixel;

int c = 4;             // Variable of 4x4 grids
int width, height;    //image variable declarations

//Raw 1 dimensional store of pixel data - will contain all the data for each pixel in the image
pixel *data = (pixel *)calloc(width * height, sizeof(pixelInt));

//Loop through entire input image 
        for (int yy = 0; yy < height; yy += c)
        {
            for (int xx = 0; xx < width; xx += c)
            {
                //the total colour of cell of size 'c'
                pixelInt cell_tot = { 0, 0, 0 };        //zero initialize struct containing mosaic cell pixel totals.

                unsigned int counter = 0; //the counter for how many pixels are in a 4x4 cell

                int bx = xx + c;  //used in loop conditions
                int by = yy + c;

                // Store each color from the cell into cell_tot struct
                for (int y = yy; y < by; y++)
                {
                    for (int x = xx; x < bx; x++)
                    {
                        unsigned int index_1d = x + y * width;      //calculate 1d index from x-index (x), y-index(y) and width;

                        unsigned char r, g, b; //maximum vales are 255, i.e. unsigned char data type                    

                        fscanf(f, "%hhu %hhu %hhu", &r, &g, &b); //%hhu is unsigned char specifier

                        //store the pixel value into data array
                        data[index_1d].r = r;
                        data[index_1d].g = g;
                        data[index_1d].b = b;

                        counter++; //increment counter

                        //average pixel color of cell
                        cell_tot.r += r;
                        cell_tot.g += g;
                        cell_tot.b += b;

                    }
                }

                //average colour of cell found by dividing cell total by loop counter 
                pixel cell_average;
                cell_average.r = cell_tot.r / counter;
                cell_average.g = cell_tot.g / counter;
                cell_average.b = cell_tot.b / counter;

                //Loop through the new image in cells of size c 
                for (int y = yy; y < by; y++)
                {
                    for (int x = xx; x < bx; x++)
                    {
                        unsigned int index_1d = x + y * width;      //calculate 1d index from x-index (x), y-index(y) and width;

                        //Assign average cell value to the pixels in the cell
                        data[index_1d].r = cell_average.r;
                        data[index_1d].g = cell_average.g;
                        data[index_1d].b = cell_average.b;

                        //Output the average colour value for the image  
                        fprintf(f_output, "%hhu %hhu %hhu \t", data[index_1d].r, data[index_1d].g, data[index_1d].b);
                    }
                    fprintf(f_output, "\n");    //Prints new line 
                }
            }
        } 

1 Ответ

1 голос
/ 16 апреля 2019

На изображении размером 1024x1024 на моем компьютере ваш код выполняется в 0.325s. Следующий код выполняется в 0.182s:

unsigned w = width/c, h = height/c;
unsigned *accum = (unsigned*)malloc(3*sizeof(unsigned)*w);
char *line = (char*)malloc(12*w);
unsigned denom = c*c;

//Loop through entire input image 
for (int yy = 0; yy < h; ++yy)
{
    memset(accum, 0, 3*sizeof(unsigned)*w);

    // read and accumulate c lines
    for(int y = 0; y < c; ++y)
    {
        for (int xx = 0; xx < w; ++xx)
        {
            for (int x = 0; x < c; ++x)
            {
                unsigned char r, g, b;
                fscanf(f, "%hhu %hhu %hhu", &r, &g, &b);
                accum[3*xx+0] += r;
                accum[3*xx+1] += g;
                accum[3*xx+2] += b;
            }
        }
    }

    // format a line
    for(int xx = 0; xx < w; ++xx)
    {
        char *cell = line + 12*c*xx; 
        snprintf(cell, 12, "%3u%4u%4u", accum[3*xx]/denom, accum[3*xx+1]/denom, accum[3*xx+2]/denom);
        cell[11] = '\t';
        for(int x = 1; x < c; ++x)
            memcpy(cell + 12*x, cell, 12);
    }

    // write it out times c
    line[12*w-1] = '\n';
    for(int y = 0; y < c; ++y)
        fwrite(line, 12*w, 1, f_output);
}

Хитрость в том, чтобы отформатировать усредненные значения только один раз, а затем продублировать отформатированные строки. Кроме того, действуя по одной строке за раз, у меня больше шансов использовать кеши памяти.

Чтобы выйти за пределы этого, вам потребуется заново реализовать fscanf для более быстрого анализа целых чисел.

...