LoginSignup
11
7

More than 5 years have passed since last update.

フォントファイルに収録されてない字を機械学習で生成してみた

Last updated at Posted at 2019-02-21

結果

Screen Shot 2019-02-21 at 20.09.58.png

  • この画像は結果です、この document の。

  • この方法は、 convert することです、 source-han-sans JapaneseGLT-ごぬんね(アニメ「少女終末旅行」副題風フォント) へ。

  • '昨' isn't included in GLT-ごぬんね(アニメ「少女終末旅行」副題風フォント)

  • --max_epochs 45

主に使ったもの

Google Colab

  • https://colab.research.google.com
  • Google user は可能です、 使うことが ( GPU や TPU を) 、 Google Colab 上で。
  • Google user は可能です、しない ( pay することを ) 、 Google Colab へ。
  • ( 警告 ) Google Colabは有します、様々な制約を。
  • 持つことは ( Google account を ) must です。

Google Drive

  • https://www.google.co.jp/drive
  • Google user は使うことが出来ます、 15GB storage ( free 💰 ) を。
  • 持つことは ( Google account を ) must です。

MacBook Air (11-inch, Early 2015)

  • macOS Mojave
  • 1.6 GHz Intel Core i5
  • 8 GB 1600 MHz DDR3

GLT-ごぬんね(アニメ「少女終末旅行」副題風フォント)

adobe-fonts/source-han-sans Japanese (日本語) | HW Regular + HW Bold)

  • https://github.com/adobe-fonts/source-han-sans/tree/release
  • DL & 可能 ( 利用を ) にして下さい、 Font Book App などで。
  • Font file
  • この document の program は convert します、この font を。
  • 筆者はこの font を select しました、なぜなら、多分この font は include しています、多くの字を。

iTerm2

  • https://www.iterm2.com
  • これは must です、 run command のために。
  • iTerm2 open PATH ( or URL ) on Finder or Web browser or some applications when user click to PATH ( or URL ) when key down ⌘.
  • Or use open command in Terminal.app

Homebrew

  • https://brew.sh
  • 可能 ( 利用を ) にして下さい、 one line code で、この Web page 上の。
  • package manager for macOS (or Linux)
  • CLI Tool
  • これを使います、 ImageMagick の install のために。

ImageMagick

  • https://www.imagemagick.org
  • これは有しています、様々な機能を、加工の、 image file のための。
  • CLI Tool
  • brew install imagemagick 可能(利用を)にして下さい、Homebrew などで。

Node.js v8.11.2

  • https://nodejs.org
  • これは must です、 operation するために、ImageMagickを。
  • 筆者は recommend します、 使う ( nodebrew やその他を ) 、 install のために。
  • この programming lang を select しました、なぜなら、筆者が好きだからです、これを。
  • この version は install されていました、筆者の MacBook Air に。
  • 筆者は思います、使う ( 他の programming lang を ) ことは problem ではないと。

affinelayer/pix2pix-tensorflow

Python 3.6.4

  • https://www.python.org
  • これは must です、 affinelayer/pix2pix-tensorflow のために。
  • Python 3.6.7 は install されています、 Google Colab 上に。
  • しかし、 install は ( Python の ) must です、 local への。
  • なぜなら、使います、 local 上で、この document 内で。
  • 筆者は recommend します、 使う ( pyenv やその他を ) 、 install のために。
  • この version は install されていました、筆者の MacBook Air に。

Ml5.js

  • https://ml5js.org
  • machine learning JavaScript library
  • これは must です、 見るために、 training の結果を。
<script src="https://unpkg.com/ml5@0.1.3/dist/ml5.min.js" type="text/javascript"></script>

p5.js

<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js" type="text/javascript"></script>

手順

用意して下さい、環境やその他を。 ( 主に使ったもの base )

移動して下さい。 ( pix2pix-tensorflow ( clone した ) へ )

pushd ./pix2pix-tensorflow

用意して下さい。 ( 学習 data を )

  1. 用意して下さい。 ( 字を )

    • 定義して下さい、 JavaScript 配列を、その字の。
    • 幾つかの字が用意されました、筆者により。
    • 確認して下さい。 ( strArrToImgs.js )
    • Google Search
      • 小学生 1年生 習う漢字 ~ 小学生 6年生 習う漢字
      • 中学生 1年生 習う漢字 ~ 中学生 3年生 習う漢字
  2. Generate して下さい。 ( 字の image を。 )

    1. Save して下さい。 ( strArrToImgs.js を。 )
      1. Copy strArrToImgs.js ( この document の。 )
      2. Run.
        • vim strArrToImgs.js
      3. Put i key.
      4. Paste it.
      5. Put Escape key.
      6. Type :wq
      7. Put Return key. ( or Enter )
    2. Run.
      • node strArrToImgs.js
  3. Delete して下さい。 ( white image を。 )

    1. Save して下さい。 ( imgsCleaner.js を。 )
      1. Copy imgsCleaner.js ( この document の。 )
      2. Run.
        • vim imgsCleaner.js
      3. Put i key.
      4. Paste it.
      5. Put Escape key.
      6. Type :wq
      7. Put Return key. ( or Enter )
    2. Run.
      • node imgsCleaner.js
    3. white image は generate されます、 convert したときに、字を ( 含まれない、その font file に ) 、 image file へ。
    4. それを delete することは must です。
    5. imagemagic の identify command は output します、 65535 を、 import ( white image を ) したとき。
    6. imagemagic の identify command は output します、 0 を、 import ( black image を ) したとき。
  4. 関連付けて下さい、 'a' directory's files と 'b' directory's files を。

    1. Save して下さい。 ( matchingImgs.js を。 )
      1. Copy matchingImgs.js ( この document の。 )
      2. Run.
        • vim matchingImgs.js
      3. Put i key.
      4. Paste it.
      5. Put Escape key.
      6. Type :wq
      7. Put Return key. ( or Enter )
    2. Run.
      • node matchingImgs.js
    3. なぜなら、 幾つかの file が delete されたからです、 3. にて。
  5. Resize して下さい、 'a' directory's files と 'b' directory's files を。

    1. Run.
      • python tools/process.py --input_dir a --operation resize --output_dir resizedA
    2. Run.
      • python tools/process.py --input_dir b --operation resize --output_dir resizedB
  6. Combine して下さい、 'resizedA' directory's files と 'resizedB' directory's files を、 ab directory へ。

    • Run.
      • python tools/process.py --input_dir resizedA --b_dir resizedB --operation combine --output_dir ab
  7. Rename して下さい、 'ab' directory's files を。

    • Run.
      1. pushd ab
      2. ls *.png|awk '{printf "mv %s %s.png\n",$1,NR}'|sh
      3. popd

strArrToImgs.js

'use strict';
// strArrToImgs.js

const strArr = ['!', '#', '%', '&', '(', ')', '*', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '西', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ',', '-', '.', '/', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', ']', '^', '_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x' , 'y', 'z', '{', '|', '}', '~', '¡', '¥', '¦', '§', '©', '«', '¬', '®', '°', '±', '', '·', '»', '¿', '×', '÷', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '丿', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '彿', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '橿', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', ''];

const { execSync } = require('child_process');

execSync('mkdir -p ./a');
execSync('mkdir -p ./b');

for (let i = 0; i < strArr.length; i++) {
  execSync(`convert -font ~/Library/Fonts/GLT-GonunneObsolete.otf -pointsize 256 label:'${strArr[i]}' ./a/${i}.png`);
  execSync(`convert -font ~/Library/Fonts/SourceHanSans-Regular.otf -pointsize 256 label:'${strArr[i]}' ./b/${i}.png`);
  process.stdout.write(`\r${i+1}/${strArr.length}`);
}

実際に、使った code は https://github.com/waricoma/make-font-pix2pix-tensorflow
↑ これは async な仕様です。筆者は目指しました、 automation を。

cleaningImgs.js

'use strict';
//cleaningImgs.js

const fs = require('fs');
const { execSync } = require('child_process');

for (let ab of ['a', 'b']) fs.readdir(`./${ab}`, (e, files) => {
  for (let i = 0; i < files.length; i++) {
    const filePath = `./${ab}/${files[i]}`;
    if (execSync(`identify -format "%[mean]" ${filePath}`).toString() === '65535') fs.unlink(filePath, () => console.log(`deleted: ${filePath}`));
    process.stdout.write(`\r${ab} ${i+1}/${files.length}`);
  }
});

実際に、使った code は https://github.com/waricoma/make-font-pix2pix-tensorflow
↑ これは async な仕様です。筆者は目指しました、 automation を。

matchingImgs.js

'use strict';
// matchingImgs.js

const fs = require('fs');

/**
 * This function delete input file of can't use.
 * @param {array[string]} usingFilesArr 
 * @param {string} ab 
 * @param {string} file 
 */
const cleaner = (usingFilesArr, ab, file) => {
  const filePath = `./${ab}/${file}`;
  if (usingFilesArr.indexOf(file) === -1) fs.unlink(filePath, () => console.log(`deleted: ${filePath}`));
};

fs.readdir('./a', (e, aFiles) => { fs.readdir('./b', (e, bFiles) => {
  const usingFilesArr = aFiles.concat(bFiles).filter((fileName, i, thisFilesArr) => {
    return thisFilesArr.indexOf(fileName) === i && i !== thisFilesArr.lastIndexOf(fileName);
  });
  for (let file of aFiles) cleaner(usingFilesArr, 'a', file);
  for (let file of bFiles) cleaner(usingFilesArr, 'b', file);
}); });

実際に、使った code は https://github.com/waricoma/make-font-pix2pix-tensorflow
↑ これは async な仕様です。筆者は目指しました、 automation を。

Compress pix2pix-tensorflow

zip -r ../pix2pix-tensorflow.zip ./ && open ../

Upload pix2pix-tensorflow.zip to 'My Drive' on Google Drive.

Google Colab setting.

  1. Open Google Colab on your web browser.

  2. Click 'NEW PYTHON 3 NOTE BOOK'

  3. Select 'GPU' ( Edit > Notebook settings > Hardware accelerator )

  4. Click 'SAVE'

  5. Click '+CODE'

Mount to Google Colab from Google Drive.

from google.colab import drive
drive.mount('/content/drive')
  1. Copy ↑

  2. Paste it.

  3. Run.

    • Click '▶'
  4. Open generated URL.

  5. Login ( Google account. )

  6. Give permission.

  7. Copy the code.

  8. Back to Google Colab.

  9. Paste it.

  10. Put Return key. ( or Enter )

Unzip pix2pix-tensorflow.zip

!unzip -d '/content/drive/My Drive/pix2pix-tensorflow' '/content/drive/My Drive/pix2pix-tensorflow.zip' && rm '/content/drive/My Drive/pix2pix-tensorflow.zip'
  1. Copy ↑

  2. Paste it.

  3. Run.

    • Click '▶'

Move to pix2pix-tensorflow

import os
os.chdir('/content/drive/My Drive/pix2pix-tensorflow')
  1. Copy ↑

  2. Paste it.

  3. Run.

    • Click '▶'

Training.

!python pix2pix.py --mode train --output_dir lastTour --max_epochs 100 --input_dir ab --which_direction BtoA
  1. Copy ↑

  2. Paste it.

  3. Run.

    • Click '▶'
  4. Wait for long time...

    • Check 'progress epoch 1 step 49 image/sec 3.6 remaining ***'
  • --max_epochs 100
    • It's long time if set large number.
    • It's short time if set small number.

Export model.

!python pix2pix.py --mode export --output_dir lastTourExport --checkpoint lastTour --which_direction BtoA && python server/tools/export-checkpoint.py --checkpoint lastTourExport --output_file lastTour.pict

DL: lastTour.pict ( Google Drive > pix2pix-tensorflow ) to pix2pix-tensorflow directory. ( local )

  • Please use Google Colab Files Tab.

Make test image. ( yesterday.png )

convert -font ~/Library/Fonts/SourceHanSans-Regular.otf -pointsize 256 label:'昨' yesterday.png && mkdir -p test && mv yesterday.png test && python tools/process.py --input_dir test --operation resize --output_dir resizedTest

Check

↓ by: https://ml5js.org/docs/pix2pix-example

<!-- index.html -->

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.min.js" type="text/javascript"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/addons/p5.dom.min.js" type="text/javascript"></script>
  <script src="https://unpkg.com/ml5@0.1.3/dist/ml5.min.js" type="text/javascript"></script>
</head>

<body>
  <div id="example">
    <style>
    .border-box {
      border: black 1px solid;
    }

    .flex {
      display: flex;
    }

    .flex-space-between {
      justify-content: space-between;
    }
    </style>

    <div class="flex">
      <div>
        <div id="canvasContainer" style="height: 260px;">
        </div>
        <div id="btnContainer" class="flex flex-space-between">
          <button id="clearBtn">Clear</button><br>
          <button id="transferBtn" class="btn">Transfer</button>
        </div>
      </div>
      <div id="output">
        <img src="" class="border-box">
      </div>
    </div>

    <p id="status">Loading...</p>

    <script>
    // So the input images can only be 256x256 or 512x512, or multiple of 256
    const SIZE = 256;
    let inputImg, inputCanvas, outputContainer, statusMsg, pix2pix, clearBtn, transferBtn, modelReady = false, isTransfering = false;

    function setup() {
      // Create a canvas
      inputCanvas = createCanvas(SIZE, SIZE);
      inputCanvas.class('border-box').parent('canvasContainer');

      // Display initial input image
      inputImg = loadImage('resizedTest/yesterday.png', drawImage);

      // Selcect output div container
      outputContainer = select('#output');
      statusMsg = select('#status');

      // Select 'transfer' button html element
      transferBtn = select('#transferBtn');

      // Select 'clear' button html element
      clearBtn = select('#clearBtn');

      // line size
      strokeWeight(18);

      // Attach a mousePressed event to the 'clear' button
      clearBtn.mousePressed(function() {
        clearCanvas();
      });

      // Set stroke to black
      stroke(0);
      pixelDensity(1);

      // Create a pix2pix method with a pre-trained model
      pix2pix = ml5.pix2pix('lastTour.pict', modelLoaded);
    }

    // Draw on the canvas when mouse is pressed
    function draw() {
      if (mouseIsPressed) {
        line(mouseX, mouseY, pmouseX, pmouseY);
      }
    }

    // When mouse is released, transfer the current image if the model is loaded and it's not in the process of another transformation
    function mouseReleased() {
      if (modelReady && !isTransfering) {
        transfer()
      }
    }

    // A function to be called when the models have loaded
    function modelLoaded() {
      // Show 'Model Loaded!' message
      statusMsg.html('Model Loaded!');

      // Set modelReady to true
      modelReady = true;

      // Call transfer function after the model is loaded
      transfer();

      // Attach a mousePressed event to the transfer button
      transferBtn.mousePressed(function() {
        transfer();
      });
    }

    // Draw the input image to the canvas
    function drawImage() {
      image(inputImg, 0, 0);
    }

    // Clear the canvas
    function clearCanvas() {
      background(255);
    }

    function transfer() {
      // Set isTransfering to true
      isTransfering = true;

      // Update status message
      statusMsg.html('Applying Style Transfer...!');

      // Select canvas DOM element
      const canvasElement = select('canvas').elt;

      // Apply pix2pix transformation
      pix2pix.transfer(canvasElement, function(err, result) {
        if (err) {
          console.log(err);
        }
        if (result && result.src) {
          // Set isTransfering back to false
          isTransfering = false;
          // Clear output container
          outputContainer.html('');
          // Create an image based result
          createImg(result.src).class('border-box').parent('output');
          // Show 'Done!' message
          statusMsg.html('Done!');
        }
      });
    }

    </script>
  </div>
</body>

  1. Copy ↑

  2. Run.

    • vim index.html
  3. Put i key.

  4. Paste it.

  5. Put Escape key.

  6. Type :wq

  7. Put Return key. ( or Enter )

  8. Run.

    • python -m http.server
  9. Open URL ( by http.server ) to Web browser.

Finished 🎉

年賀状駆動開発

  • 学校法人角川ドワンゴ学園N高等学校
    • https://nnn.ed.jp
    • 全国に展開する通信制高校です。
      • 通学コースもあります。
    • 毎年、北海道から沖縄までの生徒たちは年賀状を送り合います。
    • 7875人以上の生徒がいます。 ( 2019年度現在 )
      • プログラミングをしている生徒が多いです。
      • 例えば 年賀状をスマホアプリのカメラにかざすと、メッセージが浮かび上がる ものなど様々な年賀状駆動開発が行われたりしたようです。
      • 私もこれを行いたく思い、フォントファイルに収録されてない字を機械学習で生成し、年賀状に使ってみました。

walfas-waricoma.png

今後

  • アルファベット、漢字、平仮名、片仮名、数字、記号…などで分類しそれぞれ違う色にして学習させるなどし、様子を見てみたいと思っています。
  • 生成された字をフォントファイルとして使えるようにできればなとも思います。

LTしました。

N高等学校 2/22 16:30 - 17:30 通学コースLT大会
3人目/8人の登壇者 https://font2font.netlify.com/#/0/1 (⚠: 非常に長い読込時間)

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7