在 .ppm 文件中创建棋盘仅适用于特定图块尺寸

问题描述 投票:0回答:1

我正在尝试编写一个 C 程序来生成给定长度和高度的 .ppm 图像文件,该文件看起来像一个矩形棋盘图案,由给定长度和高度的图块组成,并给出棋盘的两种颜色。我用连续的一维整数数组 (

size_t
) 表示 .ppm 图像中的像素。图像中的每个像素由 [0, 255] 范围内的三个整数值组成,代表其 RGB 代码,因此该数组包含图像中像素总数的三倍。数组按行优先顺序排列。

我尝试了几种不同的方法,例如使用单个 for 循环和模块化算术来适当地填充像素,并且我还尝试尝试一次填充一个图块图像。但我最终发现对我来说最有效的是从上到下一次一行地填充像素。几天后,我终于以为我的程序已经开始工作了,但由于某种原因,图像适用于任何瓦片高度值(参数

size_y
),但不适用于瓦片宽度(参数
size_x
)。该程序似乎仅当平铺宽度等于 20 或 100 时才能正确生成我的 500x500 图像。起初我认为这仅在平铺宽度均匀划分图像宽度时才有效,但我发现事实并非如此,因为使用 10、25、250 等平铺宽度,尽管是图像宽度 500 的因素,但仍然无法正确生成图像。似乎当我更改平铺宽度时,平铺高度显得非常小。我几乎可以肯定该程序是确定性的,输出始终与给定的输入相同,我不执行任何奇怪的指针操作或未初始化的变量。我已经尝试完全重新启动系统以防万一。我在 Stack Overflow 或网络上的其他地方找不到任何可以帮助我解决这个问题的内容。下面是我的相关 C 代码(图块宽度为 25):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define IMAGE_SIZE_X 500
#define IMAGE_SIZE_Y 500
#define IMAGE_ARRAY_SIZE 3*IMAGE_SIZE_X*IMAGE_SIZE_Y
#define MAX_PIXEL_VALUE 255


/*
    Creates a .ppm binary image file, given a file path (relative or absolute) with the file
    extension, and two positive integer dimensions (horizontal and vertical pixels), a positive
    maximum pixel color value (commonly 255), and a one-dimensional array of non-negative integers
    representing RGB (red, green blue) pixel values, in that order. Returns EXIT_SUCCESS on
    success.

    If a file with the given path already exists, the file is overwritten.
    If the file could not be opened for writing, errno is set, an error message is printed to
    stderr, and EXIT_FAILURE is returned. If the file could not be closed properly, errno is set,
    and EXIT_FAILURE is returned.
*/
int write_ppm(const char* filepath, size_t* pic, const size_t size_x, const size_t size_y, const size_t maxval) {
    //  open the file as a binary file, create the file first if it does not exist
    FILE *fp = fopen(filepath, "wb");
    if(!fp) {
        fprintf(stderr, "Failed to open file named \"%s\" for writing\n", filepath);
        return EXIT_FAILURE;
    }

    //  header
    fprintf(fp, "P6\n%zu %zu\n%zu\n", size_x, size_y, maxval);

    //  populate the RGB pixel values
    for(size_t i = 0; i < 3*size_x*size_y; i+=3)
        fprintf(fp, "%c%c%c", (unsigned char) pic[i], (unsigned char) pic[i+1], (unsigned char) pic[i+2]);

    //  close the file
    if(fclose(fp) != 0)
        return EXIT_FAILURE;
    else
        return EXIT_SUCCESS;
}


//  create a checkerboard pattern on an array representing a .ppm file,
//  given two colors and the two side lengths of the checkerboard tiles
void checkerboard_ppm(size_t* pic,
                      const size_t size_x,
                      const size_t size_y,
                      const size_t color0[3],
                      const size_t color1[3],
                      const size_t tileLength_x,
                      const size_t tileLength_y)
{
    int color = 0;

    for(size_t row = 0; row < size_y; row++) {
        for(size_t col = 3*size_x*row; col < 3*size_x*(row+1); col += 3) {
            //  swap colors when reaching the rightmost part of a tile,
            //  but not at the right edge of the image
            if(((col/3) % tileLength_x == 0) && ((col/3) % size_x != 0))
                color = 1 - color;
            if(color == 0) {
                pic[col]     = color0[0];
                pic[col + 1] = color0[1];
                pic[col + 2] = color0[2];
            }
            else {
                pic[col]     = color1[0];
                pic[col + 1] = color1[1];
                pic[col + 2] = color1[2];
            }
        }
        //  swap colors after writing a number of rows equal to the tile height
        if((row + 1) % tileLength_y == 0)
            color = 1 - color;
    }
}


int main() {
    const size_t RED  [3] = {255,   0,   0},
                 BLUE [3] = {  0,   0, 255};

    size_t image[IMAGE_ARRAY_SIZE];
    checkerboard_ppm(image, IMAGE_SIZE_X, IMAGE_SIZE_Y, RED, BLUE, 25, 20);
    write_ppm("output.ppm", image, IMAGE_SIZE_X, IMAGE_SIZE_Y, MAX_PIXEL_VALUE);
    return EXIT_SUCCESS;
}

这里是查看.ppm 文件的便捷站点
我很感激任何指导,谢谢!

c image ppm
1个回答
0
投票

这是我整理的一个示例,可能会有所帮助。

我发现为像素和图像创建结构以将所有数据保存在一起,使循环索引和颜色分配更容易,并减少所需参数的数量很有用。

我还转而使用 fwrite 将整个像素数组写入文件,因为数据已正确排列以使其成为可能。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

#define IMAGE_SIZE_X 500
#define IMAGE_SIZE_Y 500

typedef struct Pixel
{
    uint8_t r;
    uint8_t g;
    uint8_t b;
} Pixel;

typedef struct Image
{
    size_t width;
    size_t height;
    Pixel* pixels;
} Image;

int write_ppm(const char* filepath, Image* image) 
{
    FILE* fp = fopen(filepath, "wb");
    if (!fp) {
        fprintf(stderr, "Failed to open file named \"%s\" for writing\n", filepath);
        return EXIT_FAILURE;
    }

    fprintf(fp, "P6\n%zu %zu\n255\n", image->width, image->height);
    fwrite(image->pixels, 1, image->width * image->height * sizeof(Pixel), fp);
    fclose(fp);

    return EXIT_SUCCESS;
}

void checkerboard_ppm(Image* image, const Pixel colors[2], const size_t tileLength_x, const size_t tileLength_y)
{
    for (size_t row = 0; row < image->height; ++row) 
    {
        int colorIndex = (row / tileLength_y) % 2;
        for (size_t col = 0; col < image->width; ++col)
        {
            if (col && ((col % tileLength_x) == 0))
            {
                colorIndex = 1 - colorIndex;
            }
            image->pixels[image->width * row + col] = colors[colorIndex];
        }
    }
}

Image* newImage(size_t width, size_t height)
{
    Image *image = calloc(1, sizeof *image);
    image->width = width;
    image->height = height;
    image->pixels = calloc(width * height, sizeof *image->pixels);

    return image;
}

void freeImage(Image* image)
{
    free(image->pixels);
    free(image);
}

int main() 
{
    const Pixel colors[2] = { { 255,   0,   0 }, { 0,   0, 255 } };
    Image* image = newImage(IMAGE_SIZE_X, IMAGE_SIZE_Y);

    checkerboard_ppm(image, colors, 25, 20);
    write_ppm("output.ppm", image);
    
    freeImage(image);

    return EXIT_SUCCESS;
}
© www.soinside.com 2019 - 2024. All rights reserved.