031_py_pillow - kotaproj/study_zenpan GitHub Wiki

from PIL import Image, ImageDraw, ImageFont


image = Image.new("1", (128, 64))
image

# ドットを配置(2点)
draw.point([(10, 10), (20, 20)], fill='white')
image

# ライン描画
draw.line([(30, 30), (40, 40)], fill='white', width=2)
image

pillow使い方

画像読み込み

https://note.nkmk.me/python-pillow-basic/

from PIL import Image, ImageFilter

im = Image.open('data/src/lenna_square.png')

画像保存

https://note.nkmk.me/python-pillow-basic/

new_im.save('data/dst/lenna_square_pillow.jpg', quality=95)

二値化 - 1

gray = img.convert("L")                     # グレイスケールに変換
gray.point(lambda x: 0 if x < 230 else x)   # 値が230以下は0になる

二値化 - 2

https://qiita.com/kikuchiTakuya/items/9c1da0aaaf88f231d2e3

import numpy as np 
from PIL import Image
from matplotlib import pylab as plt

img = np.array(Image.open('lena.jpg').convert('L'), 'f')

img = (img > 128) * 255

plt.imshow(img)

plt.show()

pillow <-> numpy変換

https://white-wheels.hatenadiary.org/entry/20100322/p1

pillow -> numpy

NumpyのarrayからPILへの変換 PILのfromarrayメソッドによって、配列の各値を1byte整数型(0〜255)として画像のpixel値に変換することができます。

pilImg = Image.fromarray(numpy.uint8(imgArray))

numpy -> pillow

import numpy
import Image

PILからNumpyのarrayへの変換 numpyで用意されているasarray関数を使うと、PILのImageオブジェクトを配列に変換することができます。

imgArray = numpy.asarray(pilImg)

さらに配列の値を書き変えられるようにするためには、writeableフラグをTrueにする必要があるようです。

imgArray.flags.writeable = True

直線、多角形、点

https://note.nkmk.me/python-pillow-imagedraw/

  • 直線 : line(xy, fill, width)
    • xy
      • ((x1, y1), (x2, y2), (x3, y3)...)のように2点以上の複数の座標を指定する。
      • 各点を結んだ直線が描画される。
    • width : 線幅(線の太さ)
      • widthで線幅を太くした場合、xyで3点以上指定すると接続部が不格好になる。
  • 多角形 : polygon(xy, fill, outline)
    • xy
      • ((x1, y1), (x2, y2), (x3, y3)...)のように3点以上の複数の座標を指定する。
      • 各点と最初と最後の点が結ばれた多角形が描画される。
  • 点 : point(xy, fill)
    • xy
      • ((x1, y1), (x2, y2), (x3, y3)...)のように1点以上の複数の座標を指定する。
      • 各点に1ピクセルの点が描画される。

直線(line())、多角形(polygon())、点(point())の例。点は1ピクセルなので見づらいが右側に描画されている。

im = Image.new('RGB', (500, 250), (128, 128, 128))
draw = ImageDraw.Draw(im)

draw.line(((30, 200), (130, 100), (80, 50)), fill=(255, 255, 0))
draw.line(((80, 200), (180, 100), (130, 50)), fill=(255, 255, 0), width=10)
draw.polygon(((200, 200), (300, 100), (250, 50)), fill=(255, 255, 0), outline=(0, 0, 0))
draw.point(((350, 200), (450, 100), (400, 50)), fill=(255, 255, 0))

楕円

draw.ellipse((100, 100, 150, 200), fill=(255, 0, 0), outline=(0, 0, 0))

四角

draw.rectangle((200, 100, 300, 200), fill=(0, 192, 192), outline=(255, 255, 255))

直線

draw.line((350, 200, 450, 100), fill=(255, 255, 0), width=10)

一時データ

import PIL.Image
import PIL.ImageDraw
import PIL.ImageFont
import webcolors

# 使うフォント,サイズ,描くテキストの設定
ttfontname = "ipaexg.ttf"
fontsize = 28

rain_colors = ["red", "green", "blue", "cyan", "white", "magenta", "yellow"]

def create_msgpng(msg, color):
    # 画像サイズ,背景色を設定
    canvasSize = (32*(len(msg)+2), 32)
    backgroundRGB = webcolors.name_to_rgb("black")

    # 文字を描く画像の作成
    img  = PIL.Image.new('RGB', canvasSize, backgroundRGB)
    draw = PIL.ImageDraw.Draw(img)

    # 用意した画像に文字列を描く
    x_off, y_off = 28, 0

    font = PIL.ImageFont.truetype(ttfontname, 28)

    for cnt, moji in enumerate(msg):
        textWidth, textHeight = draw.textsize(moji, font=font)
        textTopLeft = (x_off, 28 - 28)
        if "rainbow" in color:
            rgb = webcolors.name_to_rgb(rain_colors[cnt%len(rain_colors)])
        else:
            rgb = webcolors.name_to_rgb(color)
        draw.text(textTopLeft, moji, fill=rgb, font=font)
        x_off += textWidth

    img = img.crop((0, 0, x_off+28, 32))
    img.save("image_moku2.png")


create_msgpng("〇×xxxfaklfaj ", color="rainbow")



import board
import neopixel
import time
import numpy as np
import cv2

IMG_PATH = "image_moku.png"
# IMG_PATH = "heart_dokidoki.gif"

# シリアルLEDの初期化
pixel_pin = board.D18
num_pixels = 32*32
ORDER = neopixel.GRB
pixels = neopixel.NeoPixel(
    pixel_pin, num_pixels, brightness=0.010, auto_write=False, pixel_order=ORDER
)

def show32x32_pixel(np_rgb):
    global pixels

    for cnt in range(32):
        if cnt%2 == 0:
            # [D],[C]エリア - 偶数行の並べ替え
            for scnt, val in enumerate(np_rgb[cnt,:16,]):
                pixels[1023 - cnt*16 - scnt] = (val[0], val[1], val[2])
            # [A],[B]エリア - 偶数行の並べ替え
            for scnt, val in enumerate(np_rgb[cnt,16:,]):
                pixels[cnt*16 + 16 - 1 - scnt] = (val[0], val[1], val[2])
        else:
            # [D],[C]エリア - 奇数行の並べ替え
            for scnt, val in enumerate(np_rgb[cnt,:16:,]):
                pixels[1024 - (cnt+1)*16 + scnt] = (val[0], val[1], val[2])
            # [A],[B]エリア - 奇数行の並べ替え
            for scnt, val in enumerate(np_rgb[cnt,16:,]):
                pixels[cnt*16 + scnt] = (val[0], val[1], val[2])
    pixels.show()
    return


def main():
    # 消灯
    pixels.fill((0, 0, 0))
    pixels.show()

    cap = cv2.VideoCapture(IMG_PATH)
    while True:
        ret, frame = cap.read()
        if ret == False:
            break

        h, w, _ = frame.shape
        # スクロール表示
        for srt in range(w-32):
            show32x32_pixel(cv2.cvtColor(frame[:,srt:srt+32,:], cv2.COLOR_BGR2RGB))

    time.sleep(0.1)

    # LED消灯
    pixels.fill((0, 0, 0))
    pixels.show()



if __name__ == "__main__":
  main()