ラズパイ4 pythonで GIFファイルを作成する。

概要

 ラズパイ4で GIF(Graphics Interchange Format)ファイルを作成しました。作成したファイルは次の通りです。複数の連続画像からコマ送り動画を作成します。

[作成したGIF画像]

構成・接続

 次の写真の通り、フィギュアをステップモーターで一定角度回転させて、写真手前のWEBカメラで撮影し、連続画像を作成します。カメラはUSB接続なので、空いているポートに挿入します。

 ステップモーターとフィギュア(写真奥)、WEBカメラ(写真手前)
 

 過去投稿と同じですが、モーターは次の様に接続しています。

 

連続画像作成プログラム

 次の様な連続画像を作成します。
 今回は9度づつ回転し、40(枚/回転)の撮影を行っています。ファイル名は撮影時の回転角度を示します。

  

 連続画像生成プログラムは次の通りです

import wiringpi as wp                   
import RPi.GPIO as GPIO
import struct
import time
import sys
import cv2

import os
import glob

SPI_CH = 0                              # SPI チャンネル
SPI_HZ = 1000000                        # SPI 通信速度

GPIO_Nbr = 25                           # GPIO-NO
GPIO.setmode(GPIO.BCM)                  # GPIO-NO 指定
GPIO.setup(GPIO_Nbr,GPIO.IN)            # GPIO INPUT 指定

# L6470初期設定    
def INIT_L6470():
    spi_send([0x00,0x00,0x00,0xc0])     # Reset Device
    spi_send([0x05,0x00,0x0e])          # Acceleration (12)
    spi_send([0x06,0x00,0x0e])          # Deceleration (12)
    spi_send([0x07,0x00,0x0e])          # Maximum speed (10)
    spi_send([0x08,0x00,0x01])          # Minimum speed (13)
    spi_send([0x15,0x03,0xFF])          # Full-step speed (10)
    spi_send([0x16,0x03])               # Micro Step (8)
    spi_send([0x09,0x50])               # Holding Kval (8)
    spi_send([0x0A,0x50])               # Constant Speed Kval (8)
    spi_send([0x0B,0x50])               # Acceleration starting Kval (8)
    spi_send([0x0C,0x50])               # Deceleration starting Kbal (8)

# SPI データ送信
def spi_send(spi_dat_ary):
    for itm in spi_dat_ary:
        tmp=struct.pack("B",itm)
        wp.wiringPiSPIDataRW(SPI_CH, tmp)

# JOG (SPEED指定 : 0---30000)
def L6470_run(run_spd):
    # 方向検出
    if (run_spd > 0):
        dir = 0x50
        spd = run_spd
    else:
        dir = 0x51
        spd = -1 * run_spd
        
    L6470_SEND_MOVE_CMD( dir , spd )

# 移動量指定移動
def L6470_POSITIONING(MV_DIST):
    # 方向検出
    if (MV_DIST > 0):
        dir = 0x40
    else:
        dir = 0x41
        MV_DIST = -1 * MV_DIST
        
    L6470_SEND_MOVE_CMD( dir , MV_DIST )

# 絶対位置指定移動
def L6470_MOVE_ABS(MV_DIST):
    dir = 0x60
    if (MV_DIST < 0):
        MV_DIST = -1 * MV_DIST
        
    L6470_SEND_MOVE_CMD( dir , MV_DIST )
        
# データ加工・送信(共通)
def L6470_SEND_MOVE_CMD( cmd , DAT ):
    tmp=[]
    tmp.append(cmd)
    tmp.append((0x0F0000 & DAT) >> 16)
    tmp.append((0x00FF00 & DAT) >> 8)
    tmp.append((0x00FF & DAT))
    spi_send(tmp)
        
# 停止指令
def L6470_STOP():
    spi_send([0xB0])                    # SOFT STOP
    time.sleep(0.2)
    spi_send([0xA8])                    # Hard HiZ
    time.sleep(0.2)

# 原点設定
def L6470_SET_ORIGIN():
    spi_send([0xD8])                    # Reset Position
    time.sleep(0.5)

# 原点移動
def L6470_MOVE_ORIGIN():
    spi_send([0x70])                    # Go Home
    time.sleep(0.5)

# ドライバーBUSY解除待ち
def wait_until_not_busy():
    while True:
        time.sleep(0.2)
        mtr_sts = GPIO.input(GPIO_Nbr)
        #print(mtr_sts)
        
        if GPIO.input(GPIO_Nbr) == GPIO.HIGH :
            #print("L6470 NOT BUSY")
            break    
    time.sleep(0.2)

##################################
# メインプログラム
##################################
if __name__ == "__main__":
    print("PROGRAM START")

    file_list = glob.glob("./pic/*")	# 保存先ファイル削除
    for file in file_list:
        os.remove(file)

    wp.wiringPiSPISetup(SPI_CH,SPI_HZ)  # SPI 接続
    INIT_L6470()                        # L6470初期設定
    L6470_SET_ORIGIN()                  # 原点位置設定(0)

    capture = cv2.VideoCapture(0)
    if capture.isOpened() is False:
        raise IOError

    rot_f = 0
    loop_cnt = 0
    while(True):
        ret,frame=capture.read()
        if ret is False:
            raise IPError

        frm_crp = frame[95:285,210:390]
        
        cv2.imshow('frm_crp',frm_crp)

        k = cv2.waitKey(1)
        if k==27:                       # ESC
            break
        if k==49:                       # 1
            print("MOVE START")
            L6470_MOVE_ABS(0)
            rot_f = 1

        if rot_f > 0 and rot_f < 41 :
            if GPIO.input(GPIO_Nbr) == GPIO.HIGH :
                loop_cnt = loop_cnt + 1
                    
                if loop_cnt > 45 :
                    rot_step = 40 * rot_f
                    file_name = ("000"+str(int(360*(40*(rot_f-1))/1600)))[-3:]
                    cv2.imwrite("./pic/"+file_name + ".jpg" , frm_crp)
                    
                    
                    #print(str(int(360*rot_step/1600))+"deg ("+str(rot_step)+"step)")
                    print( file_name + "deg , " +str(rot_step) + "step")
                    L6470_MOVE_ABS(rot_step)
                    
                    rot_f = rot_f + 1
                    loop_cnt = 0         
            

    GPIO.cleanup()
    capture.release()
    cv2.destroyAllWindows()
    print("PROGRAM END")
    sys.exit()

  

GIFファイル生成プログラム

 連続画像から GIF ファイルを作成するプログラムです。
 プログラムのあるディレクトリ下の “pic” というディレクトリ内にある全 jpeg ファイルを “Pillow” というライブラリで、GIFファイルに変換しています。

from PIL import Image
import glob

images = []

file_list = sorted(glob.glob("./pic/*.jpg"))

for filename in file_list:
    im = Image.open(filename)
    images.append(im)

images[0].save('out1.gif', save_all=True, append_images=images[1:], loop=0, duration=200)

  
 プログラム12行目が、GIFファイルへの変換処理です。

images[0].save('out1.gif', save_all=True, append_images=images[1:], loop=0, duration=200)


 変換保存時の指定パラメータは、下表の様になります。

変数内容
images[0]1フレーム目になるImageオブジェクト
‘out1.gif’保存ファイル名(拡張子:gif)
save_all=TrueTrue : リスト画像全登録 , False : 静止画
append_images=
images[1:]
2フレーム目以降のImageオブジェクトリスト
loop=00 : 無限ループ , 1 : ループしない
duration=200フレーム表示間隔(単位:ミリ秒)
個別設定→duration=[1000, 50, 50, 300,・・・]

   

まとめ

 コマ送り動画なので、容量が小さくできる利点があるのだと思っています。途中で停止したり、反転は出来ないかな(?)という疑問を持ちました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です