使用C语言读取位图

一、位图文件结构

位图文件由三部分组成:文件头 + 位图信息 + 位图像素数据

1、位图文件头。位图文件头主要用于识别位图文件。以下是位图文件头结构的定义:

typedef struct tagBITMAPFILEHEADER { // bmfh 
    WORD    bfType; 
    DWORD   bfSize; 
    WORD    bfReserved1; 
    WORD    bfReserved2; 
    DWORD   bfOffBits; 
} BITMAPFILEHEADER;
其中的bfType值应该是“BM”(0x4d42),标志该文件是位图文件。bfSize的值是位图文件的大小。
2、位图信息 中所记录的值用于分配内存,设置调色板信息,读取像素值等。
以下是位图信息结构的定义:
typedef struct tagBITMAPINFO {
    BITMAPINFOHEADER    bmiHeader;
    RGBQUAD             bmiColors[1];
} BITMAPINFO;
可见位图信息也是由两部分组成的:位图信息头 + 颜色表 

2.1位图信息头。 位图信息头包含了单个像素所用字节数以及描述颜色的格式,此外还包括位图的宽度、高度、目标设备的位平面数、图像的压缩格式。以下是位图信息头结构的定义:
typedef struct tagBITMAPINFOHEADER{ // bmih 
    DWORD  biSize; 
    LONG   biWidth; 
    LONG   biHeight; 
    WORD   biPlanes; 
    WORD   biBitCount 
    DWORD  biCompression; 
    DWORD  biSizeImage; 
    LONG   biXPelsPerMeter; 
    LONG   biYPelsPerMeter; 
    DWORD  biClrUsed; 
    DWORD  biClrImportant; 
} BITMAPINFOHEADER; 
下表是对结构体当中各个成员的说明: 
结构成员说 明
biSize结构BITMAPINFOHEADER的字节数,即sizeof(BITMAPINFOHEADER)*
biWidth以像素为单位的图像宽度*
biHeight以像素为单位的图像长度*
biplanes目标设备的位平面数
biBitCount每个像素的位数*(1)
biCompression图像的压缩格式(这个值几乎总是为0)
biSizeImage以字节为单位的图像数据的大小(对BI_RGB压缩方式而言)
biXPelsPermeter水平方向上的每米的像素个数
biYpelsPerMeter垂直方向上的每米的像素个数
biClrused调色板中实际使用的颜色数(2)
biClrImportant现实位图时必须的颜色数(3)

说明:*是需要加以注意的部分,因为它们是我们在进行位图操作时经常参考的变量
(1)对于每个像素的字节数,分别有一下意义:
0,用在JPEG格式中
1,单色图,调色板中含有两种颜色,也就是我们通常说的黑白图片
4,16色图
8,256色图,通常说的灰度图
16,64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个位表示一个RGB分量
24,16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量
32,4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式

(2)这个值通常为0,表示使用biBitCount确定的全部颜色,例外是使用的颜色树木小于制定的颜色深度的颜色数目的最大值。

(3)这个值通常为0,表示所有的颜色都是必需的

2.2颜色表。颜色表一般是针对16位一下的图像而设置的,对于16位和16位以上的图像,由于其位图像素数据中直接对对应像素的RGB(A)颜色进行描述,因而省却了调色板。而对于16位一下的图像,由于其位图像素数据中记录的只是调色板索引值,因而需要根据这个索引到调色板去取得相应的RGB(A)颜色。颜色表的作用就是创建调色板。


颜色表是由颜色表项组成的,颜色表项结构的定义如下:

typedef struct tagRGBQUAD { // rgbq 
    BYTE    rgbBlue; 
    BYTE    rgbGreen; 
    BYTE    rgbRed; 
    BYTE    rgbReserved; 
} RGBQUAD;
其中需要注意的问题是,RGBQUAD结构中的颜色顺序是BGR,而不是平常的RGB。

3、位图数据。最后,在位图文件头、位图信息头、位图颜色表之后,便是位图的主体部分:位图数据。根据不同的位图,位图数据所占据的字节数也是不同的,比如,对于8位位图,每个字节代表了一个像素,对于16位位图,每两个字节代表了一个像素,对于24位位图,每三个字节代表了一个像素,对于32位位图,每四个字节代表了一个像素。 

下面附上代码:

// Bmp.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"


int _tmain(int argc, _TCHAR* argv[])
{
    int ReadFileHeader(char *filepath,BITMAPFILEHEADER *bmfh);
    int ReadInfoHeader(char *filepaht,BITMAPINFOHEADER *bmih);
    int ReadPixelData(char *filepath);
    DWORD GetLineBytes(int width,int bitCount);
    //BYTE *imgData;
    //int i=ReadPixelData("E:\\1\\1.bmp");
    int i=ReadPixelData("D:\\2.bmp");
    scanf("%d");
    return 0;
}


//读入文件头  
int ReadFileHeader(char *filepath,BITMAPFILEHEADER *bmfh)
{
  FILE *fp;
  //打开文件
  fp=fopen(filepath,"rb");
  if(!fp)
  { //如果打开失败
     printf("Can not open the file:%s\n",filepath);
     return -1;
  }
  //读入bfType
  if(fread(&bmfh->bfType,sizeof(WORD),1,fp)!=1)
  {
  printf("Can not read bfType in the file header.\n");
  fclose(fp);
  return -1;
  }
  //读入bfSize
  if(fread(&bmfh->bfSize,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read bfSize in the file header.\n");
  fclose(fp);
  return -1;
  }
  //读入bfReserved1
  if(fread(&bmfh->bfReserved1,sizeof(WORD),1,fp)!=1)
  {
  printf("Can not read bfReserved1 in the file header.\n");
  fclose(fp);
  return -1;
  }
  //读入bfReserved2
  if(fread(&bmfh->bfReserved2,sizeof(WORD),1,fp)!=1)
  {
  printf("Can not read bfReserved2 in the file header.\n");
  fclose(fp);
  return -1;
  }
  //读入bfOffBits
  if(fread(&bmfh->bfOffBits,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read bfOffBits in the file header.\n");
  fclose(fp);
  return -1;
  }
  //关闭文件指针
  fclose(fp);
  return 0;

}

//读入信息头  
int ReadInfoHeader(char *filepath,BITMAPINFOHEADER *bmih) 
{
  FILE *fp;
  //打开文件
  fp=fopen(filepath,"rb");
  if(!fp)
  {
  printf("Can not open the file:%s\n",filepath);
  return -1;
  }
  //使文件指针跳过文件头(14字节)
  fseek(fp,14,SEEK_SET);
  //读入biSize
  if(fread(&bmih->biSize,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read biSize in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biWidth
  if(fread(&bmih->biWidth,sizeof(LONG),1,fp)!=1)
  {
  printf("Can not read biWidth in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biHeight
  if(fread(&bmih->biHeight,sizeof(LONG),1,fp)!=1)
  {
  printf("Can not read biHeight in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biPlanes
  if(fread(&bmih->biPlanes,sizeof(WORD),1,fp)!=1)
  {
  printf("Can not read biPlanes in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biBitCount
  if(fread(&bmih->biBitCount,sizeof(WORD),1,fp)!=1)
  {
  printf("Can not read biBitCount in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biCompression
  if(fread(&bmih->biCompression,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read biCompression in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biSizeImage
  if(fread(&bmih->biSizeImage,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read biSizeImage in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biXPelsPerMeter
  if(fread(&bmih->biXPelsPerMeter,sizeof(LONG),1,fp)!=1)
  {
  printf("Can not read biXPelsPerMeter in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biYPelsPerMeter
  if(fread(&bmih->biYPelsPerMeter,sizeof(LONG),1,fp)!=1)
  {
  printf("Can not read biYPelsPerMeter in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biClrUsed
  if(fread(&bmih->biClrUsed,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read biClrUsed in the info header.\n");
  fclose(fp);
  return -1;
  }
  //读入biClrImportant
  if(fread(&bmih->biClrImportant,sizeof(DWORD),1,fp)!=1)
  {
  printf("Can not read biClrImportant in the info header.\n");
  fclose(fp);
  return -1;
  }
  //关闭文件
  fclose(fp);
  return 0;
}

//读取像素数据
int ReadPixelData(char *filepath)
{
    BITMAPINFOHEADER bmih;
    BITMAPFILEHEADER bmfh;
    BYTE *imgdata;
    FILE *fp;
    int n;
    int width;
    int height;
    int bitCount;
    long biSizeImage;
    int i;
    int j;
    long a[256]={0};
    int p;
    DWORD dwLineBytes;
    //读入文件头
    n=ReadFileHeader(filepath,&bmfh);
    if(n==-1)
    {
        printf("Can not read the file header of BMP file.\n");
        return -1;
    }
    //读入信息头
    n=ReadInfoHeader(filepath,&bmih);
    if(n==-1)
    {
        printf("Can not read the info header of BMP file.\n");
        return -1;
    }
    //获取信息头有用信息
    width=bmih.biWidth;
    height=bmih.biHeight;
    bitCount=bmih.biBitCount;
    biSizeImage=bmih.biSizeImage;
    dwLineBytes=GetLineBytes(width,bitCount);
    //printf("%ld\n",dwLineBytes);
    printf("位图宽:%ld\n",width);
    printf("位图高:%ld\n",height);
    printf("位图位数:%d\n",bitCount);
    printf("位图所占字节数:%ld\n",biSizeImage);
    fp = fopen(filepath,"rb");
    if(!fp)
    {
        printf("Can not open the file:%s\n",filepath);
        return -1;
    }
    if(bitCount==8||bitCount==24)
    {
        fseek(fp,bmfh.bfOffBits,SEEK_SET);//直接跳到像素数据
    }else
    {
        printf("此位图为%d位!",bitCount);
        printf("只支持8或24位");
        fclose(fp);
        return -1;
    }
    
    //给imgdata分配内存
    imgdata=(BYTE*)malloc(dwLineBytes*height*sizeof(BYTE));
    if(!imgdata)
    {
        printf("Can not allocate memory for the pixel data.\n");
        return -1;
    }
    
    
    //读入像素数据,大小为高度乘上每行所占字节数
    n=fread(imgdata,dwLineBytes*height*sizeof(BYTE),1,fp);
    if(n==0)
    {
        if(ferror(fp))
        {
            printf("Can not read the pixel data.\n");
            free(imgdata);
            fclose(fp);
            return -1;
        }
    }
    printf("图片像素值首地址:%ld\n",imgdata);
    
    //读取位图像素值
    if(bitCount==8)
    {
        for(i=0;i<height;i++)
        {
            for(j=0;j<width;j++)
            {
            //读取灰度值
            p=*(imgdata+dwLineBytes*(height-1-i)+j);
            a[p]++;
            //printf("%d,",p);
            }
        }
        printf("\n");
    }else if(bitCount==24)
    {
        for(i=0;i<height;i++)
        {
            for(j=0;j<width*3;j++)
            {
                printf("(");
                //读取蓝色分量
                p=*(imgdata+dwLineBytes*(height-1-i)+j);
                printf("%d,",p);
                j++;
                //读取绿色分量
                p=*(imgdata+dwLineBytes*(height-1-i)+j);
                printf("%d,",p);
                j++;
                //读取红色分量
                p=*(imgdata+dwLineBytes*(height-1-i)+j);
                printf("%d,",p);
                printf(")");
            }
            printf("\n");
        }
    }else
    {
        printf("只支持8或24位");
    }
    free(imgdata);
    fclose(fp);
    printf("统计结果如下:\n");
    for(i=0;i<256;i++)
    {
        printf("位图中%3d有%ld个\n",i,a[i]);
    }
    
    return 0;
}


DWORD GetLineBytes(int width,int bitCount) 
{
    //首先bitCount是指位图的每个像素所占的二进制位数,不是字节数;
    //其次,windows中位图每行所占的字节数必须为4的倍数,
    //例如灰度图中每行200个像素占用200个字节,如果每行有201个像素就占用204个字节。
    //return ((width*bitCount/8+3)/4)*4;
    return (width*bitCount+31)/32*4;
}

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
位图直方图均衡是一种常见的图像处理方法,用于增强图像对比度和亮度。以下是C语言实现位图直方图均衡的基本步骤: 1. 读取位图文件头和位图信息头信息。 2. 读取位图像素数据,并将每个像素的灰度值记录在一个数组中。 3. 统计灰度值出现的次数,生成灰度值和出现次数的对应表。 4. 计算每个灰度值在图像中的出现概率,并生成累积概率表。 5. 根据累积概率表,将每个灰度值映射到新的灰度值上。 6. 重新生成像素数据,并将新的像素数据写入位图文件中。 下面是一个简单的C语言程序示例,用于实现位图直方图均衡: ```c #include <stdio.h> #include <stdlib.h> #pragma pack(push, 1) typedef struct { unsigned short signature; // 文件类型,"BM"表示Windows位图格式 unsigned int fileSize; // 文件大小 unsigned short reserved1; // 保留字段1 unsigned short reserved2; // 保留字段2 unsigned int dataOffset; // 数据偏移量 } BMPFileHeader; typedef struct { unsigned int headerSize; // 信息头大小 int width; // 图像宽度 int height; // 图像高度 unsigned short colorPlanes; // 颜色平面数量,固定值1 unsigned short bitsPerPixel; // 每个像素使用的位数 unsigned int compression; // 压缩类型 unsigned int imageSize; // 图像数据大小 int horizontalResolution; // 水平分辨率 int verticalResolution; // 垂直分辨率 unsigned int colorsUsed; // 调色板中的颜色数量 unsigned int importantColors; // 重要的颜色数量 } BMPInfoHeader; typedef struct { unsigned char blue; // 蓝色分量 unsigned char green; // 绿色分量 unsigned char red; // 红色分量 } RGB; #pragma pack(pop) void equalizeHistogram(unsigned char *imageData, int width, int height) { int histogram[256] = {0}; int cumulativeHistogram[256] = {0}; int i, j; unsigned char pixelValue; float probability; // 统计灰度值出现的次数 for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { pixelValue = imageData[i * width + j]; histogram[pixelValue]++; } } // 计算每个灰度值在图像中的出现概率 for (i = 0; i < 256; i++) { probability = (float)histogram[i] / (float)(width * height); cumulativeHistogram[i] = (int)(probability * 255.0f + 0.5f); } // 将每个灰度值映射到新的灰度值上 for (i = 0; i < height; i++) { for (j = 0; j < width; j++) { pixelValue = imageData[i * width + j]; imageData[i * width + j] = cumulativeHistogram[pixelValue]; } } } int main(int argc, char *argv[]) { BMPFileHeader fileHeader; BMPInfoHeader infoHeader; FILE *inputFile, *outputFile; unsigned char *imageData; int width, height, padding, imageSize; // 打开输入文件 inputFile = fopen(argv[1], "rb"); if (inputFile == NULL) { printf("Error: Unable to open input file.\n"); return 1; } // 读取文件头和信息头信息 fread(&fileHeader, sizeof(BMPFileHeader), 1, inputFile); fread(&infoHeader, sizeof(BMPInfoHeader), 1, inputFile); // 获取图像宽度、高度、填充字节数和图像数据大小 width = infoHeader.width; height = infoHeader.height; padding = (4 - ((width * infoHeader.bitsPerPixel / 8) % 4)) % 4; imageSize = (width * infoHeader.bitsPerPixel / 8 + padding) * height; // 分配内存并读取图像数据 imageData = (unsigned char *)malloc(imageSize); fread(imageData, sizeof(unsigned char), imageSize, inputFile); // 关闭输入文件 fclose(inputFile); // 对图像进行直方图均衡 equalizeHistogram(imageData, width, height); // 打开输出文件 outputFile = fopen(argv[2], "wb"); if (outputFile == NULL) { printf("Error: Unable to open output file.\n"); free(imageData); return 1; } // 写入文件头和信息头信息 fwrite(&fileHeader, sizeof(BMPFileHeader), 1, outputFile); fwrite(&infoHeader, sizeof(BMPInfoHeader), 1, outputFile); // 写入图像数据 fwrite(imageData, sizeof(unsigned char), imageSize, outputFile); // 关闭输出文件 fclose(outputFile); // 释放内存 free(imageData); return 0; } ``` 该程序可以通过命令行参数指定输入文件和输出文件的路径,例如: ``` ./histogram_equalization input.bmp output.bmp ``` 注意,该程序只支持Windows位图格式(BMP)的文件。如果需要处理其他格式的图像文件,需要根据不同的文件格式进行解析和写入。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值