如何从特定文本组成图像?

如何从特定文本组成图像?

我喜欢玩 ASCII 艺术,正如你所知,有一些工具可以从图像生成 ASCII 艺术,其中 ASCII 由那些可以产生最佳图像的字符组成,如第一张图片所示:

眼睛 ASCII 艺术

通过眼睛,您需要具有大量白色的 ASCII 字符(例如点、逗号、连字符)来生成较亮的区域,以及具有大量黑色的字符(例如 M、$)来生成较暗的区域。

现在,我想要实现的是能够控制组成图像的人物。下图就是一个示例,其中的故事形成了一只乌鸦的形状。

在此处输入图片描述

但是这只乌鸦只是平的,没有眼睛那样的深度。我想把眼睛和乌鸦的例子结合起来。

是否可以通过对每个字符使用不同的灰色阴影或不同的大小来实现相同的效果?因此,对于较亮的区域,请使用浅灰色或小黑色字符,对于较暗的区域,请使用黑色和大字符。

这有意义吗?

是否可以使用 LaTeX 以自动化的方式实现?

答案1

这个答案需要 lualatex 和图像魔法或类似程序。您必须将彩色图片转换为,.ppm将灰度图片转换为.pgm,这非常容易处理。您可以使用以下命令保持纵横比:

convert -compress none -resize ___OutputWidth___ ___InputFile___ ___output___.ppm
convert -compress none -resize ___OutputWidth___ ___InputFile___ ___output___.pgm

警告:这是一个漫长的过程,处理较大的图像会花费大量时间。

代码如下:

文本文件

\documentclass{article}
\usepackage{luacode}
\usepackage{xcolor}
\usepackage{graphicx}

\directlua {require "asciiart"}
\newcommand\bwascii[1]{\directlua{bwascii("#1")}}
\newcommand\colorascii[1]{\directlua{colorascii("#1")}}
\newcommand\bwframe[1]{\vspace*{\fill}\bwascii{#1}\vspace*{\fill}\newpage}
\newcommand\colorframe[1]{\vspace*{\fill}\colorascii{#1}\vspace*{\fill}\newpage}

\begin{document}
\ttfamily\frenchspacing
\newlength{\correctem}\settowidth{\correctem}{M}%to set the size of the minipage
\newlength{\correctex}\settowidth{\correctex}{x}%to set the line height
\pagestyle{empty}\centering

\colorframe{lenna_128x128.ppm}
\colorframe{lisa_150x224.ppm}
\colorframe{knuth_192x227.ppm}
\bwframe{einstein_150x206.pgm}
\end{document}

asciiart.lua

colorascii = function ( picture )
  -- read a picture in .ppm format
  local file = io.open(picture, "r")
  if file==nil then
    tex.sprint("file not found")
    return
  end
  local arr = {}
  for line in file:lines() do
    if line:sub(1,1)~="#" then--if line starts with # don't insert it
      table.insert(arr,line);
    end
  end
  file:close()
  if arr[1]~="P3" then
    tex.sprint("i don't like this file")
    --TODO: implement moar ways to detect if the file is corrupt
    return
  end
  local sizes = {}
  for i in string.gmatch(arr[2],"%d+") do
    table.insert(sizes,i)
  end
  local xsize = tonumber(sizes[1])
  local ysize = tonumber(sizes[2])
  table.remove(arr,1)--remove "P3"
  table.remove(arr,1)--remove sizes
  table.remove(arr,1)--remove maxval and assume no value is bigger than 255
  colors = ""
  for k,v in pairs(arr) do
    colors = colors..v.." "
  end
  colors = string.gsub(colors, " +", " ")
  --now all our picture is in a single string
  rgb = {}
  for i in string.gmatch(colors,"%d+ %d+ %d+") do
    temp = {}
    for j in string.gmatch(i, "%d+") do
      table.insert(temp,j)
    end
    table.insert(rgb, temp)
  end
  tex.sprint("\\noindent\\resizebox{\\textwidth}{!}{")
  tex.sprint("\\noindent\\begin{minipage}{"..xsize.."\\correctem}\\setlength\\baselineskip{1\\correctex}\\setlength\\lineskip{0pt}\\setlength\\prevdepth{0pt}")
  for i = 1,#rgb do
    tex.sprint("\\definecolor{mycolor}{RGB}{"..rgb[i][1]..","..rgb[i][2]..","..rgb[i][3].."}\\textcolor{mycolor}x\\hspace{0pt}")
  end
  tex.sprint("\\end{minipage}}")
end

valchar = function (val)--takes an integer from 0 to 255 and returns a character
  val = tonumber(val)
  valuetable = {"\\$","B","Q","Y","v","~","."," "}--return darker characters for darker values
  return valuetable[math.floor(val/32)+1]
end

bwascii = function ( picture )
  local file = io.open(picture, "r")
  if file==nil then
    tex.sprint("file not found")
    return
  end
  local arr = {}
  for line in file:lines() do
    if line:sub(1,1)~="#" then
      table.insert(arr,line);
    end
  end
  file:close()
  if arr[1]~="P2" then
    tex.sprint("i don't like this file")
    return
  end
  local sizes = {}
  for i in string.gmatch(arr[2],"%d+") do
    table.insert(sizes,i)
  end
  local xsize = tonumber(sizes[1])
  local ysize = tonumber(sizes[2])
  table.remove(arr,1)
  table.remove(arr,1)
  table.remove(arr,1)
  greys = ""
  for k,v in pairs(arr) do
    greys = greys..v.." "
  end
  greys = string.gsub(greys, " +", " ")
  value = {}
  for i in string.gmatch(greys,"%d+") do
    table.insert(value, i)
  end
  tex.sprint("\\noindent\\resizebox{\\textwidth}{!}{")
  tex.sprint("\\noindent\\begin{minipage}{"..xsize.."\\correctem}\\setlength\\baselineskip{1\\correctex}\\setlength\\lineskip{0pt}\\setlength\\prevdepth{0pt}\\leavevmode")
  for i = 1,#value do
    tex.sprint("\\smash{"..valchar(value[i]).."}\\hspace{0pt}")
  end
  tex.sprint("\\end{minipage}}")
end

示例输出(下载pdf放大):

莱娜 丽莎 德克 爱因斯坦

警告:对于较大的图像,可能需要比您预期更长的时间。

这些是原始图片:莱娜丽莎克努特爱因斯坦

如果您不想安装 imagemagick,请转换为 .ppm/.pgm:莱娜丽莎克努特爱因斯坦

虽然这可能不是确切地你想要的(乌鸦,改变字体大小和粗细……),这是一个开始。据我所知,没有其他方法可以用 latex 生成 ascii 艺术。

相关内容