图像处理基本方法-c语言生成纯色BMP文件

图像处理基本方法-C语言生成纯色BMP文件

1、目的

我们有时需要产生各种各样的纯色图片,于是设计了这个简单的基本的图像处理小程序。

本文主要实现基本的BMP图片数据的生成过程,并保存为Windows可以识别的BMP文件。

本文是由C语言完成该功能,因为本人很久以来主要使用C语言进行开发,所以就先使用c语言实现这个功能。

2、所需知识和材料

1)、windows电脑,安装vmware虚拟机,并安装ubuntu系统,ubuntu中安装了gcc工具链。

2)、基本的C语言知识。

3)、BMP格式的知识。

除上面所列之外,还需要基本的vim或gedit工具使用方法,基本的ubunut 下linux操作方法等。

3、c语言简介

C语言是一门面向过程的、抽象化的通用程序设计语言,广泛应用于底层开发。C语言能以简易的方式编译、处

理低级存储器。C语言是仅产生少量的机器语言以及不需要任何运行环境支持便能运行的高效率程序设计语言

。尽管C语言提供了许多低级处理的功能,但仍然保持着跨平台的特性,以一个标准规格写出的C语言程序可在

包括类似嵌入式处理器以及超级计算机等作业平台的许多计算机平台上进行编译。

4、BMP格式简介

BMP(Bitmap-File)图形文件是Windows采用的图形文件格式,在Windows环境下运行的所有图象处理软件都支持BMP图象文件格式。Windows系统内部各图像绘制操作都是以BMP为基础的。

位图文件可看成由4个部分组成:位图文件头(bitmap-file header)、位图信息头(bitmap-information header)、彩色表(color table)和定义位图的字节(位图数据,即图像数据,Data Bits 或Data Body)阵列。具体如下所示:

0000h 文件标识 2 bytes 两字节的内容用来识别位图的类型:

0002h File Size 1 dword 用字节表示的整个文件的大小

0006h Reserved 1 dword 保留,必须设置为0

000Ah Bitmap Data Offset 1 dword 从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量

000Eh Bitmap Header Size 1 dword位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:

0012h Width 1 dword位图的宽度,以象素为单位

0016h Height 1 dword位图的高度,以象素为单位

001Ah Planes 1 word位图的位面数(注:该值将总是1)

001Ch Bits Per Pixel 1 word 每个象素的位数

001Eh Compression 1 dword 压缩说明:

0022h Bitmap Data Size 1 dword 用字节数表示的位图数据的大小。该数必须是4的倍数

0026h HResolution 1 dword 用象素/米表示的水平分辨率

002Ah VResolution 1 dword 用象素/米表示的垂直分辨率

002Eh Colors 1 dword位图使用的颜色数。如8-比特/象素表示为100h或者 256.

0032h Important Colors 1 dword 指定重要的颜色数。该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要调色板数据 根据BMP版本的不同而不同 Palette N * 4 byte 调色板规范。

纯红BMP的16进制文件如下图:

图像处理基本方法-c语言生成纯色BMP文件

红线框出来的是54个字节的bmp头,蓝线框出来的是一个BGR像素点。

该图片在windows电脑上预览效果如下:
图像处理基本方法-c语言生成纯色BMP文件

5、代码实现

程序实现是这样的:

/*******************************************************
* file:testbmp.c
* date:2021-04-27
* version:1.0.0.1
* author:jack8126
* description: create bmp file
*******************************************************/


#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>

#define PRINT_CTRL		 	1

typedef unsigned char       WORD8;       /* 8位无符号整型   */
typedef unsigned short      WORD16; 	 /* 16位无符号整型  */
typedef unsigned int        WORD32; 	 /* 32位无符号整型  */
typedef unsigned char       BYTE;        /* 8位无符号整型   */
typedef signed int          INT;         /* 32位有符号整型   */
typedef signed char         CHAR;        /* 8位有符号字符型  */		
typedef signed long         LONG;        /* 32位有符号长整型 */
typedef unsigned long       uLONG;       /* 32位无符号长整型 */
typedef signed short	    SHORT;       /* 16位有符号整型  */
typedef void                *LPVOID;     /* 空类型           */
typedef unsigned char       BOOLEAN;     /* 布尔类型        */

#pragma pack(push)
#pragma pack(1)


typedef struct INFOHEADER  
{   
    WORD32 biSize;   
    WORD32 biWidth;   
    WORD32 biHeight;   
    WORD16 biPlanes;   
    WORD16 biBitCount;   
    WORD32 biCompression;   
    WORD32 biSizeImage;   
    WORD32 biXPelsPerMeter;   
    WORD32 biYPelsPerMeter;   
    WORD32 biClrUsed;   
    WORD32 biClrImportant;   
}BITMAPINFOHEADER; 

typedef struct tagBITMAPFILEHEADER
{
    WORD16 bfType; 
    WORD32 bfSize;
    WORD16 bfReserved1;
    WORD16 bfReserved2;
    WORD32 bfOffBits;
} BITMAPFILEHEADER;
#pragma pack(pop)

/*
u8PicNameOut    输出的bmp路径及名字,返回值,如果pcPtnBuf为NULL时,则返回bmp文件
iWidth          要转换的bmp图片宽度,1-4096
iHeight         要转换的bmp图片高度,1-4096
pcPtnBuf[]      输入的图片buffer地址,可为NULL,为NULL时生成bmp图片,返回的名称在u8PicNameOut
int iBmpBit     要转换的bit数,3-表示输出8bit的bmp,即RGB, 6-表示输出16bit的bmp,RRGGBB
BYTE u8R 
BYTE u8G
BYTE u8B
*/
int generateBmpFunc(char u8PicNameOut[], int iWidth, int iHeight,BYTE pcPtnBuf[],int iBmpBit, BYTE u8R, BYTE u8G, BYTE u8B)
{
    //int fd = -1;
    FILE* fd ;
    int nReadLenRet = 0;
    int nReadLen = 0;
    int nReadLenAll = 0;

    FILE* fdWrite;
    //char cBmpWriteData[780*500*3] = {0};
    int iBmpWriteDataLength = iWidth*iHeight*iBmpBit;
    int iflag = 0;
    int isavefileflag = 0;
    static int siCountFlag = 0;
    
    if(pcPtnBuf != NULL)
    {
        iflag = 1;
        printf("iflag=%d\r\n",iflag);
    }
    char* pcBmpWriteData = NULL;
    WORD16* pw16BmpWriteData = NULL;

    if(iflag == 0)
    {
        pcBmpWriteData = malloc(iBmpWriteDataLength);
        printf("pcBmpWriteData malloc iBmpWriteDataLength=%d\r\n",iBmpWriteDataLength);
    }
    else
    {
        pcBmpWriteData = pcPtnBuf;
        printf("pcBmpWriteData = pcPtnBuf \r\n");
    }
    
    //char cBmpWriteData[iWidth*iHeight*3] = {0};
    int nWriteLenRet = 0;
    BITMAPFILEHEADER bmp_file_header_save = {0};
	BITMAPINFOHEADER bmp_info_header_save = {0};
    BYTE u8PicNameTmp[64] = {0};         /* 图片名称 */
    BYTE u8PicNameSave[64] = {0};         /* 图片名称 */
	BYTE u8PicNameRead[64] = {0}; 
	BYTE *pSmallPicptnBuf = NULL;
	INT nSmallPicSize = 0;
	INT fileLong = 0;
    BYTE *pAllPicptnBuf = NULL;
	INT nAllPicSize = 0;

	sprintf(u8PicNameTmp,"%s",u8PicNameOut);

	memset(pcBmpWriteData,0,iBmpWriteDataLength);
	
	bmp_info_header_save.biSize = 0x28;
    bmp_info_header_save.biWidth = iWidth;
    bmp_info_header_save.biHeight = iHeight;
    bmp_info_header_save.biPlanes = 1;
    bmp_info_header_save.biBitCount = 24;
    bmp_info_header_save.biXPelsPerMeter = 0xEC4;
    bmp_info_header_save.biYPelsPerMeter = 0xEC4;
    //bmp_info_header_save.file_size = bmp_header.biHeight*(bmp_header.biWidth * 3);

    bmp_file_header_save.bfType = 0x4D42;
    bmp_file_header_save.bfSize = iWidth * iHeight * iBmpBit + sizeof(bmp_file_header_save) + sizeof(bmp_info_header_save); //0x11DA86;
    bmp_file_header_save.bfOffBits = 0x36;
    

	// 打开图片,准备写入保存的bmp图片,也可不进行写入
	if(u8PicNameOut != NULL)
	{
	    isavefileflag = 1;
	}

	if(isavefileflag == 1)
	{
    	sprintf(u8PicNameSave,"%s",u8PicNameTmp);
    	sprintf(u8PicNameOut,"%s",u8PicNameSave);

        #ifdef PRINT_CTRL
        printf("u8PicNameSave=%s\r\n",u8PicNameSave);
        #endif
        
        fdWrite= fopen(u8PicNameSave, "w+");
        //fdWrite = open("8.bmp", O_RDWR | O_CREAT | O_APPEND,0644);
        if(fdWrite < 0)
        {
            fclose(fd);
            if(iflag == 0)
            {
                if(pcBmpWriteData != NULL)
                {
                    free(pcBmpWriteData);
                    pcBmpWriteData = NULL;
                }
            }
            perror("open log failed!\n");
            return -1;
        }
        
        fwrite(&bmp_file_header_save, 1, sizeof(bmp_file_header_save), fdWrite);
        fseek(fdWrite,0,SEEK_END);
        
        //nWriteLenRet = write(fdWrite,&bmp_file_header_save,sizeof(bmp_file_header_save));
        //lseek(fdWrite, 0, SEEK_END);
        
        fwrite(&bmp_info_header_save, 1, sizeof(bmp_info_header_save), fdWrite);
        fseek(fdWrite,0,SEEK_END);

        //nWriteLenRet = write(fdWrite,&bmp_info_header_save,sizeof(bmp_info_header_save));        
        //lseek(fdWrite, 0, SEEK_END);
    }

	#ifdef PRINT_CTRL
	printf("bmp header size1=%ld\r\n", sizeof(bmp_file_header_save) );
	printf("bmp header size2=%ld\r\n", sizeof(bmp_info_header_save) );

	printf("bmp header size=%ld\r\n",sizeof(bmp_file_header_save) + sizeof(bmp_info_header_save) );
	#endif
	
    if(u8R == u8G && u8R == u8B)
    {    	
	    memset(pcBmpWriteData,u8R,iBmpWriteDataLength);
	    if(iBmpBit == 6)
	    {
	    	// just memset 0
	        memset(pcBmpWriteData,0,iBmpWriteDataLength);
	    }    
    }
    else
    {    	
	    int i = 0;
	    int j = 0;
	    int yinit = 0;
	    int xinit = 0;
	    
	    int iComNumTmp = 0;
	    // 需根据分辨率每个像素点进行循环写入RGB的值
	    
		// 描点法
	    #if 1
		for (i = yinit; i < iHeight; i++) 
		{ 
			for (j = xinit; j< iWidth; j++) 
			{ 
				int ntmp = (iHeight -1 -  i)*iBmpBit*iWidth + (j )*iBmpBit;
	
				//printf("i=%d,j=%d,k=%d,iCRGB=%d,%d,%d,ntmp=%d\r\n",i,j,k,iCR16[k],iCG16[k],iCB16[k],ntmp);
	
				pcBmpWriteData[ntmp + 0] = u8B;
				pcBmpWriteData[ntmp + 1] = u8G;
				pcBmpWriteData[ntmp + 2] = u8R;
			}
		}
	    #endif
    }


    // 写bmp文件
    if(isavefileflag)
    {    
		//nWriteLenRet = write(fdWrite,&cBmpWriteData,sizeof(cBmpWriteData));
        fwrite(pcBmpWriteData, 1, iBmpWriteDataLength, fdWrite);
    }
    
    if(iflag == 0)
    {
	    if(pcBmpWriteData != NULL)
        {
            free(pcBmpWriteData);
            pcBmpWriteData = NULL;
        }
    }

    siCountFlag = 0;
    
    if(isavefileflag)
    {
        if(fdWrite)
        {         
			//close(fdWrite);
            fclose(fdWrite);
        }
    }

	if(fd)
	{
	
		//close(fd);
		fclose(fd);
	}

}

//主函数
int main(int argc, char * * argv)
{
    BYTE u8PicNameRead[64] = {0}; 
    BYTE u8PicNameOut[64] = {0}; 
    int iBmpBit = 3;
    int iBmpBitTmp = 0;
	struct timeval tStartTime;
    struct timeval tEndTime;
    int isecond = 0;
    int iusecond = 0;
    int iWidth = 0;
    int iHeigth = 0;
    BYTE u8R = 0;    
    BYTE u8G = 0; 
    BYTE u8B = 0; 

	if(argc < 8)
	{
		printf("please input like this:\r\n");
		printf("./testbmp.bin test.bmp 3 1920 1080 255 255 255 \r\n");
		printf("test.bmp --------------- output file \r\n");
		printf("3        --------------- 3-RGB 3 bytes \r\n");
		printf("1920     --------------- width\r\n");
		printf("1080    ---------------- heigth \r\n");
		printf("255     ---------------- B \r\n");
		printf("255     ---------------- G \r\n");
		printf("255     ---------------- R \r\n");
		return -1;
	}
    printf("argv[1]=%s\r\n",argv[1]);
    printf("argv[2]=%s,%d\r\n",argv[2],atoi(argv[2]));

    sprintf(u8PicNameRead,"%s",argv[1]);
    sprintf(u8PicNameOut,"%s",argv[1]);

    printf("u8PicNameRead=%s\r\n",u8PicNameRead);

	iBmpBitTmp = atoi(argv[2]);
    if(iBmpBitTmp == 3 || iBmpBitTmp ==6)
    {
        iBmpBit = iBmpBitTmp;
    }
    
	iWidth = atoi(argv[3]);
	iHeigth = atoi(argv[4]);
	u8R = atoi(argv[5]);
	u8G = atoi(argv[6]);
	u8B = atoi(argv[7]);

    printf("iWidth  = %d\r\n",iWidth);
    printf("iHeigth = %d\r\n",iHeigth);
    printf("u8R     = %d\r\n",u8R);
    printf("u8G     = %d\r\n",u8G);
    printf("u8B     = %d\r\n",u8B);

    
    gettimeofday(&tStartTime, NULL);
    //printf("tStartTime time.tv_sec = %d, time.tv_usec = %d.\r\n", tStartTime.tv_sec, tStartTime.tv_usec);

    generateBmpFunc(u8PicNameOut,iWidth,iHeigth,NULL,iBmpBit,u8R,u8G,u8B);

    gettimeofday(&tEndTime, NULL);
    if(tEndTime.tv_sec >= tStartTime.tv_sec)
    {
        isecond = tEndTime.tv_sec-tStartTime.tv_sec;
    }
    if(tEndTime.tv_usec >= tStartTime.tv_usec)
    {
        iusecond = tEndTime.tv_usec-tStartTime.tv_usec;
    }
    else
    {
        isecond--;
        iuseconEndTime.tv_usec-tStartTime.tv_usec + 1000000;
    }
    //printf("tEndTime time.tv_sec = %d, time.tv_usec = %d.\r\n", tEndTime.tv_sec, tEndTime.tv_usec);
    printf("gernerateBmpFunc use time tv_sec=%ds, ms=%d, tv_usec=%dus\r\n",isecond,iusecond/1000,iusecond);

    printf("u8PicNameOut=%s\r\n",u8PicNameOut);
    
    return 0; 
}


6、编译程序

编译命令如下

gcc testbmp.c -o testbmp.bin

执行完上述命令之后,会生成test-bmp.bin文件,后面执行程序时需要该文件。

7、执行程序

使用如下命令执行程序,可产生bmp文件。

./testbmp.bin test.bmp 3 1920 1080 0 0 255

执行上述命令,由于需要写入大量数据,所以耗时较长。

执行完成之后,会生成bmp文件。

需要修改分辨率和颜色的话,可以修改输入的宽高和红绿蓝参数即可。

8、参考资料

https://blog.csdn.net/nibiewuxuanze/article/details/78805763

https://blog.csdn.net/whik1194/article/details/115533242

https://blog.csdn.net/whik1194/article/details/115440543

上一篇:CodeForces - 31E TV Game【DP】


下一篇:怎样将 HomePod、HomePod mini、Apple TV 或 iPad 设置为家居中枢?