使用C语言Pixel结构体克隆BMP图像失败求助
BMP图像像素复制问题排查
我是C语言新手,使用Clion C17读取并复制BMP图像像素。已确认BMP头可正确复制,但分配的像素数据未被正确复制,代码存在问题,参考相关代码后仍未解决,请求帮忙排查。
相关代码
typedef struct { uint8_t blue; uint8_t green; uint8_t red; } Pixel; Pixel* read_bmp_pixels(FILE* stream, BMPHeader* header, Pixel* pixels) { int padding = (4 - (header->width_px * sizeof(Pixel)) % 4) % 4; if (pixels == NULL) { printf("Error: Failed to allocate memory for pixel data.\n"); exit(EXIT_FAILURE); } fseek(stream, 54, SEEK_SET); int index_p = 0; for (int i = 0; i < header->width_px; i++) { for (int j = 0; j < header->height_px; j++) { fread(&(pixels[index_p].blue), 1, 1, stream); fread(&(pixels[index_p].green), 1, 1, stream); fread(&(pixels[index_p].red), 1, 1, stream); index_p++; } fseek(stream, padding, SEEK_CUR); } //print_pixels(pixels, header->width_px * header->height_px); return pixels; } void copy(){ Pixel *small_pixel = malloc(num_of_small_pixels * sizeof(Pixel)); Pixel *big_pixel = malloc(num_of_big_pixels * sizeof(Pixel)); if (small_pixel == NULL) { printf("Error: Failed to allocate memory for pixel data.\n"); exit(EXIT_FAILURE); } if (big_pixel == NULL) { printf("Error: Failed to allocate memory for pixel data.\n"); exit(EXIT_FAILURE); } Pixel *read1= read_bmp_pixels(small_file, bmpHeader[0], small_pixel); FILE* test = fopen("test.bmp", "wb"); if (!test) { printf("Error: could not open output BMP file\n"); exit(1); } // Write BMP header to the output file fseek(test, 0, SEEK_SET); fwrite(small_header, sizeof(char), 54, test); rewind(test); fseek(test, 54, SEEK_SET); printf("%s", big_buffer); // Write the pixel data to the output file int num_pixels = bmpHeader[1]->width_px * bmpHeader[1]->height_px; int bytes_per_pixel = bmpHeader[0]->bits_per_pixel / 8; int row_padding = (4 - (bmpHeader[0]->width_px * bytes_per_pixel) % 4) % 4; print_pixels(small_pixel, bmpHeader[0]->width_px * bmpHeader[0]->height_px); for (int i = bmpHeader[0]->height_px - 1; i >= 0; i--) { for (int j = 0; j < bmpHeader[0]->width_px; j++) { fwrite(&small_pixel[i * bmpHeader[0]->width_px + j].blue, sizeof(uint8_t), 1, test); fwrite(&small_pixel[i * bmpHeader[0]->width_px + j].green, sizeof(uint8_t), 1, test); fwrite(&small_pixel[i * bmpHeader[0]->width_px + j].red, sizeof(uint8_t), 1, test); } // Write row padding fwrite(&row_padding, 1, row_padding, test); } fclose(test); }
问题排查与修正
1. 读取循环行列颠倒
BMP像素按行存储,每行包含width_px个像素,原代码先遍历宽度再遍历高度,完全搞反了行和列的顺序,导致像素存储混乱。修正后的读取循环:
// 修正read_bmp_pixels中的循环逻辑 int index_p = 0; // 不要硬编码54,用header的data_offset字段定位像素起始位置,适配不同BMP格式 fseek(stream, header->data_offset, SEEK_SET); int bytes_per_pixel = header->bits_per_pixel / 8; int padding = (4 - (header->width_px * bytes_per_pixel) % 4) % 4; for (int i = 0; i < header->height_px; i++) { // 先遍历每一行(高度方向) for (int j = 0; j < header->width_px; j++) { // 再遍历该行的每个像素(宽度方向) fread(&(pixels[index_p].blue), 1, 1, stream); fread(&(pixels[index_p].green), 1, 1, stream); fread(&(pixels[index_p].red), 1, 1, stream); index_p++; } fseek(stream, padding, SEEK_CUR); // 跳过当前行的padding }
2. 写入padding的错误
原代码直接写入row_padding变量的内存值,而非填充对应数量的0字节。比如padding为1时,写入的是int类型的0x01000000,而非单个0x01字节。修正方式:
// 替换原padding写入代码 uint8_t pad_byte = 0; for (int p = 0; p < row_padding; p++) { fwrite(&pad_byte, 1, 1, test); }
3. 冗余的文件指针操作
写入BMP头时,fseek(test, 0, SEEK_SET)、rewind(test)、fseek(test, 54, SEEK_SET)都是冗余操作,fwrite后文件指针会自动移动到写入的末尾,直接删除这些语句即可。
4. 索引计算与读取顺序匹配
修正读取循环后,写入时的索引计算i * width + j就能正确对应存储的像素顺序,无需额外调整行顺序(如果读取是按BMP从下到上的存储顺序,写入时倒序行是正确的,前提是读取行列顺序正确)。
5. 避免硬编码偏移量
不要直接用fseek(stream, 54, SEEK_SET),BMP头长度不一定是54字节(比如带调色板的BMP),应该用header->data_offset字段定位到像素数据的起始位置。
内容的提问来源于stack exchange,提问作者Aaron Lee
相关产品推荐
相关产品推荐

