引言

看到酷安上有这样一个活动,萌生了用 C# 生成字符画的想法,先放出原图。

 
酷安手绘牛啤
 
 

§1 黑白

将图像转换成字符画在 C# 中很简单,思路大致如下:

  1. 加载图像,逐像素提取明度。
  2. 根据明度映射到字符列表中对应的字符。
  3. 输出字符。

GetChars函数负责将传入的图像按一定比例导出字符画的字符串。hScale为横向比例,即每次跳过的横向像素数;vScale为纵向比例,在控制台中输出推荐为hScale的 2 倍。

private static string GetChars(Bitmap bmp, int hScale, int vScale)
{
StringBuilder sb = new StringBuilder();
for (int h = 0; h < bmp.Height; h += vScale)
{
for (int w = 0; w < bmp.Width; w += hScale)
{
Color color = bmp.GetPixel(w, h);
float brightness = color.GetBrightness(); // 这里的明度也可以使用 RGB 分量合成
char ch = GetChar(brightness);
sb.Append(ch);
}
sb.AppendLine();
}
return sb.ToString();
}

GetChar负责做明度到字符的映射工作,由于brightness取值范围为 [0, 1],所以需要乘 0.99 防止index越界。listChar是可用的字符列表,自定义只需遵循一条规则,从左往右字符应该越来越复杂。

private static readonly List<char> listChar =
new List<char>() { ' ', '^', '+', '!', '$', '#', '*', '%', '@' };
private static char GetChar(float brightness)
{
int index = (int)(brightness * 0.99 * listChar.Count);
return listChar[index];
}

调用函数,输出结果。初具雏形,黑白样式减少了不少神韵。

 
 

§2 有限彩色

2.1 Console

一开始希望通过改变Console.ForegroundColor属性来改变色彩,但是残酷的事实是这个属性只接受ConsoleColor枚举中的 16 个颜色。将全彩图片映射成 16 色输出,费力不讨好,遂求其他方法。

2.2 Colorful.Console

找到了一个彩色控制台的库 Colorful Console。看网页介绍挺厉害的,RGB、渐变色、多色输出……妥了,这肯定符合我们的需要,通过 nuget 可以直接添加到项目中。

在引用区域加一行,就可以把代码中的ConsoleColorfulConsole替代。

using Console = Colorful.Console;

GetChars函数需要改变一下,因为每个字符的颜色不同,所以要在函数里面增加输出。好简单,输出内容后面加个颜色的参数就可以了。

private static string GetChars(Bitmap bmp, int hScale, int vScale, bool shouldDraw)
{
StringBuilder sb = new StringBuilder();
for (int h = 0; h < bmp.Height; h += vScale)
{
for (int w = 0; w < bmp.Width; w += hScale)
{
Color color = bmp.GetPixel(w, h);
float brightness = color.GetBrightness();
char ch = GetChar(brightness);
if (shouldDraw)
{
Console.Write(ch, color);
}
sb.Append(ch);
}
if (shouldDraw) { Console.WriteLine(); }
sb.AppendLine();
}
return sb.ToString();
}

然而现实再一次残酷起来,输出结果一片黑,使用白色背景看一看。

 
 

可能看不清,不过牛角的位置确实有几个字符不是黑色,那我们换张图片来看。可以看到确实有彩色输出,不过效果尚可的仅限最前面的一些字符,之后白色完全不见了。

 
 

在测试官网上的操作都没有问题后,我陷入了深深的思考,NMD,为什么?直到我看到了官网上最下面的一段话。

Colorful.Console can only write to the console in 16 different colors (including the black that's used as the console's background, by default!) in a single console session. This is a limitation of the Windows console itself (ref: MSDN), and it's one that I wasn't able to work my way around. If you know of a workaround, let me know!

Colorful.Console只能同时输出 16 种颜色,果然原版Console能接受的ConsoleColor枚举也是 16 种颜色是算计好的。可恶,难道只能到此为止了吗?

我不甘心。

§3 全彩

终于,我找到了这个 visual studio - Custom text color in C# console application? - Stack Overflow。在下面 Alexei Shcherbakov 和 Olivier Jacot-Descombes 的回答中,我看到了希望。

Since Windows 10 Anniversary Update, console can use ANSI/VT100 color codes

You need set flag ENABLE_VIRTUAL_TERMINAL_PROCESSING(0x4) by SetConsoleMode

Use sequences:

"\x1b[48;5;" + s + "m" - set background color by index in table (0-255)

"\x1b[38;5;" + s + "m" - set foreground color by index in table (0-255)

"\x1b[48;2;" + r + ";" + g + ";" + b + "m" - set background by r,g,b values

"\x1b[38;2;" + r + ";" + g + ";" + b + "m" - set foreground by r,g,b values

Important notice: Internally Windows have only 256 (or 88) colors in table and Windows will used nearest to (r,g,b) value from table.

有了这个神奇的ENABLE_VIRTUAL_TERMINAL_PROCESSING(0x4),就可以随意修改前后景颜色了。说干就干,首先需要增加一个NativeMethods类,用来 Call kernel32.dll里的 3 个函数。

using System;
using System.Runtime.InteropServices; namespace Img2ColorfulChars
{
internal class NativeMethods
{
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetConsoleMode(IntPtr hConsoleHandle, int mode); [DllImport("kernel32.dll", SetLastError = true)]
public static extern bool GetConsoleMode(IntPtr handle, out int mode); [DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr GetStdHandle(int handle);
}
}

然后在主程序Main函数里一开始增加以下三行,-11代表STD_OUTPUT_HANDLE(GetStdHandle function - Windows Console | Microsoft Docs), 0x4就是上面所说的ENABLE_VIRTUAL_TERMINAL_PROCESSING

var handle = NativeMethods.GetStdHandle(-11);
NativeMethods.GetConsoleMode(handle, out int mode);
NativeMethods.SetConsoleMode(handle, mode | 0x4);

因为我们要修改的是字符的前景色,所以把上一节中GetChars函数里的

Console.Write(ch, color);

替换为

Console.Write($"\x1b[38;2;{color.R};{color.G};{color.B}m{ch}");

输出结果如下,完美。

 
 

尾声

多彩的细节,巧妙的象征,这就是青春啊(不是)。

而这个项目真正的用法:

 
 

项目链接

推荐阅读

作者:Kabuto_W
链接:https://www.jianshu.com/p/8a083421c11d
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

来自多彩世界的控制台——C#控制台输出彩色字符画的更多相关文章

  1. [笔记]Go语言在Linux环境下输出彩色字符

    Go语言要打印彩色字符与Linux终端输出彩色字符类似,以黑色背景高亮绿色字体为例: fmt.Printf("\n %c[1;40;32m%s%c[0m\n\n", 0x1B, & ...

  2. c++ 在控制台用 wcout输出宽字符的问题

    在我的电脑上要想通过 std::wcout输出 宽字符 需加入以下代码 #include <io.h> #include <fcntl.h> void main() { _se ...

  3. echo 输出彩色字符

    借助echo的-e选项来实现,语法格式为 echo -e "\033[3xmsome things you want to print out.\033[0m" \033[3xm为 ...

  4. 如何在浏览器控制台(console)里输出彩色样式调试信息

    console.log(XX,XX,XX) log 的第一个参数声明第二.第三个参数的作用,第二个参数就是样式,第三个参数是要输出的字符串 console.log("%c%s", ...

  5. Python同时向控制台和文件输出日志logging的方法 Python logging模块详解

    Python同时向控制台和文件输出日志logging的方法http://www.jb51.net/article/66756.htm 1 #-*- coding:utf-8 -*- 2 import ...

  6. Java 控制台输入数字 输出乘法表(代码练习)

    最近,回忆了一些刚学习Java时经常练习的一些小练习题.感觉还是蛮有趣的,在回顾时想起好多学习时的经历和坎坷,一道小小的练习题要研究半天,珍重过往,直面未来.下面贡献代码,Java 控制台输入数字 输 ...

  7. 双缓冲解决控制台应用程序输出“闪屏”(C/C++,Windows)

    使用 C 语言编写游戏的小伙伴们想必起初都要遇到这样的问题,在不断清屏输出数据的过程中,控制台中的输出内容会不断地闪屏.出现这个问题的原因是程序对数据处理花掉的时间影响到了数据显示,或许你可以使用局部 ...

  8. 改变 C/C++ 控制台程序的输出颜色和样式

    我们经常可以看见Linux自带终端下的许多程序都输出了不同颜色和底纹的字体.最近也想要自己实现一下这种效果,方法是在输出流中插入占位符\033[***. 我从网上收集了一些常用的控制语句,并用以下代码 ...

  9. VC printf输出彩色字体

    在VC下使用SetConsoleTextAttribute()函数可以改变当前控制台的前景色和背景色,从而达到输出彩色字体的效果. 使用的方法也很简单,具体代码如下: #include <win ...

  10. 有趣的console.log(console.log输出彩色字,图片等)

    亲们支持我的新博客哦==>原文地址 ) 逛网站的时候经常发现很多网站控制台打印了很好玩的内容 比如我的网站 →calamus 或者知乎→ 平时是不是只用console调试或者打印别的信息了,没有 ...

随机推荐

  1. 比nestjs更优雅的ioc:跨模块访问资源

    使用ts的最佳境界:化类型于无形 在项目中使用ts可以带来类型智能提示与校验的诸多好处.同时,为了减少类型标注,达到化类型于无形的效果,CabloyJS引入了ioc和依赖查找的机制.在上一篇文章中,我 ...

  2. Centos7、CentOS8、CentOS9 修改硬盘分区大小扩充root分区大小

    Centos7 修改硬盘分区大小,实现CentOS无损分区扩容 扩充root分区大小 安装了CentOS7 开发环境及软件后,发现root分区已经才剩下不到1G空间,难不成要干掉重装,OMG,NO! ...

  3. Web前端 -- NPM包管理器

    初始化: #建立一个空文件夹,在命令提示符进入该文件夹 执行命令初始化 npm init #按照提示输入相关信息,如果是用默认值则直接回车即可. #name: 项目名称 #version: 项目版本号 ...

  4. JavaScript中数值小知识

    1. 数值10.0 这种类似的会被去掉数值后的0 之所以这样是因为,整数的存储空间占用比浮点数小,当一个数值不是真浮点数(即10.0这种格式),会被转换为整数10,如果业务中有一些需求需要进行数值位数 ...

  5. 基于开放共享的自主研发—MaxCompute 持续增强生态与开放性建设

    简介: MaxCompute 是阿里巴巴自研的云原生数据仓库,同时也兼容大部分大数据生态系统.一个平台无法实现所有功能和解决所有问题,MaxCompute 需持续增强生态与开放性建设,方能走得更远. ...

  6. 从图森未来的数据处理平台,看Serverless工作流应用场景

    4月,阿里云Serverless工作流正式商业化,这是一款用于协调多个分布式任务执行的全托管 Serverless 云服务.产品致力于简化开发和运行业务流程所需要的任务协调.状态管理以及错误处理等繁琐 ...

  7. 高德AR驾车导航解决方案

    简介: 高德从2018年首创了车载AR导航后,已经先后在后视镜.智能车盒.前装整车厂.后装车机产品.行车记录仪等众多场景落地应用,搭建了非常完整的AR导航生态. 日前,高德地图最新发布了v10.60新 ...

  8. C# 二进制数组与结构体的互转

    本文将告诉大家在 dotnet 里面的二进制基础处理知识,如何在 C# 里面将结构体数组和二进制数组进行相互转换的简单方法 尽管本文属于基础入门的知识,但是在阅读之前还请自行了解 C# 里面的结构体内 ...

  9. WinForms 使用 Image 的 FromFile 方法加载文件和使用 Bitmap 有什么不同

    本文来告诉大家使用 GDI+ 的 Image.FromFile 加载图片文件和使用创建 Bitmap 传入图片文件有什么不同 如使用下面代码加载图片 using var image = Image.F ...

  10. 五:瑞芯微RV1109

    瑞芯微RV1109是一款用于工控机或人工智能视觉应用的高性能机器视觉处理器SoC. 参考资料 http://www.neardi.com/news_22/434.html https://www.ro ...