You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

CS50技术问询:如何修改BMP的biHeight实现图像180度旋转?

实现BMP图像180度旋转的修改方案

要让BMP图像旋转180度,仅修改biHeight的符号是不够的,还需要逆序读取原图像的扫描行,同时将每行内的像素逆序写入。以下是针对你提供的CS50 BMP复制代码的具体修改步骤:

核心修改点

  • 调整BITMAPINFOHEADER中的biHeight:将其设置为原数值的相反数,告知BMP解码器图像存储方向反转
  • 逆序读取原图像扫描行:从最后一行开始读取,直到第一行
  • 每行像素逆序写入:读取一行像素后,从最后一个像素开始写入输出文件

修改后的完整代码

// Copies a BMP file and rotates it 180 degrees
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include "bmp.h"

int main(int argc, char *argv[])
{
    // Ensure proper usage
    if (argc != 3)
    {
        printf("Usage: rotate infile outfile\n");
        return 1;
    }

    // Remember filenames
    char *infile = argv[1];
    char *outfile = argv[2];

    // Open input file
    FILE *inptr = fopen(infile, "r");
    if (inptr == NULL)
    {
        printf("Could not open %s.\n", infile);
        return 2;
    }

    // Open output file
    FILE *outptr = fopen(outfile, "w");
    if (outptr == NULL)
    {
        fclose(inptr);
        printf("Could not create %s.\n", outfile);
        return 3;
    }

    // Read infile's BITMAPFILEHEADER
    BITMAPFILEHEADER bf;
    fread(&bf, sizeof(BITMAPFILEHEADER), 1, inptr);

    // Read infile's BITMAPINFOHEADER
    BITMAPINFOHEADER bi;
    fread(&bi, sizeof(BITMAPINFOHEADER), 1, inptr);

    // Ensure infile is (likely) a 24-bit uncompressed BMP 4.0
    if (bf.bfType != 0x4d42 || bf.bfOffBits != 54 || bi.biSize != 40 ||
        bi.biBitCount != 24 || bi.biCompression != 0)
    {
        fclose(outptr);
        fclose(inptr);
        printf("Unsupported file format.\n");
        return 4;
    }

    // 计算图像实际高度(兼容biHeight为负的情况)
    int actual_height = abs(bi.biHeight);
    // 修改biHeight符号,标记图像存储方向反转
    bi.biHeight = -bi.biHeight;

    // Write outfile's BITMAPFILEHEADER
    fwrite(&bf, sizeof(BITMAPFILEHEADER), 1, outptr);

    // Write outfile's modified BITMAPINFOHEADER
    fwrite(&bi, sizeof(BITMAPINFOHEADER), 1, outptr);

    // Determine padding for scanlines
    int padding = (4 - (bi.biWidth * sizeof(RGBTRIPLE)) % 4) % 4;
    // 计算每行总字节数(像素数据+padding)
    int row_size = bi.biWidth * sizeof(RGBTRIPLE) + padding;

    // 分配内存存储一行像素
    RGBTRIPLE *row = malloc(bi.biWidth * sizeof(RGBTRIPLE));
    if (row == NULL)
    {
        fclose(inptr);
        fclose(outptr);
        printf("Memory allocation failed.\n");
        return 5;
    }

    // 逆序读取每一行:从最后一行到第一行
    for (int i = 0; i < actual_height; i++)
    {
        // 定位到当前要读取的行的起始位置
        long current_row_pos = bf.bfOffBits + (actual_height - 1 - i) * row_size;
        fseek(inptr, current_row_pos, SEEK_SET);

        // 读取当前行的所有像素(跳过padding)
        fread(row, sizeof(RGBTRIPLE), bi.biWidth, inptr);

        // 逆序写入当前行的像素到输出文件
        for (int j = bi.biWidth - 1; j >= 0; j--)
        {
            fwrite(&row[j], sizeof(RGBTRIPLE), 1, outptr);
        }

        // 写入padding到输出文件
        for (int k = 0; k < padding; k++)
        {
            fputc(0x00, outptr);
        }
    }

    // 释放内存
    free(row);

    // Close infile
    fclose(inptr);

    // Close outfile
    fclose(outptr);

    // Success
    return 0;
}

关键修改说明

  1. 调整biHeight:

    • 用abs(bi.biHeight)获取图像实际高度,兼容原图像biHeight为负(表示从上到下存储)的场景
    • 将bi.biHeight设为原数值的相反数,告知解码器输出图像的扫描行存储方向与原图像相反
  2. 逆序读取扫描行:

    • 通过fseek定位到原图像倒数第i+1行的起始位置,计算公式为bf.bfOffBits + (actual_height - 1 - i) * row_size,其中bf.bfOffBits是像素数据的起始偏移量,row_size是每行总字节数
  3. 每行像素逆序写入:

    • 先将整行像素读取到内存缓冲区,再从最后一个像素开始循环写入,实现每行的左右翻转
  4. 内存管理:

    • 用malloc分配内存存储一行像素,减少频繁IO操作,程序结束前释放内存避免泄漏

内容的提问来源于stack exchange,提问作者Alok Chedambath

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 00:30:40