c++ 在控制台里用字符拼成图片
2015-06-14 08:38
573 查看
先上个效果图
原图
打印图
放大图
大体思路是这样的,不小心写了个死循环,看见屏幕上不停输出一行一行的乱序字母,然后想到,如果字母很多,能不能用不同的字母模拟像素点,然后写一个程序,读入一张图片,然后用各种字符把它在屏幕上拼出来.
不同的字符,不如说是16 x16 的点阵字 在相同的16 x16 中 每个字符所占的点阵中的点数是不一样的。
比如这个m 很显然比这个逗号 要“黑”很多。那我就可以用m表示比较暗的像素点,用逗号表示较亮的像素点。 如果把0~127 每个字符的明暗排序的话,我就得到了一组可以使用的像素点,然后读入我要画的图片,获取其像素点颜色信息。
和刚排好序的字符集 对应,然后按对应位置输出相对字符,就应该能大致描绘出图片了。
我没有接触过c/c++的图像处理,也不知道有什么处理图片的库什么的,反正我的要求也不高,只想获取像素点的信息而已, 不管怎样 先了解一下各种图片的存储格式吧。查了一圈,决定选择处理 BMP格式的图片,我们来看一下 它的结构
////////////////////////////////////////////////////////////////////////////////////////////////一下内容来自百科,大概了解下结构,找到我们要用的信息
BMP文件头(14字节)
3:位图信息头(40字节)
BMP位图信息头数据用于说明位图的尺寸等信息。
4:颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
5:位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
看完我找到了这些信息,
bmp图的头部有一些数据结构来描述图片的一些相关信息,有文件头,信息头,彩色表,数据。
其中文件头和信息头是固定长度的,根据图片的颜色类型不同,彩色表的长度不定,但在在文件头里有一个
bfOffBits 这个偏移量来标示真正的像素信息开始的位置,通过这个量就可以跳过头部信息,直接找到我们想要的像素信息。
还有一点要提的是,图片的色彩 深度,不确定是不是这么叫,简单理解就是支持多少颜色。在信息头里有一个
biBitCount
可以取值 1,4,8,16,24.比如说biBitCount =8 那表示 用8位表示一个像素点信息,也就是这个像素点 有0~255 中颜色可选,也就是说这是一个灰度图,通俗说 图片只有黑白灰颜色。
现在确定一下目标,我要处理 8位的 bmp图片, 因为灰度图 在黑框框里描绘 比较合适,而且每个像素点信息占一个字节也比较好处理~~~然后思路是这样的,程序读取一张bmp图,通过指针偏移控制,得到图片的
biWidth和
biHeight(乘积就是
所有像素点的个数,而且这两个量 在最后控制台输出的时候要做输出控制用),同样能得到bfOffBits, 通过这个量就能找到像素数据的起点。然后把数据都读出来。剩下的问题是 我如何把这个像素点信息,映射成最终要在屏幕上输出的字符。这里用一个数组[256]
(起个名字叫point),这个数组是这样来的,前边我们说过,要有一个字符集中各个字符“亮度”的一个排序, 把这些排好序的字符,对应到这个point[256]的数组中,数组每个元素存储的是对应字符的ASCII码。
然后我从图片中读取一个像素的信息,其实就是一个字节,就是一个0~255
的数字,这个数字num代表这个像素点在原图中的颜色,在8位灰度图中就理解为亮度,假设num= 255 那这个像素点在原图中是“最亮”的,即白色, 那么
point[num]=
就对应字符集中相对“最亮的”的那个字符的ASCII码。其他的不同亮度也能按下标一一对应,因为
point[]
已经按亮度排好序了。这时候想在屏幕上输出这个点 可以
cout<<(char)piont[num];
这样就找到了像素点所对应的字符。
很重要的一个问题是,怎么获得字符集中128个字符的“亮度”排序? 我实在没有找到什么好办法。。。 找了一张
ASCII码表 切成了 128张bmp。。。
然后写了个程序,通过上边对bmp文件格式的分析,读出每个bmp像素信息,统计黑像素点个数,然后排序,将结果保存。。。
这其中有几点,
128个字符 中黑色像素点个数 有很多字符是相同,也就是这些字符打在屏幕上“亮度”是,一样的。还有一些字符是不可打印的,考虑到上述因素,最后筛选出可以用字符集“亮度”序列,有64
个字符,然后让每一个字符 对应 0~255中的四个亮度,形式point[255],保存到文件。
获取point[]的程序 getdata.cpp
绘图程序 bmp.cpp
//...没有整理公共头文件
后来自己先写个char* 缓冲下,只输出一次,就能很流畅的跑了
这个程序处理那些 黑白分明的图片效果比较好,毕竟用字符描绘 能力有限。
再上几张图
原图
打印图
放大图
大体思路是这样的,不小心写了个死循环,看见屏幕上不停输出一行一行的乱序字母,然后想到,如果字母很多,能不能用不同的字母模拟像素点,然后写一个程序,读入一张图片,然后用各种字符把它在屏幕上拼出来.
不同的字符,不如说是16 x16 的点阵字 在相同的16 x16 中 每个字符所占的点阵中的点数是不一样的。
比如这个m 很显然比这个逗号 要“黑”很多。那我就可以用m表示比较暗的像素点,用逗号表示较亮的像素点。 如果把0~127 每个字符的明暗排序的话,我就得到了一组可以使用的像素点,然后读入我要画的图片,获取其像素点颜色信息。
和刚排好序的字符集 对应,然后按对应位置输出相对字符,就应该能大致描绘出图片了。
我没有接触过c/c++的图像处理,也不知道有什么处理图片的库什么的,反正我的要求也不高,只想获取像素点的信息而已, 不管怎样 先了解一下各种图片的存储格式吧。查了一圈,决定选择处理 BMP格式的图片,我们来看一下 它的结构
////////////////////////////////////////////////////////////////////////////////////////////////一下内容来自百科,大概了解下结构,找到我们要用的信息
BMP文件头(14字节)
BMP位图信息头数据用于说明位图的尺寸等信息。
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节,按顺序分别为B,G,R;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
看完我找到了这些信息,
bmp图的头部有一些数据结构来描述图片的一些相关信息,有文件头,信息头,彩色表,数据。
其中文件头和信息头是固定长度的,根据图片的颜色类型不同,彩色表的长度不定,但在在文件头里有一个
bfOffBits 这个偏移量来标示真正的像素信息开始的位置,通过这个量就可以跳过头部信息,直接找到我们想要的像素信息。
还有一点要提的是,图片的色彩 深度,不确定是不是这么叫,简单理解就是支持多少颜色。在信息头里有一个
biBitCount
可以取值 1,4,8,16,24.比如说biBitCount =8 那表示 用8位表示一个像素点信息,也就是这个像素点 有0~255 中颜色可选,也就是说这是一个灰度图,通俗说 图片只有黑白灰颜色。
现在确定一下目标,我要处理 8位的 bmp图片, 因为灰度图 在黑框框里描绘 比较合适,而且每个像素点信息占一个字节也比较好处理~~~然后思路是这样的,程序读取一张bmp图,通过指针偏移控制,得到图片的
biWidth和
biHeight(乘积就是
所有像素点的个数,而且这两个量 在最后控制台输出的时候要做输出控制用),同样能得到bfOffBits, 通过这个量就能找到像素数据的起点。然后把数据都读出来。剩下的问题是 我如何把这个像素点信息,映射成最终要在屏幕上输出的字符。这里用一个数组[256]
(起个名字叫point),这个数组是这样来的,前边我们说过,要有一个字符集中各个字符“亮度”的一个排序, 把这些排好序的字符,对应到这个point[256]的数组中,数组每个元素存储的是对应字符的ASCII码。
然后我从图片中读取一个像素的信息,其实就是一个字节,就是一个0~255
的数字,这个数字num代表这个像素点在原图中的颜色,在8位灰度图中就理解为亮度,假设num= 255 那这个像素点在原图中是“最亮”的,即白色, 那么
point[num]=
就对应字符集中相对“最亮的”的那个字符的ASCII码。其他的不同亮度也能按下标一一对应,因为
point[]
已经按亮度排好序了。这时候想在屏幕上输出这个点 可以
cout<<(char)piont[num];
这样就找到了像素点所对应的字符。
很重要的一个问题是,怎么获得字符集中128个字符的“亮度”排序? 我实在没有找到什么好办法。。。 找了一张
ASCII码表 切成了 128张bmp。。。
然后写了个程序,通过上边对bmp文件格式的分析,读出每个bmp像素信息,统计黑像素点个数,然后排序,将结果保存。。。
这其中有几点,
128个字符 中黑色像素点个数 有很多字符是相同,也就是这些字符打在屏幕上“亮度”是,一样的。还有一些字符是不可打印的,考虑到上述因素,最后筛选出可以用字符集“亮度”序列,有64
个字符,然后让每一个字符 对应 0~255中的四个亮度,形式point[255],保存到文件。
获取point[]的程序 getdata.cpp
#include<iostream> #include<map> #include<string> #include<windows.h> #include<vector> #include<algorithm> using namespace std; //get length of hearder void bmpHeaderPartLength(FILE* fpbmp); // get width and height void BmpWidthHeight(FILE* fpbmp); //save letter's black pixel number into a map void SaveLetterInfo(FILE* fpbmp,int asc_code); unsigned int OffSet = 0; // OffSet from Header part to Data Part long BmpWidth = 0; long BmpHeight = 0; int point[256]; map<int,int > ma; //get header length(offset) void bmpHeaderPartLength(FILE* fpbmp) { fseek(fpbmp, 10, SEEK_SET); fread(&OffSet, sizeof(char), 4, fpbmp); } //get width and height void BmpWidthHeight(FILE* fpbmp) { fseek(fpbmp, 18, SEEK_SET); fread(&BmpWidth, sizeof(char), 4, fpbmp); fread(&BmpHeight, sizeof(char), 4, fpbmp); } // save letter's black pixel number into a map void SaveLetterInfo(FILE* fpbmp,int asc_code) { unsigned char* bmpPixel= NULL; unsigned char* tem = NULL; int numOfPixel = 0; fseek(fpbmp, OffSet, SEEK_SET); if ((bmpPixel=(unsigned char*)malloc(sizeof(char)*BmpWidth*BmpHeight))==NULL) { printf("allocation failed.!!!\n"); exit(1); } tem = bmpPixel; fread(bmpPixel, sizeof(char), BmpWidth*BmpHeight, fpbmp); for(int i=0;i<BmpWidth;++i) { for(int j=0;j<BmpHeight;++j) { if(*bmpPixel < 100) numOfPixel++; bmpPixel++; } } ma.insert(pair<int,int>(numOfPixel,asc_code)); free(tem); }
int main(int argc, char* argv[]) { string s_fileName; char fileNamehead[4]; for(int i=0;i<128;++i) { sprintf(fileNamehead,"%d",i); s_fileName.erase(); s_fileName += fileNamehead; s_fileName += ".bmp"; FILE *fpbmp = fopen(s_fileName.c_str(), "r+"); if (fpbmp == NULL) { printf("Open bmp failed!!!\n"); return 1; } bmpHeaderPartLength(fpbmp); BmpWidthHeight(fpbmp); SaveLetterInfo(fpbmp,i); fclose(fpbmp); } //erase the character which cant be printed ma.erase(22); ma.erase(4); ma.erase(41); ma.erase(34); ma.erase(42); ma.erase(54); ma.erase(63); ma.erase(70); ma.erase(94); ma.erase(99); ma.erase(115); ma.erase(6);//22 4 41 34 42 54 63 70 94 99 115 6 cout<<"map size ="<<ma.size()<<endl; map<int,int>::iterator it = ma.begin();
////////////////////////for test to find witch charcter to erase // int k =0; // for(it=ma.begin();it!=ma.end();++it) // { // cout<<(char)(it->second)<<" "<<(it->second)<<" "<<it->first<<"---------"<<k++<<endl; // } for( int k=0;k<256;k+=4) { point[k] = it->second; if(k < 255) { point[k+1] = it->second; point[k+2] = it->second; point[k+3] = it->second; } it++; } FILE *fpbmp = fopen("data.txt", "w"); if (fpbmp == NULL) { printf("Open data failed!!!\n"); return 1; } for(int m =0;m<256;++m) { fprintf(fpbmp,"%d ",point[m]); } }
绘图程序 bmp.cpp
//...没有整理公共头文件
#include<iostream> #include<map> #include<string> #include<windows.h> using namespace std; //get length of hearder void bmpHeaderPartLength(FILE* fpbmp); // get width and height void BmpWidthHeight(FILE* fpbmp); //save letter's black pixel number into a map void SaveLetterInfo(FILE* fpbmp,int asc_code); //draw the bmp on screen with charcter void bmpOut(FILE* fpbmp); unsigned int OffSet = 0; // OffSet from Header part to Data Part long BmpWidth = 0; // The Width of the Data Part long BmpHeight = 0; // The Height of the Data Part int point[256]; //get header length(offset) void bmpHeaderPartLength(FILE* fpbmp) { fseek(fpbmp, 10, SEEK_SET); fread(&OffSet, sizeof(char), 4, fpbmp); //printf("The Header Part is of length %d.\n", OffSet); } //get width and height void BmpWidthHeight(FILE* fpbmp) { fseek(fpbmp, 18, SEEK_SET); fread(&BmpWidth, sizeof(char), 4, fpbmp); fread(&BmpHeight, sizeof(char), 4, fpbmp); } void bmpOut(FILE* fpbmp) { unsigned char* bmpPixelTmp = NULL; unsigned char* tem ; unsigned char * charcterToPrint = NULL; fseek(fpbmp, OffSet, SEEK_SET); if ((bmpPixelTmp=(unsigned char*)malloc(sizeof(char)*BmpWidth*BmpHeight))==NULL) { printf("Data allocation failed.!!!\n"); exit(1); } charcterToPrint=(unsigned char*)malloc(sizeof(char)*BmpWidth*BmpHeight +1+ BmpHeight); if (charcterToPrint == NULL) { printf("allocation failed.!!!\n"); exit(1); } int index = 0; tem = bmpPixelTmp; fread(bmpPixelTmp, sizeof(char), BmpWidth*BmpHeight, fpbmp); bmpPixelTmp += BmpHeight * BmpWidth -1; /////////////////////////////////////////////////////////////////////////////////// bmpPixelTmp = bmpPixelTmp - BmpWidth +1; for(int i=0;i<BmpHeight;++i) { <span style="white-space:pre"> </span>for(int j=0;j<BmpWidth;++j) { charcterToPrint[index++] = (char)point[*bmpPixelTmp]; bmpPixelTmp++; } charcterToPrint[index++] = '\n'; bmpPixelTmp = bmpPixelTmp - 2*BmpWidth; } charcterToPrint[index] = '\0'; cout <<charcterToPrint<<endl; free(tem); free(charcterToPrint); } int main() { FILE *fpdata = fopen("data.txt", "r+"); if (fpdata == NULL) { printf("Open data failed!!!%d\n"); return 1; } for(int i=0;i<256;++i) { fscanf(fpdata,"%d ",&point[i]); } <span style="white-space:pre"> </span>// open the picture and draw it! FILE *fpbmp = fopen("filename.bmp", "r+"); if (fpbmp == NULL) { printf("Open mbp failed!!!\n"); return 1; } bmpHeaderPartLength(fpbmp); BmpWidthHeight(fpbmp); bmpOut(fpbmp); return 0; }这里边有个坑,开始的时候我读一个像素点就打印一个字符,结果一个300 x300 的图片 要打印十万次,十万次的陷入内核去输出,程序在我哦电脑上一跑就卡死了。。。
后来自己先写个char* 缓冲下,只输出一次,就能很流畅的跑了
这个程序处理那些 黑白分明的图片效果比较好,毕竟用字符描绘 能力有限。
再上几张图
相关文章推荐
- C++11特性(01)auto关键字
- C++ I/O
- C++对象模型之RTTI的实现原理
- c/c++与java------之JNI学习(一)
- C/C++中问号冒号表达式的陷阱
- poj1062昂贵的聘礼有等级限制的最短路径
- 《C++Primer》读书笔记--异常处理
- 【C++ Primer Plus 9.2】 存储持续性、作用域和链接性
- c++学习
- C语言使用正则表达式
- C++为什么要设计友元函数和友元类
- C++为什么要设计友元函数和友元类
- 学生cpp成绩统计 【基类派生类练习】
- 大话设计模式C++版——抽象工厂模式
- 大话设计模式C++版——抽象工厂模式
- String的c语言实现
- 【好程序员笔记分享】—— C语言指针的理解
- 开始学习C++心得实例(1)
- c/c++回车不换行
- Cpp Primer<<学习IO标准库--文件模式、字符串流_7