首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言入門

Linux 下 C 語言 BMP 圖片操作編程:文件格式解析、像素級讀寫、旋轉變換實戰

     編輯:C 語言入門

BMP 文件格式核心結構

BMP(Bitmap)是 Windows 標準圖像格式,結構簡單、無壓縮,適合教學與嵌入式裸機顯示。

文件頭(14 字節)

typedef struct {
    uint16_t bfType;      // 0x4D42 ('BM')
    uint32_t bfSize;      // 文件總大小
    uint16_t bfReserved1; // 0
    uint16_t bfReserved2; // 0
    uint32_t bfOffBits;   // 像素數據偏移量(通常 54)
} __attribute__((packed)) BMPFileHeader;

信息頭(BITMAPINFOHEADER,40 字節)

typedef struct {
    uint32_t biSize;          // 40
    int32_t  biWidth;         // 寬度(像素),負值表示自下而上
    int32_t  biHeight;        // 高度,正值自下而上,負值自上而下
    uint16_t biPlanes;        // 1
    uint16_t biBitCount;      // 色深:1/4/8/16/24/32
    uint32_t biCompression;   // 0=BI_RGB 無壓縮
    uint32_t biSizeImage;     // 像素數據大小(可 0)
    int32_t  biXPelsPerMeter; // 水平分辨率
    int32_t  biYPelsPerMeter; // 垂直分辨率
    uint32_t biClrUsed;       // 調色板顏色數
    uint32_t biClrImportant;  // 重要顏色數
} __attribute__((packed)) BMPInfoHeader;

像素數據佈局

  • 掃描行順序:默認自下而上(biHeight > 0),即文件中第一行是圖片最底行。
  • 行對齊:每行字節數 = `((width * bitCount + 31) / 32) * 4`(4 字節對齊)。
  • 24 位色序:BGR 順序(藍綠紅),每像素 3 字節。
  • 32 位:BGRA(含保留/Alpha)。

關鍵計算公式

// 每行字節數(含填充)
int row_stride = ((width * bit_count + 31) / 32) * 4;

// 像素數據總大小
int image_size = row_stride * abs(height);

// 訪問像素 (x, y) —— y=0 為頂行(邏輯座標)
// 文件中實際行索引 = (height > 0) ? (height - 1 - y) : y
uint8_t *pixel_ptr = data + row_index * row_stride + x * (bit_count / 8);

實戰:讀取 BMP、順時針 90° 旋轉、寫入新文件

#include 
#include 
#include 
#include 
#include 
#include 

#pragma pack(push, 1)
typedef struct {
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1, bfReserved2;
    uint32_t bfOffBits;
} BMPFileHeader;

typedef struct {
    uint32_t biSize;
    int32_t  biWidth, biHeight;
    uint16_t biPlanes, biBitCount;
    uint32_t biCompression, biSizeImage;
    int32_t  biXPelsPerMeter, biYPelsPerMeter;
    uint32_t biClrUsed, biClrImportant;
} BMPInfoHeader;
#pragma pack(pop)

// 讀取整個 BMP 到內存
int read_bmp(const char *path, BMPFileHeader *fh, BMPInfoHeader *ih, uint8_t **pixels) {
    int fd = open(path, O_RDONLY);
    if (fd < 0) { perror("open"); return -1; }
    
    if (read(fd, fh, sizeof(*fh)) != sizeof(*fh)) { close(fd); return -2; }
    if (fh->bfType != 0x4D42) { close(fd); return -3; } // Not BM
    
    if (read(fd, ih, sizeof(*ih)) != sizeof(*ih)) { close(fd); return -4; }
    if (ih->biBitCount != 24) { close(fd); return -5; } // Only 24-bit
    
    int row_stride = ((ih->biWidth * 24 + 31) / 32) * 4;
    int img_size = row_stride * abs(ih->biHeight);
    
    *pixels = malloc(img_size);
    if (!*pixels) { close(fd); return -6; }
    
    lseek(fd, fh->bfOffBits, SEEK_SET);
    if (read(fd, *pixels, img_size) != img_size) { free(*pixels); close(fd); return -7; }
    
    close(fd);
    return 0;
}

// 寫入 BMP
int write_bmp(const char *path, BMPFileHeader *fh, BMPInfoHeader *ih, uint8_t *pixels) {
    int fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
    if (fd < 0) { perror("open"); return -1; }
    
    int row_stride = ((ih->biWidth * 24 + 31) / 32) * 4;
    int img_size = row_stride * abs(ih->biHeight);
    
    fh->bfSize = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader) + img_size;
    fh->bfOffBits = sizeof(BMPFileHeader) + sizeof(BMPInfoHeader);
    
    write(fd, fh, sizeof(*fh));
    write(fd, ih, sizeof(*ih));
    write(fd, pixels, img_size);
    
    close(fd);
    return 0;
}

// 順時針 90° 旋轉:新寬=原高,新高=原寬
uint8_t *rotate_90_cw(BMPInfoHeader *ih, uint8_t *src) {
    int w = ih->biWidth, h = abs(ih->biHeight);
    int src_stride = ((w * 24 + 31) / 32) * 4;
    int dst_w = h, dst_h = w;
    int dst_stride = ((dst_w * 24 + 31) / 32) * 4;
    
    uint8_t *dst = calloc(dst_stride * dst_h, 1); // 零初始化含填充
    
    for (int y = 0; y < h; ++y) {
        for (int x = 0; x < w; ++x) {
            // 源像素指針(邏輯座標 y 從頂部開始)
            int src_row = (ih->biHeight > 0) ? (h - 1 - y) : y;
            uint8_t *sp = src + src_row * src_stride + x * 3;
            
            // 目標像素:旋轉後 (x, y) → (y, w-1-x)
            int dst_x = y;
            int dst_y = w - 1 - x;
            int dst_row = (ih->biHeight > 0) ? (dst_h - 1 - dst_y) : dst_y; // 保持存儲順序
            uint8_t *dp = dst + dst_row * dst_stride + dst_x * 3;
            
            // 複製 BGR
            dp[0] = sp[0]; dp[1] = sp[1]; dp[2] = sp[2];
        }
    }
    
    // 更新信息頭
    ih->biWidth = dst_w;
    ih->biHeight = (ih->biHeight > 0) ? dst_h : -dst_h; // 保持符號
    ih->biSizeImage = dst_stride * dst_h;
    
    return dst;
}

int main(int argc, char **argv) {
    if (argc != 3) {
        fprintf(stderr, "Usage: %s input.bmp output.bmp\n", argv[0]);
        return 1;
    }
    
    BMPFileHeader fh;
    BMPInfoHeader ih;
    uint8_t *pixels = NULL;
    
    if (read_bmp(argv[1], &fh, &ih, &pixels) != 0) {
        fprintf(stderr, "Read BMP failed\n");
        return 2;
    }
    
    printf("Original: %dx%d, %d-bit, stride=%d\n", 
           ih.biWidth, ih.biHeight, ih.biBitCount, 
           ((ih.biWidth * 24 + 31) / 32) * 4);
    
    uint8_t *rotated = rotate_90_cw(&ih, pixels);
    free(pixels);
    
    if (write_bmp(argv[2], &fh, &ih, rotated) != 0) {
        fprintf(stderr, "Write BMP failed\n");
        free(rotated);
        return 3;
    }
    
    free(rotated);
    printf("Rotated saved to %s\n", argv[2]);
    return 0;
}

編譯與運行

gcc bmp_rotate.c -o bmp_rotate -std=c99
./bmp_rotate input.bmp output_rotated.bmp

進階擴展方向

功能關鍵點
任意角度旋轉仿射變換 + 雙線性插值,需浮點計算目標像素反向映射源座標
縮放最近鄰/雙線性/雙三次插值,獨立處理 X/Y 縮放比
格式轉換24↔32 位、BGR↔RGB、灰度化(Y=0.299R+0.587G+0.114B)
調色板處理8 位及以下讀取調色板(bfOffBits 後),索引轉 RGB
RLE 壓縮解碼 BI_RLE8/4,編碼輸出
大圖流式處理分塊讀寫,避免全圖載入內存,mmap 加速

避坑指南

  1. 結構體對齊:必須 `#pragma pack(1)` 或 `__attribute__((packed))`,否則 `sizeof` 含填充導致讀寫錯位。
  2. 行填充:讀寫像素時按 `row_stride` 步進,不能假設 `width*3`。
  3. 座標系:BMP 存儲自下而上,邏輯處理常用自上而下,轉換公式 `row = (h>0)?(h-1-y):y`。
  4. 色序:24 位是 BGR,非 RGB,顯示/處理時注意通道順序。
  5. 負高度:biHeight < 0 表示自上而下(DIB 頂下格式),旋轉/翻轉邏輯需保持符號一致性。

總結

BMP 格式簡單透明,是學習二進制文件解析、像素級圖像處理、內存佈局控制的最佳切入點。掌握文件頭/信息頭結構、行對齊、座標系轉換、BGR 色序,即可實現讀寫、旋轉、鏡像、裁剪、濾波等基礎操作。進階可對接 libpng/libjpeg/stb_image 處理壓縮格式,或用 OpenCV 做計算機視覺。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved