Как пикселировать двоичный (P6) файл PPM - PullRequest
1 голос
/ 11 мая 2019

Я изо всех сил пытаюсь пикселировать изображение, которое состоит из значений RGB, хранящихся в двоичном (P6) файле PPM. Шаги для пикселирования изображения следующие:

  1. Считать двоичные данные и сохранить их в одномерном массиве
  2. Итерация по данным, хранящимся в этом массиве, в ячейках размера 'c' (строка x столбцов). Эта переменная 'c' может быть изменена пользователем, но для этой программы в настоящее время она установлена ​​на int c = 4;, что означает, что она выполняет итерации по пиксельным данным в измерениях блока 4 x 4
  3. Теперь найдите среднее значение цвета в каждой из этих ячеек размера 'c'
  4. Вывести каждое среднее значение цвета в новый выходной файл PPM

Каждый двоичный файл PPM начинается с заголовка в следующем формате, состоящего из «магического числа», за которым следуют ширина, высота и, наконец, максимальное значение цвета 255. Комментарии заголовка игнорируются. В следующем примере заголовка показано изображение PPM в формате P6 (следовательно, двоичный файл ), шириной 16, высотой 16 и максимальным значением цвета 255:

P6
16
16
255 

Где я борюсь:

  1. Я не уверен, как найти среднее значение RGB для каждой ячейки и затем вывести его в новый поток данных. Я нашел способ сделать это при линейном считывании данных (то есть не считывая их в массив (или буфер)) путем деления общего количества цветов в этой ячейке на количество итераций цикла, но это оказалось неверным.
  2. Упорядочены ли вложенные циклы, изменяющие ориентацию выходного изображения, например он вращается на 180 градусов и т.д.? Я пытаюсь определить это с помощью чтения сырых двоичных данных.

Моя попытка:

#define _CRT_SECURE_NO_WARNINGS                 //preprocessor requirement 

#include <stdio.h>                              //library for I/O functions 
#include <stdlib.h>                             //library for general functions 

#define magic_size 10                           //macro for PPM character found within header

typedef struct {
    int t_r, t_g, t_b;                          //Struct to hold  RGB pixel data
} Pixel;

int main()
{
    char magic_number[magic_size];              //variable for PPM format
    int width = 0, height = 0, max_col = 0;     //imagine dimensions
    int c = 4;                                  //mosaic parameter

    /* INPUT FILE HANDLING */
    FILE *inputFile; 
    inputFile = fopen("Sheffield512x512.ppm", "r");
    //input file error handling
    if (inputFile == NULL)
    {
        printf(stderr, "ERROR: file cannot be opened");
        getchar();  //prevent cmd premature closure
        exit(1);    //exit program cleanly
    }

    /* OUTPUT FILE HANDLING */
    FILE *outputFile; 
    outputFile = fopen("mosaic.ppm", "w");
    //output file error handling
    if (outputFile == NULL)
    {
        printf(stderr, "ERROR: cannot write to file");
        getchar();  //prevent cmd premature closure
        exit(1);    //exit program cleanly
    }

    // Scan the header (these variables are used later on)
    fscanf(inputFile, "%s\n%d\n%d\n%d", &magic_number, &width, &height, &max_col);

    // Error handling. Program only supports binary files (i.e. of P6 format) 
    if (magic_number[1] != '6')
    {
        printf("Only Binary images supported!\n");
        getchar();  //prevent cmd premature closure
        return;
    }

    // Raw 1 dimensional store of pixel data
    Pixel *data = malloc(width*height * sizeof(Pixel));

    //2D index to access pixel data
    Pixel **pixels = malloc(height * sizeof(Pixel*));

    // Read the binary file data 
    size_t r = fread(data, width*height, sizeof(unsigned char), inputFile);

    // Build a 1-dimensional index for the binary data 
    for (unsigned int i = 0; i < height; ++i)
    {
        pixels[i] = data + (i * width); 
    }

    // Close the input file 
    fclose(inputFile);


    /* BEGIN PIXELATION PROCESS */

    // Print the OUTPUT file header 
    fprintf(outputFile, "%s\n%d\n%d\n%d", magic_number, width, height, max_col);

    //loop condition variables 
    int cw_x = ceil((double)(width / (float)c));
    int cw_y = ceil((double)(height / (float)c));

    //iterate through 2d array in cells of size c 
    for (int c_x = 0; c_x < cw_x; c_x += 1)
    {
        for (int c_y = 0; c_y < cw_y; c_y += 1)
        {

            //iterate within the cells
            for (int _x = 0; _x < c; _x++)
            {
                int x = c_x * c + _x;

                //bounds checking
                if (x < width)
                {
                    for (int _y = 0; _y < c; _y++)
                    {
                        int y = c_y * c + _y;

                        //bounds checking 
                        if (y < height)
                        {
                            //write data to the output FILE stream 
                            fwrite(data, width*height, sizeof(unsigned char), outputFile);
                        }
                    }
                }

            }
        }
    }

    //close the output file
    fclose(outputFile);

    return 0; 
}

1 Ответ

2 голосов
/ 11 мая 2019

В комментариях я дал вам несколько отзывов об ошибках в вашем коде. Вы можете исправить это самостоятельно. Возьмите отладчик для проверки / проверки всех этих предварительных шагов. Например, прочитайте файл и запишите его немедленно (и отобразите изображение), чтобы вы знали, что чтение в порядке.

Ваша основная проблема и вопрос касаются цикла.

По сути, у вас есть одномерный массив, который состоит из строки после сканирования, и каждая строка содержит пиксели. В отличие от формата BMP, в вашем формате не используются байты заполнения для выравнивания строк развертки по границам слов. Это немного облегчает.

Пиксель состоит из трех значений цвета, R, G и B, и я предполагаю, что каждое значение цвета составляет один байт (беззнаковый символ). Распределение и чтение памяти затем становится:

unsigned char *data = malloc(width*height*3);
r = fread(data, width*height*3, 1, inputFile);

Теперь цикл проходит через все строки с шагом четыре и обрабатывает каждый пиксель с шагом четыре . Таким образом, он обрабатывает один квадрат за раз, вычисляет среднее значение и записывает его:

    c= 4;
    for (y=0; y<height; y += c)
    {
        for (x=0; x<width; x += c)
        {
            unsigned int avgR=0, avgG=0, avgB= 0;

            for (dy=0; dy<c && y+dy<height; dy++)
            {
                for (dx=0; dx<c && x+dx<width; dx++)
                {
                    avgR += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3         // R pixel in line of square
                                 ];
                    avgG += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3 + 1     // G pixel in line of square
                                 ];
                    avgB += data[  y*width*3    // line in image
                                 + x*3          // pixel on line
                                 + dy*width*3   // line of square
                                 + dx*3 + 2     // B pixel in line of square
                                 ];
                }
            }
            unsigned char avgRb= avgR/(dx*dy);
            unsigned char avgGb= avgG/(dx*dy);
            unsigned char avgBb= avgB/(dx*dy);
            fwrite(&avgR,1,1,outputFile);
            fwrite(&avgG,1,1,outputFile);
            fwrite(&avgB,1,1,outputFile);
        }
    }

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

Примечания:

  • ..&& y+dy<height проверяет граничный случай, когда последний квадрат не соответствует высоте. То же самое для ширины.

  • , следовательно, среднее значение рассчитывается путем деления на (dx*dy).

Отказ

Я не мог проверить это, поэтому алгоритм - это умственная конструкция.

...