LINQ to ASCII Art






4.81/5 (33投票s)
使用 LINQ 技术将图像转换为 ASCII 艺术。
引言
我看到了很多解释如何从图像生成 ASCII 艺术的文章,但我没有看到任何文章以 LINQ 的风格实现同样的功能。 因此,我将解释如何使用 LINQ 技术来组合一些函数以实现我们的目标。
背景
简而言之,ASCII 艺术指的是基于文本的视觉艺术。 所以我们可以使用 ASCII 代码创建很酷的艺术品,从简单的 ^_^ :) 到稍微高级的东西,例如
欲了解更多信息,我建议您查看此链接。
众所周知,语言集成查询 (LINQ) 是一种强大的技术,用于组合、查询和转换不同的数据源,所有这些都在您的代码中完成。
我认为 ASCII 艺术是组合和转换一些函数以生成美丽的东西。
Using the Code
LINQ 到 ASCII 艺术非常直接。 在本节中,我将详细解释每个函数。
获取像素
此函数的主要目的是获取指定图像的实际像素,因此它接受位图,使用该图像的宽度和高度水平和垂直地迭代其所有像素,并使用 GetPixel
函数获取具有特定坐标的像素。 然后,返回值是实际像素作为 IEnumerable(Of Color)
,以便以后可以使用 LINQ 轻松处理这些像素。 它的代码如下
Public Function GetPixels(bmp As Bitmap) As IEnumerable(Of Color)
Return From x In Enumerable.Range(0, bmp.Width - 1)
From y In Enumerable.Range(0, bmp.Height - 1)
Select bmp.GetPixel(y, x)
End Function
灰度像素
此函数的主要目的是将实际像素转换为灰度,因此它接受大量的像素,迭代它们并应用以下公式
Gray Scale = 0.3 * Red + 0.59 * Green + 0.11 * Blue
有关灰度的更多信息,请查看此链接。
然后,返回值是灰度像素作为 IEnumerable(Of Color)
,以便以后可以使用 LINQ 轻松处理这些像素。 它的代码如下
Public Function GrayScalePixles_
(pixels As IEnumerable(Of Color)) As IEnumerable(Of Color)
Return From p In pixels
Let g = 0.3 * p.R + 0.59 * p.G + 0.11 * p.B
Select p = Color.FromArgb(g, g, g)
End Function
获取字符
此函数的主要目的是获取与灰度像素值对应的字符。 顺便说一句,我要感谢某人,我不记得他的名字了:),感谢他尝试获取代码中的底层字符,但您可以使用任何合适的集合。 因此,其背后的想法是检查灰度颜色的亮度,它是该像素的任何一个 RGB 的值。 例如,如果值为 230,则表示像素变为白色,如果小于 50,则表示像素变为黑色,因此从亮度到黑暗的想法很简单。 然后返回值是应该打印出来的字符 (ASCII),它们是 IEnumerable(Of Char)
,以便以后可以使用 LINQ 轻松处理这些字符。 它的代码如下
Public Function GetChars_
(pixels As IEnumerable(Of Color)) As IEnumerable(Of Char)
Return From p In pixels
Select If(p.R >= 230, " "c, If(p.R >= 200, _
"."c, If(p.R >= 180, "*"c, If(p.R >= 160, _
":"c, If(p.R >= 130, "o"c, If(p.R >= 100, _
"&"c, If(p.R >= 70, "8"c, _
If(p.R >= 50, "#"c, "@"c))))))))
End Function
最后一件事是使用 LINQ 将它们组合起来,如下所示
Dim img As Image = Image.FromFile("Hisham.jpg")
Dim bmp As New Bitmap(img, 150, 150)
Dim parts = GetChars(GrayScalePixles(GetPixels(bmp))).Chunk(bmp.Width _
- 1).Select(Function(part) String.Join("", part))
Dim data = String.Join(vbCrLf, parts)
File.WriteAllText("test.txt", data)
最后,我想向您展示我们可以更进一步,并将它们全部组合到一个巨大的 LINQ 查询中,如下所示
Dim data = String.Join(vbCrLf, (From x In Enumerable.Range(0, bmp.Width - 1)
From y In Enumerable.Range(0, bmp.Height - 1)
Let p = bmp.GetPixel(y, x)
Let g = 0.3 * p.R + 0.59 * p.G + 0.11 * p.B
Let v = Color.FromArgb(g, g, g)
Select If(v.R >= 230, " ", If(v.R >= 200, ".", _
If(v.R >= 180, "*", If(v.R >= 160, ":", _
If(v.R >= 130, "o", If(v.R >= 100, "&", _
If(v.R >= 70, "8", If(v.R >= 50, "#", _
"@"))))))))).Chunk(bmp.Width - 1).Select(Function(part) _
String.Join("", part)))
最后,我想提一下,你们中的一些人说最后一个查询非常昂贵并且需要很多时间。 当然,我同意你的观点,因为有很多像素需要进行大量处理,无论如何,我们可以使用 AsParallel()
方法来提高性能,我们也可以使用新的 async
修饰符来消除线程阻塞。
关注点
令人惊讶的是,我们已经看到了如何在 LINQ 风格中生成 ASCII 艺术,这是一件很棒的事情 :) 并且为许多进一步的事情(例如图像处理)敞开了思路。 我们看到了如何使用简单的 LINQ 查询灰度化图像,并且我们可以做更多的事情,反转,翻转,...等等。