实现位图的读取显示保存,能完成24位位图的转换,是一个典型的图像处理基础程序,程序很好,很简单清晰,容易看懂,但是所有图像信息头和数据的处理全在响应函数和ondrow函数里!
将24位真彩色图转换为灰度图
在图像处理中,我们经常需要将真彩色图像转换为黑白图像。严格的讲应该是灰度图,因为真正的黑白图像是二色,即只有纯黑,纯白二色。开始之前,我们先简单补充一下计算机中图像的表示原理。
计算机中的图像大致可以分成两类:位图(Bitmap)和矢量图(Metafile)。 位图可以视为一个二维的网格,整个图像就是由很多个点组成的,点的个数等于位图的宽乘以高。每个点被称为一个像素点,每个像素点有确定的颜色,当很多个像素合在一起时就形成了一幅完整的图像。
我们通常使用的图像大部分都是位图,如数码相机拍摄的照片,都是位图。因为位图可以完美的表示图像的细节,能较好的 还原图像的原景。
但位图也有缺点:第一是体积比较大,所以人们开发了很多压缩图像格式来储存位图图像,目前应用最广的是JPEG格式,在WEB上得到了广泛应用,另外还有GIF,PNG等 等。第二是位图在放大时,不可避免的会出现“锯齿”现象,这也由位图的本质特点决定的。
所以在现实中,我们还需要使用到另一种图像格式:矢量图。同位图不 同,矢量图同位图的原理不同,矢量图是利用数学公式通过圆,线段等绘制出来的,所以不管如何放大都不会出现变形,但矢量图不能描述非常复杂的图像。所以矢量图都是用来描述图形图案,各种CAD软件等等都是使用矢量格式来保存文件的。
24位真色位图转化为8位灰度位图
(C语言实现)
//最近在学习图像处理有关的基础知识,并试着编程实践,这样有助于自己的理解和学习。首先遇到的第一个问题就是bmp位图的基础知识、bmp位图的C语言读入、操作、存入等。以下便是有关这期间学习的知识经验总结:欢迎分享!欢迎建议!谢谢!
//要理解的基础问题:bmp位图文件格式(掌握了bmp位图文件的具体格式后,才有可能更好的对其进行编程操作实践!):
位图文件(bitmap file)保存顺序如下:
位图头文件(BITMAPFILEHEADER)
位图信息头文件(BITMAPINFOHEADER)
调色板RGBQUAD(真彩色位图没有调色板)
图像数据
##释义1##位图头文件(BITMAPFILEHEADER):
typedef struct tagBITMAPFILEHEADER
{ /*14 bytes BMP文件头数据结构含有BMP文件的类型、
文件大小和位图起始位置等信息*/
WORD bfType; /*2,位图文件的类型,必须为BM(0-1字节)*/
DWORD bfSize; /*4,位图文件的大小,以字节为单位(2-5字节)*/
WORD bfReserved1; /*2,位图文件保留字,必须为0(6-7字节)*/
WORD bfReserved2; /*2,位图文件保留字,必须为0(6-7字节)*/
DWORD bfOffBits; /*4,位图数据的起始位置,以相对于位图(10-13字节) */
}BITMAPFILEHEADER,*PBITMAPFILEHEADER;
##释义2##位图信息头文件(BITMAPINFOHEADER):
typedef struct tagBITMAPINFOHEADER
{ /*40 bytes BMP位图信息头数据用于说明位图的尺寸等信息。*/
DWORD biSize; /*4,本结构所占用字节数(14-17字节) */
LONG biWidth; /*4,位图的宽度,以像素为单位(18-21字节)*/
LONG biHeight; /*4,位图的高度,以像素为单位(22-25字节) */
WORD biPlanes; /*2,目标设备的级别,必须为1(26-27字节) */
WORD biBitCount;/*2每个像素所需的位数,必须是1(双色),(28-29字节)
4(16色),8(256色)或24(真彩色)之一 */
DWORD biCompression;/*4,位图压缩类型,必须是 0(不压缩),(30-33字节)
1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 */
DWORD biSizeImage; /*4,位图的大小,以字节为单位(34-37字节) */
LONG biXPelsPerMeter;/*4,位图水平分辨率,每米像素数(38-41字节) */
LONG biYPelsPerMeter; /*4,位图垂直分辨率,每米像素数(42-45字节) */
DWORD biClrUsed;/*4, 位图实际使用的颜色表中的颜色数(46-49字节) */
DWORD biClrImportant;/*4,位图显示过程中重要的颜色数(50-53字节) */
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
##释义3##调色板RGBQUAD(真彩色位图没有调色板):
typedef struct tagRGBQUAD
{ /*颜色表用于说明位图中的颜色,它有若干个表项
,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。*/
BYTE rgbBlue; /*蓝色的亮度(值范围为0-255)*/
BYTE rgbGreen; /*绿色的亮度(值范围为0-255) */
BYTE rgbRed;/*红色的亮度(值范围为0-255)*/
BYTE rgbReserved; /*保留,必须为0 */
}RGBQUAD;
##释义4##图像数据:
对于用到调色板的位图,图像数据就是该像素颜色在调色板中的索引值。对于真彩色图像,图像数据就是实际的R、G、B值,一个像素由3个字节24位组成,第一个字节表示B,第二个字节表示G,第三个字节表示R。
注意:BMP文件是从下到上、从左都右排列的。读文件时,最先读到的是图像的最下面一行的左边的第一个像素,最后读到的是最上面一行的最右一个像素。
//C源程序:本C程序由两部分组成(.c文件和.h文件)。(注:VC6编译链接运行通过)
*********************************************************************************************
rgbtogray.h文件:
*****************
/* C语言读入图像 位图文件结构声明 */
#ifndef BMP_H_INCLUDED
#define BMP_H_INCLUDED
typedef unsigned short WORD;//2*8=16
typedef unsigned long DWORD;//4*8=32
typedef long LONG;//4*8=32
typedef unsigned char BYTE;//1*8=8
#pragma pack(1)
typedef struct tagBITMAPFILEHEADER
{ /*14 bytes BMP文件头数据结构含有BMP文件的类型、
文件大小和位图起始位置等信息*/
WORD bfType; /*2,位图文件的类型,必须为BM(0-1字节)*/
DWORD bfSize; /*4,位图文件的大小,以字节为单位(2-5字节)*/
WORD bfReserved1; /*2,位图文件保留字,必须为0(6-7字节)*/
WORD bfReserved2; /*2,位图文件保留字,必须为0(6-7字节)*/
DWORD bfOffBits; /*4,位图数据的起始位置,以相对于位图(10-13字节) */
}BITMAPFILEHEADER,*PBITMAPFILEHEADER;
#pragma pack()
#pragma pack(1)
typedef struct tagBITMAPINFOHEADER
{ /*40 bytes BMP位图信息头数据用于说明位图的尺寸等信息。*/
DWORD biSize; /*4,本结构所占用字节数(14-17字节) */
LONG biWidth; /*4,位图的宽度,以像素为单位(18-21字节)*/
LONG biHeight; /*4,位图的高度,以像素为单位(22-25字节) */
WORD biPlanes; /*2,目标设备的级别,必须为1(26-27字节) */
WORD biBitCount;/*2每个像素所需的位数,必须是1(双色),(28-29字节)
4(16色),8(256色)或24(真彩色)之一 */
DWORD biCompression;/*4,位图压缩类型,必须是 0(不压缩),(30-33字节)
1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 */
DWORD biSizeImage; /*4,位图的大小,以字节为单位(34-37字节) */
LONG biXPelsPerMeter;/*4,位图水平分辨率,每米像素数(38-41字节) */
LONG biYPelsPerMeter; /*4,位图垂直分辨率,每米像素数(42-45字节) */
DWORD biClrUsed;/*4, 位图实际使用的颜色表中的颜色数(46-49字节) */
DWORD biClrImportant;/*4,位图显示过程中重要的颜色数(50-53字节) */
} BITMAPINFOHEADER,*PBITMAPINFOHEADER;
#pragma pack()
#pragma pack(1)
typedef struct tagRGBQUAD
{ /*颜色表用于说明位图中的颜色,它有若干个表项
,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。*/
BYTE rgbBlue; /*蓝色的亮度(值范围为0-255)*/
BYTE rgbGreen; /*绿色的亮度(值范围为0-255) */
BYTE rgbRed;/*红色的亮度(值范围为0-255)*/
BYTE rgbReserved; /*保留,必须为0 */
}RGBQUAD;/*颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。*/
#pragma pack()
#pragma pack(1)
typedef struct tagBITMAPIMAGE
{ /*位图信息头和颜色表组成位图信息*/
BITMAPFILEHEADER bmiHeader; /*位图信息头*/
RGBQUAD bmiColors[1]; /*颜色表*/
}BITMAPIMAGE;
#pragma pack()
#endif
*********************************************************************************************
rgbtogray.h文件:
******************
/*******************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
/********************************************************************/
//自定义的有关图像处理的头文件为rgbtogray.h
//系统定义有关图像处理的头文件为windows.h
//目前,引用系统的windows.h程序工作正常,但自己的头文件有问题,待改正!
//最终实现引用自己的头文件rgbtogray.h
//太棒了:头文件的问题终于解决了
//#include<windows.h>
#include "rgbtogray.h"
/********************************************************************/
/*****************************************************************************/
//全局变量声明
FILE * fpSrcBmpfile;//定义文件指针,用于读入和存储图像文件
FILE * fpDestBmpfile;
/******************************************************************************/
//程序子函数声明
void GetBmpHeader(PBITMAPFILEHEADER, PBITMAPINFOHEADER);//获得位图的文件头和信息头
void ChangeBmpHeader(PBITMAPFILEHEADER, PBITMAPINFOHEADER, WORD);//改变位图的文件头和信息头
void SetBmpHeader(const PBITMAPFILEHEADER, const PBITMAPINFOHEADER);//将修改后的文件头和信息头存入新位图文件
void SetRGBQUAD();//建立颜色板并存入灰度图文件中
int RgbToGray();//24位真色图转化为8位灰度图的主要函数
/******************************************************************************/
//主函数框架,菜单!(方便调用其他程序,以便拓展)
void main()
{
int command=1;
while(command)//菜单选项,可拓展
{
printf("\n*****************************Image Processing Menu***************************\n");
printf("|--------1:RGB To GRAY transform!--------|\n");
printf("|--------0:End!---------|\n");
printf("*****************************************************************************\n");
printf("Hint:Processor Number?\n");
scanf("%d",&command);
switch(command)
{
case 1:
RgbToGray();
break;
case 0:
printf("---Bye-bye---\n");
break;
default:
printf("---Not defined number!\n");
break;
}
}
}
/******************************************************************************/
//函数体部分(副)
void GetBmpHeader(PBITMAPFILEHEADER pbfheader, PBITMAPINFOHEADER pbiheader)
{
fread(pbfheader, sizeof(BITMAPFILEHEADER), 1,fpSrcBmpfile);
fread(pbiheader, sizeof(BITMAPINFOHEADER), 1,fpSrcBmpfile);
}
void ChangeBmpHeader(PBITMAPFILEHEADER pbfheader, PBITMAPINFOHEADER pbiheader, WORD wType)
{
pbiheader->biBitCount = wType; // 24 或者 8
pbiheader->biClrUsed = (wType == 24) ? 0 : 256;
pbfheader->bfOffBits = 54 + pbiheader->biClrUsed * sizeof(RGBQUAD);
pbiheader->biSizeImage = ((((pbiheader->biWidth * pbiheader->biBitCount) + 31) & ~31) / 8) * pbiheader->biHeight;
pbfheader->bfSize = pbfheader->bfOffBits + pbiheader->biSizeImage;
}
void SetBmpHeader(const PBITMAPFILEHEADER pbfheader, const PBITMAPINFOHEADER pbiheader)
{
fwrite(pbfheader, sizeof(BITMAPFILEHEADER), 1, fpDestBmpfile);
fwrite(pbiheader, sizeof(BITMAPINFOHEADER), 1, fpDestBmpfile);
}
void SetRGBQUAD()
{
int i;
RGBQUAD rgbquad[256];
for(i=0;i<256;i++) {
rgbquad[i].rgbBlue = i;
rgbquad[i].rgbGreen = i;
rgbquad[i].rgbRed = i;
rgbquad[i].rgbReserved = 0;
}
fwrite(rgbquad, 256 * sizeof(RGBQUAD), 1, fpDestBmpfile);
}
/******************************************************************************/
//函数体部分(主)
int RgbToGray()
{
LONG w,h;
BYTE r,g,b;
BYTE gray;
BYTE count24,count8;
BYTE Bmpnul=0;
char SrcBmpfile[256];
char DestBmpfile[256];
BITMAPFILEHEADER bmfh; // bmp文件头
BITMAPINFOHEADER bmih; // 位图信息头
BYTE *data;
memset(&bmfh, 0, sizeof(BITMAPFILEHEADER));//内存初始化
memset(&bmih, 0, sizeof(BITMAPINFOHEADER));
data=(BYTE *)malloc(3*sizeof(BYTE));
if(!data)
{
printf("Error:Can not allocate memory .\n");
free(data);
return -1;
}
getchar();
printf("Input the path of SrcBmpfile:\n");
gets(SrcBmpfile);
if((fpSrcBmpfile=fopen(SrcBmpfile,"rb"))==NULL)
{
printf("Error:Open the file of SrcBmpfile failed!\n");//输入源位图文件
free(data);
return -1;
}
rewind(fpSrcBmpfile);
GetBmpHeader(&bmfh,&bmih);
//ceshie_start
printf("The contents in the file header of the BMP file:\n");
printf("bfType:%ld\n",bmfh.bfType);
printf("bfSize:%ld\n",bmfh.bfSize);
printf("bfReserved1:%ld\n",bmfh.bfReserved1);
printf("bfReserved2:%ld\n",bmfh.bfReserved2);
printf("bfOffBits:%ld\n",bmfh.bfOffBits);
printf("The contents in the info header:\n");
printf("biSize:%ld\n",bmih.biSize);
//ceshi_end
if(bmfh.bfType!=0x4D42)
{
printf("Error:This file is not bitmap file!\n");
free(data);
return -1;
}
if(bmih.biBitCount!=24)
{
printf("Error:This bmpfile is not 24bit bitmap!\n");
free(data);
return -1;
}
if(bmih.biCompression!=0)
{
printf("Error:This 8bit bitmap file is not BI_RGB type!\n");
free(data);
return -1;
}
printf("Input the path of the DestBmpfile:\n");//输入目标位图文件
gets(DestBmpfile);
if((fpDestBmpfile=fopen(DestBmpfile,"wb"))==NULL)
{
printf("Error:Open the file of DestBmpfile failed!\n");
free(data);
return -1;
}
ChangeBmpHeader(&bmfh,&bmih,8);
SetBmpHeader(&bmfh,&bmih);
SetRGBQUAD();
count24=(4-(bmih.biWidth*3)%4)%4;
count8=(4-(bmih.biWidth)%4)%4;
for(h=bmih.biHeight-1;h>=0;h--)
{
for(w=0;w<bmih.biWidth;w++)
{
fread(data,3,1,fpSrcBmpfile);
if(feof(fpSrcBmpfile))
{
printf("Error:Read Pixel data failed!\n");
free(data);
return -1;
}
b=*data;
g=*(data+1);
r=*(data+2);
gray=(299*r+587*g+114*b)/1000;
//if(gray>120)gray=250;
fwrite(&gray,sizeof(gray),1,fpDestBmpfile);
}
fseek(fpSrcBmpfile,count24,SEEK_CUR);
fwrite(&Bmpnul,1,count8,fpDestBmpfile);
}
printf("Hint:Convert RGB To GRAY Successfully!\n");
free(data);//释放内存空间
fclose(fpDestBmpfile);//关闭文件指针
fclose(fpSrcBmpfile);
return 0;
}
/******************************************************************************/
*******************************************************************************************
****当时困扰了本人很长时间的一个程序问题,最后终于解决:
#prama pack(n)
结构体定义;
#prama pack()
##释义##:关于struct的使用方法:
struct是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如array、struct、union等)的数据单元。对于结构体,编译器会自动进行成员变量的对齐,以提高运算效率。缺省情况下,编译器为结构体的每个成员按其自然对界(natural alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
自然对界是指按结构体的成员中size最大的成员对齐。
#pragma pack规定的对齐长度,实际使用的规则是:
结构,联合,或者类的数据成员,第一个放在偏移为0的地方,以后每个数据成员的对齐,按照#pragma pack指定的数值和结构体的自然对齐长度中比较小的那个进行。
也就是说,当#pragma pack的值等于或超过所有数据成员长度的时候,这个值的大小将不产生任何效果。
结构体的对齐,按照结构体中size最大的数据成员和#pragma pack指定值之间,较小的那个进行。






















大小: 8KB
大小: 89.5M
终端仿真器 SecureCRTv7.1.1.264 最新版
串口调试小助手1.3 免费版
WinHex 十六进制编辑器v20.2 SR-5 绿色中文注册版
小旋风ASP服务器安装版
16进制转换工具V1.0 中文绿色版
Adobe AIRV33.1.1.932 官方最新版
github离线安装包64位版V2.9.11官方最新版(github desktop)
mysql数据库.net开发驱动(mysql connector net )8.0.11 官方最新版
MSDN Library Visual Studio 6.0(VC、VB、VF、VJ)中文版win32开发人员必备
版本控制软件(SourceTree)v3.4.6 官方最新版
Memory Analyzer (MAT)V1.01 免费绿色版
Delphi Distillerv1.85绿色版
IBM内存检测工具(IBM Thread and Monitor Dump Analyzer for Java)V4.3.3 绿色版
.NETv3.0 可再发行组件包
一键安装JSP环境安装版
slave4j(基于eclipse插件的java代码生成器)V1.0.0 正式版
Auto DebugProfessional 5.6.5.18 中文绿色版
Understand For C++V1.4.319英文安装版