1. プログラミングを思い出そう

夏休みで忘れてしまったかもしれないプログラミングを思い出していきましょう。

1.1. 入社面談より

昔の倉光研の学生がGoogle社の入社面談を受けたとき、ホワイトボードの前で「ちょっと書いてみて」と聞かれた設問のひとつです。

例題(Google関数)

与えられた整数xを反転させる関数g(x)を定義してみてください。

g(x)の例:

  • g(12)ならば21

  • g(1234)ならば4321

  • g(90)ならば9

ホワートボードコーディング

面接でプログラミング力を試させること

ただ、プログラミングを書くだけでなく、エレガントなコーディングやセンスも問われます。

解答例を見る前に少し書いてみましょう。 (この間、倉光は就職活動に関する雑談をします。)

06bfb82c84524d6aa905a21103791a08

1.1.1. 解答例(1)

過去、Google関数をプログラミング課題として出題してみると、驚いてしまうことに、ほとんどの学生が以下のような解答を提出してきました。

[ ]:
def g(x):
    if x == 12: return 21
    if x == 1234: return 4321
    return 9


たぶん、この解答で採用してくれる企業は、ありません。(プログラミング力以外のものを期待しているのでしょうね。)

1.1.2. 解答例(2)

前期のPython プログラミングを履修してきた皆さんなら、文字列に変換して反転かと思います。

[ ]:
def g(x):
    return int(str(x)[::-1])


正直、好き嫌いが分かれる解答です。(文字列に変換するなんてずるいと感じる人もいます。)

1.1.3. 解答例(3)

昔、すごく頭のよい学生がいて、ループであっさりと書いてくれたことがあります。

[ ]:
def g(x):
    inv = 0
    while x!= 0:
        inv = inv * 10 + x % 10
        x //= 10
    return inv


頭が良すぎて、ソースコードを読んだだけでは、正しいのかわかりません。

1.1.4. 解答例(4):再帰派

僕は再帰派なので、入社面接で聞かれたら、 意地でも再帰で答えておくでしょう。

[ ]:
import math

def g(x):
    if x < 10:
        return x
    scale = 10 ** int(math.log10(x))
    return (x % 10) * scale + g(x//10)


1.2. モジュール: PNGアニメーション

今回は、モジュールを使ったプログラミングに慣れていきます。 あと、Python(前期の授業)も思い出していきましょう。

1.2.1. モジュールの準備

Pythonは、最低限の言語機能のみ提供し、 便利な拡張機能はモジュール(Pythonのライブラリのこと)で提供しています。

Pillow(PIL)モジュール: Pythonで画像を簡単に扱う

[ ]:
from PIL import Image, ImageDraw

IPythonモジュール: 画像/動画をColab上に表示する

[ ]:
import IPython

APNGモジュール: アニメーションPNGを作成する

Colabは、あらかじめ多くのモジュールがインストールされた状態ですが、 APNGはインストールされていません。 そのようなモジュールは、pip installを使って、 先に追加インストールしておきます。

[ ]:
!pip install APNG
from apng import APNG


pip/pip3

Pythonのモジュールをインストールする便利なコマンドです。

pip install <モジュール名>

Colab上では、他のUNIXコマンドと同様に!マークをつけて記述します。

これらのモジュールは、ノートブックの先頭でまとめてインポートしておくと良いでしょう。

まとめてインポートする

[6]:
from PIL import Image, ImageDraw
import IPython
!pip install APNG
from apng import APNG

Requirement already satisfied: APNG in /usr/local/lib/python3.9/site-packages (0.3.4)
WARNING: You are using pip version 21.1.2; however, version 21.2.4 is available.
You should consider upgrading via the '/usr/local/opt/python@3.9/bin/python3.9 -m pip install --upgrade pip' command.

1.3. キャンバス

画像を描画するキャンバス(canvas)を作ってお絵かきを始めます。

大きさを指定して、キャンバスを作成する

[7]:
canvas = Image.new("RGB", (400,300))


  • キャンバスの大きさは、動画にすることを考えて、あまり大きくせず、400 x 300 くらいにしておきましょう。

コンピュータ画面の座標系

5532364f035c43b39e2fa892953b1f69

1.3.1. 図形を描画する

実際の描画操作は、描画コンテキスト(draw)を作って行います。 (この辺りの仕組みは、Python以外のプログラミング言語でもほぼ同じです。)

キャンバスの描画コンテキストを作る

[8]:
draw = ImageDraw.Draw(canvas)

まずは、長方形{rectangle}をキャンバス上に描画してみましょう。fillでは、塗りつぶす色を指定してます。

長方形を描画する

[9]:
draw.rectangle((100,100,200,200), fill='green')

39149c5b4f9d4263ae865a5b0752dd40

1.3.2. 画像ファイルの生成

キャンバスに描画した図形をファイル名を指定して、 画像ファイルに保存してみましょう。

``rect.png``というファイル名で保存する

[10]:
canvas.save('rect.png')

保存されたファイルを表示する

[11]:
IPython.display.Image("rect.png")

[11]:
_images/201prog_25_0.png

以上をまとめて、一度に実行しても構いません。

四角形を描画してみる

[12]:
canvas = Image.new("RGB", (400,300))
draw = ImageDraw.Draw(canvas)
draw.rectangle((100,100,200,200), fill='green')
canvas.save('rect.png')
IPython.display.Image("rect.png")


[12]:
_images/201prog_27_0.png

Let’s try (写経するだけでは身につきません)

長方形の色や位置、大きさを変えて表示してみよう

Colab上で、色々、パラメータや条件を変更して試してみましょう。

  • 色をピンク色にしてみよう

  • 四角形をキャンバスの真ん中においてみよう

1.3.3. 円も描画する

PIL/Pillowでは、長方形以外も様々な図形を描画できます。

ecllipseは、指定した短形におさまる楕円(円)を描画します。今回は、色をRGBで指定してみましょう。

[14]:
draw.ellipse((100, 100, 200, 200),fill=(192, 64, 64))

25963e4effa5446185b38ead4ecc5129

RGB

三原色の組み合わせからコンピュータ上の色を指定する方法

c882c656b4ef4b64abbf717e9e04a4fc

同じキャンバスの上に図形を追加すれば、複数の図形を描画できます。

四角形と円を描画してみる

[15]:
canvas = Image.new("RGB", (400,300))
draw = ImageDraw.Draw(canvas)

draw.rectangle((100,100,200,200), fill='green')
draw.ellipse((250,200, 300, 250),fill=(192, 64, 64))

canvas.save('canvas.png')
IPython.display.Image("canvas.png")

[15]:
_images/201prog_31_0.png

(チャレンジ課題) キャンバスになれる

ここまで無事にできたら、 キャンバス(Pillow)に長方形や円形以外にどのような図形が描画できるか調べてみましょう。

  • 多角形

  • 線分

  • 文字(text,font)

  • 他の画像ファイル

ヒント:Pillowの情報源

以下のサイトを参考にするとわかりやすいです。

1.4. アニメーションを作る

動画は、パラパラ漫画と同じ原理で作ります。 つまり、複数の画像ファイルを作成し、APNGモジュールで合成してみます。

1.4.1. 複数枚の画像を作る

まず、ボールが転がるアニメーションを作ってみましょう。

x軸方向に +20ずつ移動させながら、3枚ほど画像を作ります。

ball0.pngを作る

[16]:
canvas = Image.new("RGB", (400,300), color='black')
draw = ImageDraw.Draw(canvas)
draw.ellipse((100, 200, 150, 250),fill=(192, 64, 64))
canvas.save('ball0.png')
IPython.display.Image("ball0.png")

[16]:
_images/201prog_33_0.png

ball1.pngを作る

[17]:
canvas = Image.new("RGB", (400,300), color='black')
draw = ImageDraw.Draw(canvas)
draw.ellipse((100+20, 200, 150+20, 250),fill=(192, 64, 64))
canvas.save('ball1.png')
IPython.display.Image("ball1.png")

[17]:
_images/201prog_35_0.png

ball2.pngを作る

[18]:
canvas = Image.new("RGB", (400,300), color='black')
draw = ImageDraw.Draw(canvas)
draw.ellipse((100+40, 200, 150+40, 250),fill=(192, 64, 64))
canvas.save('ball2.png')
IPython.display.Image("ball2.png")

[18]:
_images/201prog_37_0.png

0d41fbceed994c91a8b00edab5aaa78e

こんなの一枚ずつ画像を作っていてはいけませんよね。

Let’s try

for文を使って、3枚以上作成してみてください。

1.4.2. APNGに変換する

Pillowで保存したPNG画像のファイル名をリストに入れます。

[19]:
filelist = ['ball0.png', 'ball1.png', 'ball2.png']

APNG モジュールは、画像ファイルのリストからアニメーションPNGを作成します。

delay は、画像1枚辺りのめくる速度(ミリ秒)です。

[20]:
APNG.from_files(filelist, delay=100).save("ball-anime.png")
IPython.display.Image("ball-anime.png")

[20]:
_images/201prog_41_0.png

(大したアニメでないのであまり期待しないでください)

1.5. 放物のアニメメーション

(例題) 放物線

ボールがいい感じで放物運動するアニメーションを作ってみましょう。

ヒント: 放物線を関数で表します

時刻\(t\)のときの(x,y)を次のようにします。 (ここのパラメータは自由に調整して構いません。)

  • \(x = 10t + 100\)

  • \(y = (t-10)^2 + 100\)

あとは、時刻tをfor t in range(20): のようにループを使って進め、順次PNG画像を作ります。そのとき、同時にPNG画像の名前を filelist に追加しておきます。

放物線のアニメーション

[21]:
filelist = []

for t in range(20):
    canvas = Image.new("RGB", (400,300), color='black')
    draw = ImageDraw.Draw(canvas)
    x = 10 * t + 100
    y = (t -10)**2 + 100
    draw.ellipse((x, y, x+50, y+50),fill=(192, 64, 64))
    canvas.save(f'ball{t}.png')
    filelist.append(f"ball{t}.png") #ファイルリスト

APNG.from_files(filelist, delay=100).save("ball-anime.png")
IPython.display.Image("ball-anime.png")

[21]:
_images/201prog_43_0.png

1.6. コースワーク

(演習問題)円形運動

キャンバスの中心をぐるぐるとまわる物体のアニメーションを作成してみましょう。 (中心の座標も表示できたら、良いかも)

42dfdce10cd343e59baefac7f448045f

座標変換に苦労するときは

紙と鉛筆を用意して、落ち着いて座標を書いてみよう。

  1. 回転運動の中心座標(ox, oy)を求める

  2. 三角関数(cos(t), sin(t))で円の中心座標を求める

  3. y軸が反転していることに注意する

どうしても正しく円が表示されないとき

print(x,y)のように値を表示して、 キャンバス内(400,300)に(x,y)が含まれるか確認しよう。