1. Home
  2. /
  3. プログラミング
  4. /
  5. pythonで簡単プログラミング
  6. /
  7. 【画像描画】pythonで直線を描いてみる

【画像描画】pythonで直線を描いてみる

rainbow line pythonで簡単プログラミング

#python #Pillow #画像 #直線 #line #色 #RGB

この記事では、pythonのPILというモジュール(Pillow)で直線を描く方法について、わかりやすく解説します。

本記事では、主としてWindows、および、python3を前提にしています。
スポンサーリンク

絵を描くための準備

本記事では、画像を描くための準備は、下記のプログラムで行います。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

im.show() # 画面に表示する

実行は以下のようにして行います。

python line.py

結果は以下のとおり、真っ白な500✕600ドットのキャンバスが表示されます。
なお、小生のPCでは画像表示にXnViewというソフトを使っているため、プログラムで、im.show()関数を実行したときに、これが起動されました。
読者の方は、違うソフトが起動されると思います。

円を描くことに集中するため、この部分のプログラムの詳細な説明はここではしません。コメントに簡単な説明をしていますので、参照してください。
詳しい説明が必要な場合は、『【第4回】にっこちゃんを描いてやんわりと関数・モジュールを学ぶ(解説編)』の『画像を描くキャンバスの作成』以降を参照してください。

座標について

画像の表示で使う座標は、下図のように
左上が (x, y)=(0, 0) で、
左に行くにつれて x座標が増え、
下に行くにつれて y座標が増えます。
中学で学ぶ数学の座標では、y座標は下が0で、上に行くにつれて増えますが、コンピュータでは、逆になるため、注意が必要です。

直線を描いてみる

Pillowで直線を描くには、line関数を使います。
以下の入力が必要です。
・ 直線の開始位置x座標, y座標
・ 直線の終了位置x座標, y座標
・ 直線の色の赤(Red)成分, 緑(Green)成分, 青(Blue)成分

プログラム中に書くと、以下になります。

draw.line(   (  (直線の開始位置x座標, y座標),      (直線の終了位置x座標, y座標)  ), 
             fill=(  直線の色のRed成分,    Green成分,     Blue成分))

実は直線の座標の指定方法にはいくつかの書式がありますが、この記事では現時点で最も問題のない(Pillowのバグを発生させない)書式で記載しています

では、まずオーソドックスに、
キャンバスの左上(0, 0)から、(500, 500)に黒い線を描いてみます。
以下の行を付け加えます。

draw.line((( 0, 0),( 500, 500)), fill=(  0,   0,   0))

line関数の最初の入力 (( 0, 0), ( 500, 500)) は、
直線の開始位置x座標, y座標, 直線の終了位置x座標, y座標
です。
括弧 ”(“, “)” が外側に余分についていますが、これについては後述します。

2つ目の入力 fill=( 0, 0, 0) は、
fill=(直線の色の赤(Red成分), 緑(Green成分), 青(Blue成分)),
です。
Red成分, Green成分, Blue成分の全てが0の場合に黒となります。

付け加えた後のプログラムは以下になります。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

draw.line((( 0, 0),( 500, 500)), fill=(  0,   0,   0))

im.show() # 画面に表示する

実行した結果は以下のとおりに表示されます。

直線をいくつか描いてみる

直線をいくつか描いてみます。
座標をずらして5つ追加して並べます。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

draw.line(((   0,   0),( 500, 500)), fill=(  0,   0,   0))
draw.line(((  83,   0),( 416, 500)), fill=(  0,   0,   0))
draw.line((( 166,   0),( 333, 500)), fill=(  0,   0,   0))
draw.line((( 250,   0),( 250, 500)), fill=(  0,   0,   0))
draw.line((( 333,   0),( 166, 500)), fill=(  0,   0,   0))
draw.line((( 416,   0),(  83, 500)), fill=(  0,   0,   0))
draw.line((( 500,   0),(   0, 500)), fill=(  0,   0,   0))

im.show() # 画面に表示する

プログラムの着色部分を見るとわかりますが、x座標を83ずつずらしました。
結果は以下のとおりになります。

色を変える

黒では面白くないので、色を変えてみます。
色は、赤(Red), 緑(Green), 青(Blue)の3色を指定して混ぜた色になります。
それぞれ、0 ~ 255の数値で濃さを指定します。

ただしこの色は、光を重ね合わせて混ぜる色になるため、絵の具で混ぜた色とは異なります。たとえば、絵の具の場合は、赤、緑、青を全て混ぜると黒くなりますが、パソコンのモニタやスマホの画面では、白になります。
この色の指定形式をRGB形式といい、pythonに限らず、どのような言語で使用する場合でも共通です。

では、色を0か255にして順に変えてみます。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

draw.line(((   0,   0),( 500, 500)), fill=(  0,   0,   0))
draw.line(((  83,   0),( 416, 500)), fill=(255,   0,   0))
draw.line((( 166,   0),( 333, 500)), fill=(  0, 255,   0))
draw.line((( 250,   0),( 250, 500)), fill=(  0,   0, 255))
draw.line((( 333,   0),( 166, 500)), fill=(255, 255,   0))
draw.line((( 416,   0),(  83, 500)), fill=(  0, 255, 255))
draw.line((( 500,   0),(   0, 500)), fill=(255, 255, 255))

im.show() # 画面に表示する

結果は以下となります。

黄色が見えにくいですねえ。
あと、実は消えているように見えるところは白です。

せっかくなので、0と255だけではなく、中間の色も表示してみます。
量が多くなるので、プログラムが見にくくなりますが、受け入れてください。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

draw.line(((   0,   0),( 500, 500)), fill=(  0,   0,   0))
draw.line(((  41,   0),( 458, 500)), fill=(128,   0,   0))
draw.line(((  83,   0),( 416, 500)), fill=(255,   0,   0))
draw.line((( 124,   0),( 375, 500)), fill=(128, 128,   0))
draw.line((( 166,   0),( 333, 500)), fill=(  0, 255,   0))
draw.line((( 207,   0),( 292, 500)), fill=(  0, 128, 128))
draw.line((( 250,   0),( 250, 500)), fill=(  0,   0, 255))
draw.line((( 291,   0),( 208, 500)), fill=(  1,   0, 128))
draw.line((( 333,   0),( 166, 500)), fill=(255, 255,   0))
draw.line((( 375,   0),( 124, 500)), fill=(128, 255, 128))
draw.line((( 416,   0),(  83, 500)), fill=(  0, 255, 255))
draw.line((( 458,   0),(  41, 500)), fill=(128, 255, 255))
draw.line((( 500,   0),(   0, 500)), fill=(255, 255, 255))

im.show() # 画面に表示する

実行結果です。
いくつかの中間色の線が描画できました。

計算をして線をもっと細かく表示させる

上述のように、線の座標を何度も繰り返して書くのは非常に疲れます。
そのため、せっかくですので、線の座標を計算で求めてみます。
繰り返し座標を変えていくためにはwhile文かfor文を使いますが、ここではfor文が適しているため、for文を使います。

値の集まり[集合]の数だけ、[行]を実行するという文法は、下記になります。
[集合]から、値を1つずつ[値]に取り出して、[行]の中で使って処理します。

for [値] in [集合] :
    [行]
    [行]

今回の線画では、x座標を一定の値で増やしたり、減らしたりする必要があります。
一定の値で増加する値の集まりは、以下のrange関数で作ることができます。

range( [開始値], [終了値]+1, [増分] )

range関数は、[開始値]から[終了値]まで、[増分]で増加する値を生成できます。
[終了値]ではなく、[終了値]+1を指定するところに注意が必要です。

このrange関数を以下のようにfor文の[集合]に指定することで、
[開始値]から[増分]で増加させつつ、[終了値]まで[行]を実行する
という処理を行なうことができます。

for [値] in range( [開始値], [終了値]+1, [増分] ) :
    [行]
    [行]

実際のプログラムを書いてみます。

0から500まで1ドットずつ増加させて線を描画します。
値は変数deltaに取り出して、(delta, 0)から{500-delta, 500)まで、黒=(0,0,0)の線で描画します。

for delta in range(  0, 501,  1) :
    draw.line(((   delta,   0),( 500-delta, 500)), fill=(0, 0, 0))

完全なプログラムは以下になります。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

for delta in range(  0, 501,  1) :
    draw.line(((   delta,   0),( 500-delta, 500)), fill=(0, 0, 0))

im.show() # 画面に表示する

実行結果は以下になります。
黒のみで描画したため、塗りつぶされたようになりました。

虹色にしてしまう

色が黒のままでは地味ですので、この際、虹色にします。
ただし、虹色にする場合も、色の値(色値しきち)を細かく指定することになるため、座標の場合と同じようにかなり大変なプログラムになります。
ここは、徐々に色値を変えるように計算させてみます。

計算は以下のプログラムで行います。
なお、下記プログラムでは、print関数で色値を表示させています。

red = 0
green = 0
blue = 0

red_step = 5
green_step = 5
blue_step = -5

red_cnt = 0
green_cnt = -250
blue_cnt = 250

for delta in range(  0, 501,  1) :
    red_cnt = red_cnt + red_step
    if red_cnt > 0 :
        red = red + red_step
        if red > 250 :
            red = 250
        if red < 0 :
            red = 0
    if red_cnt >= 500 or red_cnt <= -250 :
        red_step = -red_step

    green_cnt = green_cnt + green_step
    if green_cnt > 0 :
        green = green + green_step
        if green > 250 :
            green = 250
        if green < 0 :
            green = 0
    if green_cnt >= 500 or green_cnt <= -250 :
        green_step = -green_step

    blue_cnt = blue_cnt + blue_step
    if blue_cnt > 0 :
        blue = blue + blue_step
        if blue > 250 :
            blue = 250
        if blue < 0 :
            blue = 0
    if blue_cnt >= 500 or blue_cnt <= -250 :
        blue_step = -blue_step

    print("red , green, blue = " + str(red) + " " + str(green) + " " + str(blue) )

上記のプログラムはおおよそ、以下の処理を行っています。
ここでは、線を描くことに集中するものとして、これ以上の詳細については解説しません。

1.赤(red)、緑(green)、青(blue)ごとにカウンタ(red_cnt, green_cnt, blue_cnt)を用意
2.for文の中でカウントアップする
3.カウンタが0より大きくなったら色値(red, green, blue)を
  増減値(red_step, green_step, blue_step)だけ増やす(または減らす)
4.色値が250を超えたら250に、0未満になったら0にする
  (色値を指定可能な値の範囲に維持する)
5.カウンタが500以上、または-250以下になった場合、増減値の符号を反転させる

実行結果は以下になります。

red , green, blue = 5 0 0
red , green, blue = 10 0 0
red , green, blue = 15 0 0
red , green, blue = 20 0 0
red , green, blue = 25 0 0
red , green, blue = 30 0 0
red , green, blue = 35 0 0
     :
red , green, blue = 235 0 0
red , green, blue = 240 0 0
red , green, blue = 245 0 0
red , green, blue = 250 0 0
red , green, blue = 250 5 0
red , green, blue = 250 10 0
red , green, blue = 250 15 0
red , green, blue = 250 20 0
red , green, blue = 250 25 0
     :
red , green, blue = 15 250 0
red , green, blue = 10 250 0
red , green, blue = 5 250 0
red , green, blue = 0 250 0
red , green, blue = 0 245 5
red , green, blue = 0 240 10
red , green, blue = 0 235 15
red , green, blue = 0 230 20
     :

以上により、赤、緑、青の成分をバラバラに増減することで、虹色が作成できます。
ちなみに、赤、緑、青を一斉に増減させてしまうと、黒⇔白のグラデーションになってしまいます。

もとの描画プログラムに上記の計算部分を入れたものが、以下になります。

<ファイル名:line.py>

from PIL import Image, ImageDraw # PILからImage, ImageDrawクラスを取り出す

im = Image.new('RGB', ( 500,  600), (255, 255, 255)) # 500 ✕ 600ドットで白(=(255,255,255)の
                                                     # キャンバスを準備する
draw = ImageDraw.Draw(im) # 絵を描き込むバケツ(ImageDraw.Drwオブジェクト)を準備する

red = 0
green = 0
blue = 0

red_step = 5
green_step = 5
blue_step = -5

red_cnt = 0
green_cnt = -250
blue_cnt = 250

for delta in range(  0, 501,  1) :
    red_cnt = red_cnt + red_step
    if red_cnt > 0 :
        red = red + red_step
        if red > 250 :
            red = 250
        if red < 0 :
            red = 0
    if red_cnt >= 500 or red_cnt <= -250 :
        red_step = -red_step

    green_cnt = green_cnt + green_step
    if green_cnt > 0 :
        green = green + green_step
        if green > 250 :
            green = 250
        if green < 0 :
            green = 0
    if green_cnt >= 500 or green_cnt <= -250 :
        green_step = -green_step

    blue_cnt = blue_cnt + blue_step
    if blue_cnt > 0 :
        blue = blue + blue_step
        if blue > 250 :
            blue = 250
        if blue < 0 :
            blue = 0
    if blue_cnt >= 500 or blue_cnt <= -250 :
        blue_step = -blue_step

    draw.line(((   delta,   0),( 500-delta, 500)), fill=(red, green, blue))
    draw.line(((   0, delta),( 500, 500-delta)), fill=(red, green, blue))

im.show() # 画面に表示する

実行結果は以下になります。

最後に

いかがでしたでしょうか?
直線を描くだけでも、以下を知る必要がありました。

  • 座標の指定方法
  • 色の指定方法

これらの要素は、直線以外の描画をおこなうときにも基本的に同じです。

また、

  • 座標位置を自動計算する処理
  • 虹色を描画する処理

など、プログラミングならではの特別な処理についても解説しました。

ということで、また別の描画方法を解説しますので、ご期待ください!

この記事へのお問い合わせや、無料のサポート、一歩踏み込んだ有料サポートが必要な場合は、ホームページからメールで受け付けています。お気軽にご連絡ください。

コメント

タイトルとURLをコピーしました