Jupyter Notebook上でCanvasを利用して画像を生成する

はじめに

Jupyter Notebook上でCanvasを使って画像を作って読み込めない?と相談を受けて調べたら

1from IPython.core.display import HTML

を使うとHTMLをセルの結果表示部分に表示させることが出来るのでトライしたメモ

成果物はgistにあげてるのでご興味があればどうぞ

Python3でテストしましたが依存してそうなのはByteIOくらいなのでStringIOに置き直せば2系でも動くと思います

キャンバスを生成する

先程のモジュールを使って要素を生成します
ブラウザで動かせるものならなんであれ使えるはずなのでjqueryで動作するもっと高機能なものを持ってきても良いと思います

1<canvas id="canvas" height="300px" width="300px" style="border: 1px solid;"></canvas>
2<p><input id="variable" type="text" placeholder="Input python variable name" size="30"></p>
3<p>
4    <button id="clear">Clear</button>
5    <button id="submit">Save image to variable</button>
6</p>

固定の変数名に書き出す場合は不要ですがPython側の変数名を指定するインプットボックスとキャンバスをクリアするボタンを用意して置きます

キャンバスにお絵かきできるようにする

良い感じにJSを書いてお絵かきできるようにします
この辺のデバッグは普通にHTML書いてやった方が幸せな気がします

 1    var config = {
 2        "linesize": 7,
 3        "linecolor": "#000000"
 4    }
 5
 6    var mouse = {
 7        "X": null,
 8        "Y": null,
 9    }
10
11    var canvas = document.getElementById("canvas");
12    var ctx = canvas.getContext("2d");
13    canvas.addEventListener("mouseup", drawEnd, false);
14    canvas.addEventListener("mouseout", drawEnd, false);
15    
16    canvas.addEventListener("mousemove", function(e){
17        if (e.buttons === 1 || e.witch === 1) {
18            var rect = e.target.getBoundingClientRect();
19            var X = e.clientX - rect.left;
20            var Y = e.clientY - rect.top;
21            draw(X, Y);
22        };
23    });
24 
25    canvas.addEventListener("mousedown", function(e){
26        if (e.button === 0) {
27            var rect = e.target.getBoundingClientRect();
28            var X = e.clientX - rect.left;
29            var Y = e.clientY - rect.top;
30            draw(X, Y);
31        }
32    });
33
34    function draw(X, Y) {
35        ctx.beginPath();
36        if (mouse.X === null) {
37            ctx.moveTo(X, Y);
38        } else {
39            ctx.moveTo(mouse.X, mouse.Y);
40        }
41        ctx.lineTo(X, Y);
42        
43        ctx.lineCap = "round";
44        ctx.lineWidth = config.linesize * 2;
45        ctx.strokeStyle = config.linecolor;
46        ctx.stroke();
47
48        mouse.X = X;
49        mouse.Y = Y;
50    };
51 
52    function drawEnd() {
53        mouse.X = null;
54        mouse.Y = null;
55    }

キャンバスのクリア

ボタン押したらclearRectを実行するだけ

1    var clear = document.getElementById("clear");
2    clear.addEventListener("click", function(){
3        ctx.clearRect(0, 0, canvas.width, canvas.height);
4    });

変数への書き出し

JS -> Pythonへはとりあえずbase64を使用した
JSからPythonのコードを実行できる
例えば

1var kernel = IPython.notebook.kernel;
2kernel.execute("hoge = 2")

とするとPython側のhoge変数に2が代入される
これをボタンのイベントと紐付ける

1submit.addEventListener("click", function(){
2    kernel.execute(variable.value + " = '" + canvas.toDataURL() + "'");
3}

Python側でbase64をデコードする

受け取った変数から余分な部分を取り除いてデコードする

1base64_img = base64_img.split(",")[-1]
2img = Image.open(BytesIO(base64.b64decode(base64_img)))
3plt.imshow(np.asarray(img))
4plt.show()

こんな感じでPython側で画像が利用できる
色々夢が広がる