Help us understand the problem. What is going on with this article?

文字で、文字や絵を書く技術

More than 1 year has passed since last update.

要約

あ…ありのまま 今 思った事を話すぜ!
「文字が文字で作れたら面白いよね?」
何を言っているのか わからねーと思うが、
おれも 何を言っているのか分からない。

  • 兎に角、下記の作例集を見れば何がしたいのかが分かる。まずは見てね
  • Colaboratoryで、前提一切不要&ブラウザだけですぐ動かせるよ

おれは 奴の前で文字を書いていたと思ったら
いつのまにか絵を書いていた。と思ったらやっぱり文字を書いていた。
頭がどうにかなりそうだった

作例集①

殺伐としたウニ

ebikani.png

これがホントの「エビカニ、クス(笑)」

殺伐としたスレに鳥取県が!!

satubatu_gunma.png

島根県 ( ※「矛盾塊」と呼ばれているらしい)

瀧「リューク、目の取引だ」

kiminona.png

アイドルの方の三葉が死ぬっ!

EVA

syogouki.png

こんなとき、どんな顔をしたらいいかわからないの


ごめんなさい。作例集を見ても
何がしたいのか」は分からなかったかもしれない。
「何が出来るようになるのか」は分かったと思う。
作例集②も最後にあるよ。

全体的な設計

逆に考えるんだ。

文字(エビ)で絵を書くためには、
文字(エビ)を書く座標が決まっていれば良い。
書く場所の座標 = 0と1で出来た二次元リスト。
二次元リスト = 白黒画像(グレースケール)

あとは、フレームとなる文字(カニ)を画像化して、
その白黒画像に入れれば完成。
まとめると、以下のような流れになる。

カニ ⇒ 画像化 ⇒ 白黒画像 ⇒ 01二次元リスト ⇒ エビで埋める

↑とても技術解説とは思えない説明文字列だ

◆さあ、以下の段取りで開発を進めよう!

  • 開発環境構築=不要(Colaboratory)
  • Step1 文字を画像にする技術
  • Step2 画像を白黒の01リストにする技術
  • Step3 白黒リストを文字で埋め尽くす技術
  • Step4 出来た関数のまとめ&最終的に画像に変換

開発環境構築=不要(Colaboratory)

今回は Colaboratory 上で、Python3 によって実装してみる。
ColaboratoryはGoogle様が用意してくれた
Jupyter&Pythonを簡単に実行出来る神環境
ブラウザでアクセスするだけですぐに本記事のコードが試せる。
お手元の環境を汚さない。エコ仕様。

全コード掲載&すぐにコピペ実行出来るようになっているので、
ぜひオリジナルの文字絵アート文字文字アートを作ってみてください!

(*´ω`)つ Colaboratory

Step1 文字を画像にする技術

準備:日本語フォントのインストール

Colaboratoryでは、最初に「!」をつけると
シェルコマンドの実行が出来る。
画像にしちゃう日本語フォントをインストールしてみよう。

Colaboratoryで日本語フォントのインストール
!apt-get -y install fonts-ipafont-gothic

インストールされたフォントのパスを確認してみよう。

TTFファイルのパスを確認する
import matplotlib.font_manager as fm
fonts = fm.findSystemFonts()
for font in fonts:
  print(str(font), "  ",fm.FontProperties(fname=font).get_name())

# 出力は省略。こんなパスの場所を確認出来る
# /usr/share/fonts/truetype/fonts-japanese-gothic.ttf

文字列を画像にする関数

Pythonの画像処理ライブラリ(Pillow)で
白色背景画像に文字を書き込み、
全体を画像として保存する。
これで、好きな「文字」を「画像」に出来る。

文字列を画像にする関数
from PIL import Image, ImageDraw, ImageFont
## 与えられた文字列を、画像にする関数
## 1文字あたりのサイズ&縦横の文字数も引数で指定
def str2img(input_str, yoko_mojisuu, tate_mojisuu, moji_size):
  # 真っ白な背景画像を生成する
  # 横(縦)幅 = 文字サイズ× 横(縦)文字数
  img  = Image.new('RGBA', (moji_size * yoko_mojisuu , moji_size * tate_mojisuu), 'white')
  # 背景画像上に描画を行う
  draw = ImageDraw.Draw(img)

  # フォントの読み込みを行う。(環境によって異なる)
  myfont = ImageFont.truetype("fonts-japanese-gothic.ttf    /usr/share/fonts/truetype/fonts-japanese-gothic.ttf", moji_size)

  # 文字を書く。基本は以下で済むが、今回は1文字ずつ記入
  # draw.text((0, 0), input_str , fill=(0, 0, 0), font = myfont)
  # ※備考:1文字ずつ記入の場合、半角と全角を区別しないといけなくなる
  # (今回は全角前提とする)
  # fillは、文字の色をRBG形式で指定するもの。今回は黒なので0,0,0固定
  # 縦横のサイズに合せて1文字ずつ描画
  yoko_count = 0
  tate_count = 0
  for char in input_str:
    #縦の文字数の許容量を途中でオーバーしてしまった場合は終了
    if tate_count >= tate_mojisuu:
      break
    #所定の位置に1文字ずつ描画
    draw.text( ( yoko_count * moji_size, tate_count * moji_size ), char, fill=(0, 0, 0), font = myfont)
    yoko_count +=1
    if yoko_count >= yoko_mojisuu:
      yoko_count =  0
      tate_count += 1

  return img

出来た関数は以下のように使える

str2img関数のお試し実行
import matplotlib.pyplot as plt
img = str2img("勝利友情努力", 2, 3, 50)
plt.imshow(img)

出力結果:
勝利友情努力.PNG

「三本柱マン」が無事降臨!!

なお、以前に、
どこでもドアを作ってみた物語
においてもPillowで画像加工を実施したことがある。
文字だけでなく画像の合成等も可能だ。

Step2 画像を白黒の01リストにする技術

「文字」の画像の場合もともと白黒なのだが、
任意の画像を文字で表現することにも対応するため、
まず画像を「白黒化」し、各ピクセルを0~1の少数で表現する。

そして、閾値(その画像全体の平均値とする)と比較して
白い場合は「1」黒い場合は「0」にすれば、
あらゆる画像が「1」と「0」の2次元リストになるというわけ。

画像の白黒化&01リスト化
from PIL import Image, ImageDraw, ImageFont
# 与えた画像を、グレースケールのリストに変換する関数(白=1、灰=0.5、黒=0)
# 元がカラー画像でも対応出来るようにしている
def img2graylist(input_img):
  #幅と高さを取得する
  img_width, img_height = input_img.size
  print('幅 : ', img_width)
  print('高さ: ', img_height)

  #最終的に出力する二次元リスト
  result_graylist = []
  for y in range(0, img_height, 1):
    # 1行ごとのテンポラリリスト
    tmp_graylist=[]
    for x in range(0, img_width, 1):
      # 1ピクセルのデータ(RGB値)を取得
      #(20, 16, 17, 255)のように4つのデータが取れる⇒3つに絞って使う
      r,g,b, = input_img.getpixel((x,y))[0:3]

      #RGB値の平均=グレースケールを求める
      g = (r + g + b)/3
      tmp_graylist.append(g)
    #1行終わるごとにテンポラリリストを最終出力に追加
    result_graylist.append(tmp_graylist)
  return result_graylist

# 与えたグレイリストを、白=1、黒=0のリストに変換する関数
# 黒が多い画像⇒全て黒、や、色の薄い画像⇒全て白、にならないように、
# 閾値として、平均値を取得した後で、その閾値との大小で判定する
# よって、薄い画像が全部白に、濃い画像が全部黒に、などはならない
import numpy as np
def graylist2wblist(input_graylist):

  #与えられた二次元配列の値の平均値を求める(npを使っても良いが)
  gray_sum_list = []
  for tmp_graylist in input_graylist:
    gray_sum_list.append( sum(tmp_graylist)/len(tmp_graylist) )
  gray_ave = sum(gray_sum_list)/len(gray_sum_list) 
  print("灰色平均値: ", gray_ave)

  # 最終的に出力する二次元の白黒リスト
  result_wblist = []
  for tmp_graylist in input_graylist:
    tmp_wblist = []
    for tmp_gray_val in tmp_graylist:
      #閾値と比べて大きいか小さいかによって1か0を追加
      if tmp_gray_val >= gray_ave:
        tmp_wblist.append(1)
      else:
        tmp_wblist.append(0)
    result_wblist.append(tmp_wblist)

  return result_wblist

出来た関数は以下のように使える

白黒化&01リスト化
#与えられた2次元文字列リストをプリントする関数(pprint的なもの)
#(※最終出力時には使わないが、途中経過を見る用途)
def print2Dcharlist(charlist):
  for tmp_charlist in charlist:
    for char in tmp_charlist:
      #改行無しで出力
      print(char, end="")
    #1行終わるごとに改行
    print()

img = str2img("般若波羅蜜多", 6, 1, 20)
graylist = img2graylist(img)
wblist = graylist2wblist(graylist)

print2Dcharlist(wblist)

出力結果:

hannya_kakunin.PNG

01デジタル化された般若心経

Step3 白黒リストを文字で埋め尽くす技術

作った白黒リストの「0」の部分だけを(または「1」の部分を)
「文字」で置き換えれば、ほとんど完成に近い。

エビエビエビエビエビ・・・と繰り返して文字を取得/出力するため、
Pythonの「ジェネレータ」を使って実装してみる。
yield は return のようなものなので、
return に読み替えると分かりやすいかもしれない(説明雑)

白黒リストを文字で埋め尽くす
# 文字列を一文字ずつ取り出すジェネレータ。半無限ループにより繰り返し
def infinity_gen_str(str):
  for a in range(1000000000):
    for s in str:
        yield s
# 以下のように使う
# 定義:gen_str =  infinity_gen_str("表示したい文字列")
# 使用:next(gen_str)
# これで、使用するたびに1文字ずつ出力される

# 白黒リストの、白黒の部分を文字列で埋め尽くした二次元リストを返す
# 白=soto_strで埋める。黒=nakami_strで埋める。
def wblist2wbcharlist(input_wblist, nakami_str, soto_str):
  # 1文字ずつ出力できるジェネレータの生成
  gen_nakami_str =  infinity_gen_str(nakami_str)
  gen_soto_str =  infinity_gen_str(soto_str)

  # 最終的に出力する二次元の白黒リスト
  result_wbcharlist = []
  for tmp_wblist in input_wblist:
    tmp_wbcharlist = []
    for tmp_wb_val in tmp_wblist:
      # 値が1か0かによって、文字列を入れていく
      # ※空白と等幅になる文字&フォントでやることが望ましい
      if tmp_wb_val == 1:
        # 1が白
        # 空白固定ならコレでも同じ ⇒ tmp_wbcharlist.append( " " )
        tmp_wbcharlist.append( next(gen_soto_str))
      else:
        # 0が黒
        tmp_wbcharlist.append( next(gen_nakami_str) )

    result_wbcharlist.append(tmp_wbcharlist)

  return result_wbcharlist

出来た関数は以下のように使える

01リストを文字列で埋める
img = str2img("般若波羅蜜多", 6, 1, 20)
graylist = img2graylist(img)
wblist = graylist2wblist(graylist)
#print2Dcharlist(wblist)

# 今回は↑の外枠で「般若波羅蜜多」のフレーム(01)を作り、
# ↓の指定で、中身を「般若波羅密多」の文字列で埋める
wbcharlist = wblist2wbcharlist(wblist, "般若波羅蜜多"," ")
print2Dcharlist(wbcharlist)

出力結果:
hannya_kakunin2.PNG

この技術に狂気と恐怖を覚える

Step4 出来た関数のまとめ&最終的に画像に変換

ここまでで、以下の流れの全てが実装できた。
カニ ⇒ 画像化 ⇒ 白黒画像 ⇒ 01二次元リスト ⇒ エビで埋める

最後に、これらの処理のまとめと、
出来たエビのリストを画像にして保存するようにしよう。

最後の画像変換では、最初の「文字を画像化する関数(カニ⇒画像化)」を
再利用することが出来る!

今までの関数の一括実行&画像化
def moji2mojiImg(flame_str, nakami_str, soto_str, yoko_len, tate_len, moji_size, final_moji_size):
  # 引数サンプル
  # flame_str = "般若"
  # nakami_str = "般若波羅蜜多"
  # yoko_len = 2
  # tate_len = 1
  # moji_size = 30
  # 最後に表示する際のフォントサイズ
  # final_moji_size = 12

  img = str2img(flame_str, yoko_len, tate_len, moji_size)
  graylist = img2graylist(img)
  wblist = graylist2wblist(graylist)
  wbcharlist = wblist2wbcharlist(wblist, nakami_str, soto_str)
  # print2Dcharlist(wbcharlist)

  # 作った配列を、str2imgで画像化する
  # 作ったリストを全てつなげて単純文字列にする
  # (※最初に作成したstr2imgに入れるための変換)
  all_str = ""
  for tmp_list in wbcharlist:
    for char in tmp_list:
      all_str += char

  #今回のファイルのサイズは縦横は、moji_size倍されている点に注意
  img = str2img(all_str, yoko_len*moji_size, tate_len*moji_size, final_moji_size)

  return img

出来た関数は以下のように使える

殺伐とした実行
img = moji2mojiImg("カニ","エビ"," ",2,1,20,15)

#正しく表示&ダウンロード出来るように、一度セーブする
img.save("ebikani.png")

#colaboratoryで表示
import IPython
IPython.display.Image("ebikani.png")

出力結果:

ebikani.png

エビもカニも甲殻類

出来た画像をColaboratoryからダウンロードするには以下

セーブしたファイルをローカルにダウンロード
from google.colab import files
files.download("ebikani.png")

(オマケ)画像を文字列で描画する技術

「文字」に文字を埋め込んで画像化することが出来た。
一方で、「画像」に文字を埋め込んで画像化することは、
実はより簡単に出来てしまう。

カニ ⇒ 画像化 ⇒ 白黒画像 ⇒ 01二次元リスト ⇒ エビで埋める

この、最初ステップのカニの画像化がなくなって、
直接画像の白黒化から始められるというだけ。
「アスキーアート」生成ツールの亜種的なものになる。

最後のStep4のまとめ関数をちょっと書き換えて実行してみる。

普段使うには 使わないけど こちらのほうが使いやすいかもしれない

画像を文字で描画する関数
def img2mojiImg(input_img, nakami_str, soto_str, final_moji_size):
  img_width, img_height = input_img.size
  #print('幅 : ', img_width)
  #print('高さ: ', img_height)

  #文字から画像を作る必要なく、input画像を使う
  #img = str2img(flame_str, yoko_len, tate_len, moji_size)
  graylist = img2graylist(input_img)
  wblist = graylist2wblist(graylist)
  wbcharlist = wblist2wbcharlist(wblist, nakami_str, soto_str)
  #print2Dcharlist(wbcharlist)

  #作った配列を、str2imgで画像化する
  #作ったリストを全てつなげて単純文字列にする
  #(※作成したstr2imgに入れるため)
  all_str = ""
  for tmp_list in wbcharlist:
    for char in tmp_list:
      all_str += char

  #今回のファイルのサイズは縦横は、moji_size倍されている点に注意
  img = str2img(all_str, img_width, img_height, final_moji_size)

  return img

出来た関数は以下のように使える
「グンマー」の画像は別途用意して
Colaboratoryにアップロードしておく。
(※左上の「>」から「ファイル」を選ぶと、アップロード出来る)

ぐんまちゃんに怒られないように気をつけて実行
img = Image.open("Gunma.png")
#幅と高さを取得する
img_width, img_height = img.size
print('幅 : ', img_width)
print('高さ: ', img_height)

#リサイズする場合は以下のような感じ
#元画像は幅640、高さ640
img = img.resize((40, 40))

result_img = img2mojiImg(img, " ", "栃木県", 14)

output_file_name = "satubatu_gunma.png"
result_img.save(output_file_name)

#colaboratoryで表示
import IPython
IPython.display.Image(output_file_name)

出力結果:
satubatu_gunma.png

グンマーは何をやっても面白いのでとてもお得

作例集②(オマケ)

はらみった

hannya.png

「写経」を自動化し、オートで功徳を積める仕組みを作ってみたのでございます。

しろくろ

simauma.png

じわじわくる

止まれ。

tomare.png

もう何十回も言ったのよ!?って言える必殺技

見よ、人がゴミのようだっ!

hitogomi.png

「バルス!!」「目がぁ~!目がぁ~!」

新時代アート

reiwa.png

【続】平成の次の元号を、AIだけで決めさせる物語(@テレビ取材)

その…下品なんですが…フフ…勃起…しちゃいましてね…

monakira.png

いいや!限界だ(いいねを)押すね!今だッ!

PythonでHello 世界(ザ・ワールド)止まった時の世界に入門してみる。ジョースターの末裔は必読

あとがき

大喜利

技術を使った大喜利として、ネタを考えるのも楽しいかもしれません。

面白い文字文字アートの案や、作例が出来たら、
ぜひコメント欄に張り付けて教えてください!

応用例/アイデアメモ

応用例はいろいろありそう。

  • 画像認識した部分を対応する文字に変える(車に認識された部分を「車」で表現など)
  • TwitterやSlackのツール/ボット作成、サービス化
  • 単純にアスキーアート生成技術として活用
  • 文字の色を変更してカラフル化
  • 濃淡に合わせて黒い文字/白い文字を使い分ける
  • セリフの無いマンガの作成
  • 薄い灰色で文字を印刷して、幼児/英単語などの書き取り練習帳に(書き終わると絵が浮かび上がる)
  • 究極的に「ざわざわっ...」「ゴゴゴゴゴ」している絵の作成(著作権的な意味で今回はパス)

いつもの名言

ちょっとした遊び & Colaboratoryの実践入門として
楽しんでいただけたら幸いです♪
ブラウザでColaboにアクセス、上から順にコピペしていくだけですぐ試せます。
文字文字アートで一緒に遊びましょう。

人類の進化は「遊び」からはじまる。
こんな「遊び」が出来るならば、というアイデアに触発される人がでて、
生活にも役に立つような「発明」が生まれるのだ。
          ~  Char Fuitter (1847~1912 オランダ) ~

長文おつきあいいただきありがとうございました。


出力結果画像は自由に転載していただいて構いません。
Char Fuitter (チャー・フイター)は架空の人物です。

youwht
「Qiitaの殿堂」(http://youwht.ml/) 「赤の他人の対義語」 「平成の次の元号」 「写経を自動化」 「パワポリント君」etc... wwwxuexihanyu@gmail.com
nri
NRIは「コンサルティング」「金融 ITソリューション」「産業 ITソリューション」「IT 基盤サービス」の4事業でお客様のビジネスや快適な社会、暮らしを支えています。※各記事の内容は個人の見解であり、所属する組織の公式見解ではありません。
https://www.nri.com/jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした