CTF做了图片的隐写题,还没有形成系统的认识,先来总结一下BMP图的组成,并通过将彩色图转为二值图的例子加深下理解。

只写了位图二进制文件的格式和代码实现,至于诸如RGB色彩和调色板是什么的一些概念就不啰嗦了。

BMP位图文件格式

BMP文件由文件头、位图信息头、调色板和图形数据四部分组成,真彩色图是没有调色板的。每部分的具体结构在代码中具体列出并解释。

结构体的对齐

定义文件头部各结构体时要注意对齐的问题,至于什么是结构体对齐,请看这篇博文,写的很详细http://www.cnblogs.com/motadou/archive/2009/01/17/1558438.html

位图的四字节对齐

这个对齐是位图每行像素的对齐,不要和上面的结构体对齐混淆,每行像素所占字节数必须是4字节的整数倍,不足的要填充,这时因为内存分配单位是32位的,即4字节,读入的每行像素是连续的,不能和其他行共占一个内存单位

彩色图转为灰度图

RGB共有256种灰色分量,就是R=G=B时的色彩是灰色的,所以可以用一个字节来表示,将彩色图转化为灰度图就是让真彩色图的三个颜色分量等于一个相同的数值,具体等于多少可以与多种方法,比如求三个分量的平均值,取三个分量的最大值或者使其中两个分量等于另一个分量的值,还有一种常用的方法是取加权平均值,根据这个很著名的心理学公式Gray = R*0.299 + G*0.587 + B*0.114

灰度图转化为二值图

二值图只有两个颜色,黑和白,而灰度有256种颜色,将灰度转化为二值是选取一个阈值,将灰度值大于这个阈值的置成白色,反之为黑色,关于这个阈值如何选取和作用范围也有多种方法,不再赘述,为了简单我在全局范围内选用一个固定的阈值190(针对我这张测试图片,手敲了个190,转化的效果还可以)二值图只有两个索引,可以用1bit表示,但是我写的程序是用1个字节表示的,至于如何压缩,看你喽。。。

说了这么多,还是看代码吧,这样更容易理解,额,好像几乎给每行都写了注释╮(╯▽╰)╭,不要嫌我墨迹

 /*
Author:蔚蓝行
Blog:http://www.cnblogs.com/duanv/
*/
#include <stdio.h>
#include <stdlib.h> /*位图文件头*/
#pragma pack(1)//单字节对齐
typedef struct tagBITMAPFILEHEADER
{
unsigned char bfType[];//文件格式
unsigned int bfSize;//文件大小
unsigned short bfReserved1;//保留
unsigned short bfReserved2;//保留
unsigned int bfOffBits;//数据偏移量
}fileHeader;
#pragma pack() /*位图信息头*/
#pragma pack(1)
typedef struct tagBITMAPINFOHEADER
{
unsigned int biSize;//BITMAPINFOHEADER结构所需要的字数
int biWidth;//图像宽度,像素为单位
int biHeight;//图像高度,像素为单位,为正数,图像是倒序的,为负数,图像是正序的
unsigned short biPlanes;//为目标设备说明颜色平面数,总被置为1
unsigned short biBitCount;//说明比特数/像素
unsigned int biCompression;//说明数据压缩类型
unsigned int biSizeImage;//说明图像大小,字节单位
int biXPixPerMeter;//水平分辨率,像素/米
int biYPixPerMeter;//垂直分辨率
unsigned int biClrUsed;//颜色索引数
unsigned int biClrImportant;//重要颜色索引数,为0表示都重要
}fileInfo;
#pragma pack() /*调色板结构*/
#pragma pack(1)
typedef struct tagRGBQUAD
{
unsigned char rgbBlue;//蓝色分亮度
unsigned char rgbGreen;//绿色分亮度
unsigned char rgbRed;//红色分亮度
unsigned char rgbReserved;
}rgbq;
#pragma pack() int main()
{
/*变量声明*/
FILE *fpBMP,*fpTwoValue;//源文件fpBMP,目标文件fpTwoValue fileHeader *fh;//位图文件头
fileInfo *fi;//位图信息头
rgbq *rg;//调色板 int i,j,k=;
unsigned char *a;//存储源图每行像素值
unsigned char b;//存储每个像素的灰度值或二值
unsigned char *c;//存储每行像素的二值 /********************************************************************/ /*打开源文件,创建输出文件*/
if((fpBMP=fopen("/Users/SPY/Desktop/1.bmp","rb"))==NULL){
printf("file open failed");
exit();
} if((fpTwoValue=fopen("/Users/SPY/Desktop/2.bmp","wb"))==NULL){
printf("file creat failed");
exit();
} /********************************************************************/ /*创建位图文件头,信息头,调色板*/
fh=(fileHeader *)malloc(sizeof(fileHeader));
fi=(fileInfo *)malloc(sizeof(fileInfo));
rg=(rgbq *)malloc(*sizeof(rgbq)); /*读入源位图文件头和信息头*/
fread(fh,sizeof(fileHeader),,fpBMP);
fread(fi,sizeof(fileInfo),,fpBMP); /*修改文件头,信息头信息*/
fi->biBitCount=;//转换成二值图后,颜色深度由24位变为8位
fi->biSizeImage=((fi->biWidth+)/)**fi->biHeight;//每个像素由三字节变为单字节,同时每行像素要四字节对齐
fi->biClrUsed=;//颜色索引表数量,二值图为2
fi->biClrImportant=;//重要颜色索引为0,表示都重要
fh->bfOffBits=sizeof(fileHeader)+sizeof(fileInfo)+*sizeof(rgbq);//数据区偏移量,等于文件头,信息头,索引表的大小之和
fh->bfSize=fh->bfOffBits+fi->biSizeImage;//文件大小,等于偏移量加上数据区大小
rg[].rgbBlue=rg[].rgbGreen=rg[].rgbRed=rg[].rgbReserved=;//调色板颜色为黑色对应的索引为0
rg[].rgbBlue=rg[].rgbGreen=rg[].rgbRed=;//白色对应的索引为1
rg[].rgbReserved=; /********************************************************************/ /*将位图文件头,信息头和调色板写入文件*/
fwrite(fh,sizeof(fileHeader),,fpTwoValue);
fwrite(fi,sizeof(fileInfo),,fpTwoValue);
fwrite(rg,*sizeof(rgbq),,fpTwoValue); /*将彩色图转为二值图*/
a=(unsigned char *)malloc((fi->biWidth*+)/*);//给变量a申请源图每行像素所占大小的空间,考虑四字节对齐问题
c=(unsigned char *)malloc((fi->biWidth+)/*);//给变量c申请目标图每行像素所占大小的空间,同样四字节对齐 for(i=;i<fi->biHeight;i++){//遍历图像每行的循环
for(j=;j<((fi->biWidth*+)/*);j++){//遍历每行中每个字节的循环
fread(a+j,,,fpBMP);//将源图每行的每一个字节读入变量a所指向的内存空间
//printf("%d ",a[j]);
}
for(j=;j<fi->biWidth;j++){//循环像素宽度次,就不会计算读入四字节填充位
b=(int)(0.114*(float)a[k]+0.587*(float)a[k+]+0.299*(float)a[k+]);//a中每三个字节分别代表BGR分量,乘上不同权值转化为灰度值
//printf("%d",b);
if(<=(int)b) b=;//将灰度值转化为二值,这里选取的阈值为190
else b=;
c[j]=b;//存储每行的二值
k+=;
}
fwrite(c,(fi->biWidth+)/*,,fpTwoValue);//将二值像素四字节填充写入文件,填充位没有初始化,为随机值
k=;
} /********************************************************************/ /*释放内存空间,关闭文件*/
free(fh);
free(fi);
free(rg);
free(a);
free(c);
fclose(fpBMP);
fclose(fpTwoValue);
printf("success!\n");
return ;
}

运行的结果如下(不支持上传位图,只能看下效果了):

C语言实现将彩色BMP位图转化为二值图的更多相关文章

  1. c语言实现灰度图转换为二值图

    将上篇得到的灰度图转换为二值图,读取像素数据,低于某一值置0,否则设置为255,为得到更好的效果不同图片应采用不同的值 /* 2015年6月2日11:16:22 灰度图转换为二值图 blog:http ...

  2. BMP彩色转成黑色二值图

    一天半把彩色bmp转成黑白了. 原理是: 第一步:读出位图数据的偏移位置:即第11个字节,用fseek即可. 然后将偏移位置之前的数据全部写入新的bmp图中. 第二步:用fseek移到位图数据这前,判 ...

  3. Windows下BMP位图格式介绍

    BMP图片,是Bitmap(位图)的简称,它是windows下显示图片的基本格式.在windows下任何格式的图片文件(包括视频播放)都要转化为位图才能显示出来.各种格式的图片文件也都是在位图格式的基 ...

  4. Linux C语言解析并显示.bmp格式图片

    /************************* *bmp.h文件 *************************/ #ifndef __BMP_H__ #define __BMP_H__ # ...

  5. 怎么样用opencv将彩色图片转化成像素值只有0和255的灰度图?

      分类: OpenCV [Q1]怎么样用opencv将彩色图片转化成像素值只有0和255的灰度图? 进行灰度化,IplImage* pImg = cvLoadImage( "C:\\1.b ...

  6. 将文件内容隐藏在bmp位图中

    首先要实现这个功能,你必须知道bmp位图文件的格式,这里我就不多说了,请看:http://www.cnblogs.com/xiehy/archive/2011/06/07/2074405.html 接 ...

  7. 浅析BMP位图文件结构(含Demo)

    浅析BMP位图文件结构(含Demo) 作者:一点一滴的Beer http://beer.cnblogs.com/   关于BMP位图格式在网上可以找到比较详细的相关文档,有兴趣的可以搜索标题为“BMP ...

  8. gnu-ucos 增加 bmp 位图显示

    昨天又下了点功夫弄了个在tft屏幕上显示bmp位图的. 我选择的是24位tft真彩測显示方式所以也要选择真彩色位图.网上给出的16位位图数组无法使用.在csdn上下载了2个制作工具,一个是c代码的,一 ...

  9. BMP位图图像格式简介

    1. 文件结构 位图文件可看成由4个部分组成:位图文件头(bitmap-fileheader).位图信息头(bitmap-informationheader).彩色表(colortable)和定义位图 ...

随机推荐

  1. 曲演杂坛--使用ALTER TABLE修改字段类型的吐血教训

    --===================================================================== 事件起因:开发发现有表插入数据失败,查看后发现INT类型 ...

  2. HLSL-高级着色语言简介【转】

    HLSL-High Level Shader Language 优点 用来书写Vertex Shader和Pixel Shader程序的代码,语法类似于C/C++,在DirectX 8.x的时代,Sh ...

  3. javascript快速排序的思考

    还记得三个月前,学习过快速排序,示例所讲的python快速排序十分易于理解,然而网上学习的c#的快速排序当时就懵逼的,现在已经全忘了,大概记得个思路 在学习完了一些高级的js方法后,今天用js模拟了p ...

  4. .Net Core与跨平台时区

    由于开发者不熟悉不同操作系统管理时区的方式,当用.Net Core开发与时区相关的应用运行在不同操作系统上会出现错误.这片文章将会探索一下在不同操作系统上用.Net Core 使用时区信息出现的问题与 ...

  5. reids 安装

    第一步:在VMware中安装CentOS(参考Linux教程中的安装虚拟机) 第二步:在Linux下安装gcc环境 [root@hadoop ~]#yum install gcc-c++ 第三步:将下 ...

  6. SSM文件上传

    **自己对于SSM文件上传的一些心得** 刚开始的时候也是在网上寻找一些简单的案例,可能我的这篇文章不是最好的,但是这些都是我自己慢慢的摸索以及自己的尝试的一些心得,希望对各位有所帮助. 其实文件的上 ...

  7. 使用Commons Email发送邮件

    Commons Email是apache commons库中的一个组件,对java mail做了一些个封装,提供能为简化的API供开发者使用.它依赖于javax.mail . 首先下载commons- ...

  8. Sorted方法排序用法

    listA = [3,4,5,3,2,1,] print(sorted(listA)) # [1, 2, 3, 3, 4, 5] listB =["a","z" ...

  9. 伸展树的实现——c++

     一.介绍 伸展树(Splay Tree)是一种二叉排序树,它能在O(log n)内完成插入.查找和删除操作.它由Daniel Sleator和Robert Tarjan创造.(01) 伸展树属于二叉 ...

  10. Xshell连接不上Linux

    用Xshell连接Linux的时候报错了: Connecting to 192.168.79.147:22...Could not connect to '192.168.79.147' (port ...