I have been idle for the past two days. I think it is interesting to display the image content in ASCII code, so I tried to write it myself, I found it online to know that it was still an art-ASCII art !!
I am also tied to art ~
In this case, the image content is displayed in ASCII code. Different characters are used to represent the gray level, so as to distinguish different gray level values to visualize the content, this principle is very simple, but it is not difficult to implement it. If you directly enter the console or txt file, it is relatively simple. It is mainly necessary to set the font width and height, preferably square, otherwise it looks awkward. The following code is used to scale lena to 100*100 and then display it directly in the console window. The characters used are punctuation marks, and the width and height of the font are equal to 8*8:
However, this method cannot be saved as an image (of course, it can be, but it is so hard !), So I studied how to write the characters directly to the image so that the image can be saved. At this time, the key question is how to write the characters to the image, and try to make the width and height of the characters equal, so that it does not seem to feel that the image has been squeezed through the sweat!
We still use OpenCV to write characters to the image and use the putText function. However, the font in OpenCV does not seem to be set to equal width or height. If there is no way, we take the compromise method, the width and height of all characters used to replace the gray scale are averaged as the character width and height, so that a pixel in the original image is replaced with such a character, in addition, you have also set several options: You can select punctuation, letters or numbers to replace grayscale, and you can choose to output binary, grayscale or color ascii images-only eight grayscale images are used in the text, to quantify the entire image to eight gray levels, the disadvantage is that the distortion of images with relatively concentrated gray distributions is relatively large. You can perform gray-scale equalization before conversion, this is not taken into account here. below is the main function code.
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 stepint get_char_size(const string &asciiStr){//init fontint 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; i<asciiStr.size(); ++i){int baseline = 0;string str(1, asciiStr[i]);cv::Size textsize = getTextSize(str, fontFace, fontScale, thickness, &baseline);if(max_size < textsize.height+baseline)max_size = textsize.height+baseline;if(max_size < textsize.width)max_size = textsize.width;total_size += (textsize.height+textsize.width+baseline);}return total_size/(asciiStr.size()*2);//return max_size;}/*convert image to ascii image;code_type: 0-symbol,1-letter,2-numbercolor_type: 0-binary,1-gray,2-color*/Mat image_to_ascii(Mat &image, int code_type, int color_type){//check input imageif(image.empty() || code_type<0 || code_type>2 || 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 imageint 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);elsegray_image = resized_image;//font initint fontFace = FONT_HERSHEY_PLAIN;double fontScale = 0.5;int thickness = 1;//print char code to the output imagefor (int i=0; i<resized_image.rows; ++i){uchar *ptr_bgr = resized_image.ptr<uchar>(i);uchar *ptr_gray = gray_image.ptr<uchar>(i);for (int j=0; j<resized_image.cols; ++j){//get pixeluchar pix_gray = ptr_gray[j];//prepare the char code and coordinate;cv::Point textOrg(j*char_size, (i+1)*char_size);string text(1, ascii_code_8[code_type][8-(pix_gray>>5)]);//colored or notif (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;}
The following is an image of color, gray, and binary lena using ascii characters of numbers. [for lena images, numbers look better, and the other two are less effective than numbers]
We can see that the effect is still relatively good, O (∩ _ ∩) O,
Of course, here we simply replace the pixels based on the gray level. More advanced features include structure-based, such as edge, to generate more concise content expressions, I will study it later ~~