Quantcast
Channel: youwhtの記事 - Qiita
Viewing all articles
Browse latest Browse all 29

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

$
0
0

要約

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

  • 兎に角、下記の作例集を見れば何がしたいのかが分かる。まずは見てね
  • 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 (チャー・フイター)は架空の人物です。


Viewing all articles
Browse latest Browse all 29

Trending Articles