这两天闲着没事,觉得将图像内容用ASCII码显示出来挺有意思的,就自己尝试着写了写,到网上一搜才知道原来这东西还是一门艺术-ASCII art!!
咱也跟艺术沾边了O(∩_∩)O哈哈哈~
闲话少说,切入正题,将图像内容用ASCII码显示出来主要就是用不同的字符表示灰度,以此来区分不同的灰度值达到将内容可视化的目的,这个道理很简单,实现起来其实也不难,如果是直接输入到控制台或者txt文件的则比较简单,主要是需要设置一下字体的宽高,最好是正方形的,否则看起来会比较别扭。下面一个是将lena缩放到100*100之后在控制台窗口直接显示的截图,采用的字符是标点,字体设置的宽高相等为8*8:
但是这种方式无法保存成图像(当然可以截图,不过那多费劲!),所以就研究了一下如何将字符直接写到图像上,这样保存图像就可以了。这时关键的问题就是如何将字符写到图像上,并且尽量让字符的宽高相等,这样看起来才不会觉得图像被挤过了⊙﹏⊙b汗!
还是用OpenCV来将字符写到图像上,使用putText函数,不过OpenCV中的字体好像不能设置为等宽高,没办法,就取了折中的方法,将用来代替灰度的所有字符的宽度和高度取了个平均作为字符宽高,这样就将原图像中的一个像素替换为这样一个字符,而且还设置了几个选项:可以选择标点,字母或者数字来代替灰度,并且可以选择输出二值,灰度或者彩色ascii图像-----文中只使用了8个灰度级,即将整幅图像量化为了8个灰度级,这样的缺点就是对灰度分布比较集中的图像失真比较大,可以先对图像进行灰度均衡化处理之后再转换,这里没有考虑这些,下面是主要的函数代码。
char ascii_code_symbol[CODE_SIZE-7] = {'#','&','$','*','+',';','.',' ',0};
char ascii_code_letter[CODE_SIZE-7] = {'m','n','e','f','t','l','i',' ',0};
char ascii_code_number[CODE_SIZE-7] = {'8','9','5','3','2','7','1',' ',0};
char *ascii_code_8[3] = { ascii_code_symbol, ascii_code_letter, ascii_code_number};
//check the input image size and return a defined size,
// that's the max one of width and height is not bigger than 100;
cv::Size get_board_size(Mat &image)
{
if(image.empty())
return Size(0,0);
int f = 0;
float big=(float)image.rows, smal=(float)image.cols;
if(image.cols>image.rows)
{
f = 1;
big = float(image.cols);
smal = float(image.rows);
}
cv::Size board_size;
if(big <= 100.f)
{
board_size = cv::Size(image.cols, image.rows);
}
else
{
board_size = cv::Size(int(f==1?100:(100*image.cols/big)), int(f==1?(100*image.rows/big):100));
}
return board_size;
}
//get the char code step
int get_char_size(const string &asciiStr)
{
//init font
int fOntFace= FONT_HERSHEY_PLAIN;
double fOntScale= 0.5;
int thickness = 1;
//max_size is the max of all the char code width and height,
//or return the average size of all the char code width and height;
int max_size = 0;
int total_size = 0;
for (size_t i=0; i2 || code_type<0)
return Mat();
//if input is a gray image, set color_type to gray;
if(image.channels()==1&&code_type==2)
color_type = 1;
//create the output image
int char_size = get_char_size(ascii_code_8[code_type]);
cv::Size board_size = get_board_size(image);
Mat out_image(char_size*board_size.height, char_size*board_size.width, CV_8UC3,Scalar::all(0));
//resize the input image to defined size;
Mat resized_image;
resize(image, resized_image, board_size);
Mat gray_image;
if(resized_image.channels() == 3)
cvtColor(resized_image, gray_image, COLOR_BGR2GRAY);
else
gray_image = resized_image;
//font init
int fOntFace= FONT_HERSHEY_PLAIN;
double fOntScale= 0.5;
int thickness = 1;
//print char code to the output image
for (int i=0; i(i);
uchar *ptr_gray = gray_image.ptr(i);
for (int j=0; j>5)]);
//colored or not
if (color_type==0)
{
putText(out_image, text, textOrg, fontFace, fontScale, Scalar::all(255));
}
else if(color_type==1)
{
putText(out_image, text, textOrg, fontFace, fontScale, Scalar::all(pix_gray));
}
else
{
putText(out_image, text, textOrg, fontFace, fontScale, Scalar(ptr_bgr[3*j],ptr_bgr[3*j+1],ptr_bgr[3*j+2]));
}
}
}
return out_image;
}
下面是采用数字的ascii字符的彩色和灰度以及二值lena图像,【对lena图像来说数字的看起来比较好,其他两种效果不如数字的】
可以看出效果还是比较好的,O(∩_∩)O,
当然这里只是简单的将像素进行了替换是基于灰度的,更高级的可以有基于结构的,如边缘,生成更加简练的内容表达,以后有时间再研究研究~~