Edited at

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


結果

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 (⚠: 非常に長い読込時間)