JPEG で画像を取得する

i-PRO の監視カメラ i-PRO mini (WV-S7130W) を入手したので、JPEG 形式で映像取得して遊んでみます。

 

 

 

製品紹介ページ:

 

i-PRO mini

 

 

1. CGI 表記仕様

[概要]

JPEG(1shot要求)で接続するための表記を以下に記載します。

「ネットワークカメラCGIコマンドインターフェース仕様書 統合版」[1] では下記あたりに書いてある情報です。

 

http://<カメラのIPアドレス>/cgi-bin/camera?resolution={resolution}

 

補足説明

 

 

2. i-PRO カメラと JPEG 接続して映像を表示してみる

[概要]

とりあえず映像を取得してPC画面に表示するまでをやってみます。

 

[評価環境1]

言語 : Python, 3.10.4
OS : Windows 11 home, 21H2

 

[評価環境2]

言語 : Python, 3.8.10
OS : Ubuntu(WSL), 20.04

 

 

[プログラム]

[プログラムソース "connect_with_jpeg_1.py"]

'''
[Abstract]
    JPEG(1shot) で i-PRO カメラと接続してみる

[Details]
    まずはやってみる

[Library install]
    pip install opencv-python
    pip install numpy
    pip install requests
'''
import requests
from requests.auth import HTTPDigestAuth
import numpy as np
import cv2

user_id     = "user-id"         # ご使用のカメラ設定に合わせて変更
user_pw     = "password"        # ご使用のカメラ設定に合わせて変更
host        = "192.168.0.10"    # ご使用のカメラ設定に合わせて変更
winname     = "VIDEO"           # ウィンドウタイトル
resolution  = 1920              # 解像度

# URL
url = f"http://{host}/cgi-bin/camera?resolution={resolution}"

while True:

    # 画像の取得
    rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw))

    # binary データを ndarray 型へ変換
    img_buf= np.frombuffer(rs.content, dtype=np.uint8)

    # バイナリデータをデコードして画像データにする
    img1 = cv2.imdecode(img_buf, cv2.IMREAD_UNCHANGED)

    # リサイズ
    img2 = cv2.resize(img1, (1280, 760))

    # 映像表示
    cv2.imshow(winname, img2)
    cv2.waitKey(1)      # imshow() を表示するためにはこの1行が必要!! 

 

上記プログラムを動かしてみます。実行はこんな感じで行います。

python connect_with_jpeg_1.py

 

Windows環境で複数の Python バージョンをインストールしている場合、下図のような感じで実行バージョンを指定することもできます。
こちらはバージョン 3.10 の Python で実行を指示する例です。

py -3.10 connect_with_jpeg_1.py

 

上記プログラムを動かした様子を動画で示します。

こんなに簡単なプログラムでちゃんと映像表示を実現することができました。

5fps(毎秒5コマ)ぐらいの表示でしょうか。JPEGなので妥当な結果だと思います。

[動画] JPEG でカメラと接続して映像表示してみた様子

 

本プログラムでは下記3つのライブラリを使用しています。概要だけ記載します。

ライブラリ名 概要
cv2 画像処理などで有名な OpenCV です。
numpy 数値計算を行うライブラリです。
requests ネットワーク通信などの処理を行います。

 

 

 

3.プログラムを改善する

[概要]

前章で作成したプログラムはとても簡単に作成できましたが、いろいろと課題がありました。
とりあえず下記3つの課題を解決してみます。

 

課題1
プログラムを起動するたびにウィンドウ位置が変わる。場合によっては画面外へ表示する場合もあって不便。
適当に画面内に収まる場所に表示してほしい。
⇨ 指定する場所にウィンドウを表示するようにします。

 

課題2
プログラムを終了するのが大変。
ウィンドウ右上の×を押すとウィンドウがいったん消えるが、すぐに再表示されて終われない。
⇨ ウィンドウ右上の✕ボタンでプログラムを終了できるようにします。

 

課題3
同様に、任意のキー入力でプログラムを終了できるとうれしい。
⇨ "z" キー押下でプログラムを終了できるようにします。

 

 

 

[評価環境1]

言語 : Python, 3.10.4
OS : Windows 11 home, 21H2

 

[評価環境2]

言語 : Python, 3.8.10
OS : Ubuntu(WSL), 20.04

 

 

[プログラム]

[プログラムソース "connect_with_jpeg_2.py"]

'''
[Abstract]
    JPEG(1shot) で i-PRO カメラと接続してみる

[Details]
    "connect_with_jpeg_1.py" で確認した下記3つの課題を改善してみます。

    [課題1]
    プログラムを起動するたびにウィンドウ位置が変わる。場合によっては画面外へ表示する場合もあって不便。
    適当に画面内に収まる場所に表示してほしい。
    -> 指定する場所にウィンドウを表示するようにします。
    [課題2]
    プログラムを終了するのが大変。
    ウィンドウ右上の×を押すとウィンドウがいったん消えるが、すぐに再表示されて終われない。
    -> ウィンドウ右上の✕ボタンでプログラムを終了できるようにします。
    [課題3]
    同様に、任意のキー入力でプログラムを終了できるとうれしい。
    -> "z" キー押下でプログラムを終了できるようにします。

[Library install]
    pip install opencv-python
    pip install numpy
    pip install requests
'''

import requests
from requests.auth import HTTPDigestAuth
import numpy as np
import cv2

user_id     = "user-id"         # ご使用のカメラ設定に合わせて変更
user_pw     = "password"        # ご使用のカメラ設定に合わせて変更
host        = "192.168.0.10"    # ご使用のカメラ設定に合わせて変更
winname     = "VIDEO"           # ウィンドウタイトル
resolution  = 1920              # 解像度

# URL
url = f"http://{host}/cgi-bin/camera?resolution={resolution}"

#
initialized = False

# Exception 定義
BackendError = type('BackendError', (Exception,), {})

'''
[Abstract]
    対象ウィンドウが存在するかを確認する。
[Param]
    winname :       ウィンドウタイトル
[Return]
    True :          対象ウィンドウは存在する
    False :         対象ウィンドウは存在しない
[Exception]
    BackendError :  バックエンドで使用している Qt でエラー発生
'''
def IsWindowVisible(winname):
    try:
        ret = cv2.getWindowProperty(winname, cv2.WND_PROP_VISIBLE)
        if ret == -1:
            raise BackendError('Use Qt as backend to check whether window is visible or not.')

        return bool(ret)

    except cv2.error:
        return False



while True:

    # 画像の取得
    rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw))

    # binary データを ndarray 型へ変換
    img_buf= np.frombuffer(rs.content, dtype=np.uint8)

    # バイナリデータをデコードして画像データにする
    img1 = cv2.imdecode(img_buf, cv2.IMREAD_UNCHANGED)

    # リサイズ
    img2 = cv2.resize(img1, (1280, 760))

    # 映像表示
    cv2.imshow(winname, img2)

    if initialized==False:
        # ウィンドウ表示位置が安定しないので、最初の起動時のみ表示場所を指定
        cv2.moveWindow(winname, 100, 100)
        initialized = True
        
    # "z" キーを押されていたら終了
    k = cv2.waitKey(1)      # imshow() を表示するためにはこの1行が必要!! 
    if k == ord("z"):
        break

    # 指定ウィンドウが無かったら終了
    if not IsWindowVisible(winname):
        break

 

 

 

4. OpenCV で顔検知を加えてみる

[概要]

OpenCV を使って顔検知を行って、見つけた顔部分に赤枠を描画してみます。 

 

[評価環境1]

言語 : Python, 3.10.4
OS : Windows 11 home, 21H2

 

[評価環境2]

言語 : Python, 3.8.10
OS : Ubuntu(WSL), 20.04

 

 

[プログラム]

[プログラムソース "connect_with_jpeg_3.py"]

'''
[Abstract]
    JPEG(1shot) でカメラと接続します。

[Details]
    ここでは opencv を使って顔検知を追加してみます。

[Library install]
    pip install opencv-python
    pip install numpy
    pip install requests

[OpenCV]
    下記URLからファイル "haarcascade_frontalface_alt2.xml" を入手すること
    https://github.com/opencv/opencv/tree/master/data/haarcascades
'''

import requests
from requests.auth import HTTPDigestAuth
import numpy as np
import cv2


user_id     = "admin"           # ご使用のカメラ設定に合わせて変更
user_pw     = "Admin12345"      # ご使用のカメラ設定に合わせて変更
host        = "192.168.2.126"   # ご使用のカメラ設定に合わせて変更
winname     = "VIDEO"           # ウィンドウタイトル
resolution  = 1920              # 解像度

# URL
url = f"http://{host}/cgi-bin/camera?resolution={resolution}"

# 顔を識別するためのファイル
cascade_file = "haarcascade_frontalface_alt2.xml"       # 顔
#cascade_file = "haarcascade_eye.xml"                   # 目?
#cascade_file = "haarcascade_eye_tree_eyeglasses.xml"   # 目?
cascade = cv2.CascadeClassifier(cascade_file)

#
initialized = False

# Exception 定義
BackendError = type('BackendError', (Exception,), {})

'''
[Abstract]
    対象ウィンドウが存在するかを確認する。
[Param]
    winname :       ウィンドウタイトル
[Return]
    True :          対象ウィンドウは存在する
    False :         対象ウィンドウは存在しない
[Exception]
    BackendError :  バックエンドで使用している Qt でエラー発生
'''
def IsWindowVisible(winname):
    try:
        ret = cv2.getWindowProperty(winname, cv2.WND_PROP_VISIBLE)
        if ret == -1:
            raise BackendError('Use Qt as backend to check whether window is visible or not.')

        return bool(ret)

    except cv2.error:
        return False



while True:

    # 画像の取得
    rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw))

    # binary データを ndarray 型へ変換
    img_buf= np.frombuffer(rs.content, dtype=np.uint8)

    # バイナリデータをデコードして画像データ(OpenCV形式)にする
    img1 = cv2.imdecode(img_buf, cv2.IMREAD_UNCHANGED)

    # 顔検出のためにグレイスケール画像に変換
    img_gray = cv2.imdecode(img_buf, cv2.IMREAD_GRAYSCALE)

    # 検出した顔の位置情報を取得
    face_list = cascade.detectMultiScale(img_gray, minSize=(100, 100))

    # 顔を検出したらループ処理を終了
    if len(face_list) != 0:
        #print("Find face.")

        for (pos_x, pos_y, w, h) in face_list:
            print(f"pos_x = {pos_x}, pos_y = {pos_y}, w = {w}, h = {h}")
            cv2.rectangle(img1, (pos_x, pos_y), (pos_x + w, pos_y + h), (0,0,255), thickness=5)

    # リサイズ
    img2 = cv2.resize(img1, (1280, 760))

    # 映像表示
    cv2.imshow(winname, img2)

    if initialized==False:
        # ウィンドウ表示位置が安定しないので、最初の起動時のみ表示場所を指定
        cv2.moveWindow(winname, 100, 100)
        initialized = True
        
    # "z" キーを押されていたら終了
    k = cv2.waitKey(1)
    if k == ord("z"):
        break

    # 指定ウィンドウが無かったら終了
    if not IsWindowVisible(winname):
        break

 

 

 

顔検知した結果の例

 

 

 

5. 連番の JPEG ファイルで保存する

受信した画像を 1 から始まる連番のファイル名 (image_NNNNNN.jpg) で JPEG ファイルとして保存してみます。

 

 

[プログラムソース "connect_with_jpeg_4.py"]

'''
[Abstract]
    JPEG(1shot) でカメラと接続します。

[Details]
    受信した JPEG 画像をファイル保存します。
    ファイル名の末尾に6ケタの番号を付けて連番で保存します。

[Library install]
    pip install opencv-python
    pip install numpy
    pip install requests
'''

import requests
from requests.auth import HTTPDigestAuth
import numpy as np
import cv2
import os


user_id     = "user-id"         # ご使用のカメラ設定に合わせて変更
user_pw     = "password"        # ご使用のカメラ設定に合わせて変更
host        = "192.168.0.10"    # ご使用のカメラ設定に合わせて変更
winname     = "VIDEO"           # ウィンドウタイトル
resolution  = 1920              # 解像度
pathOut     = 'image'           # 画像ファイル保存フォルダ

# URL
url = f"http://{host}/cgi-bin/camera?resolution={resolution}"

# Exception 定義
BackendError = type('BackendError', (Exception,), {})


'''
[Abstract]
    対象ウィンドウが存在するかを確認する。
[Param]
    winname :       ウィンドウタイトル
[Return]
    True :          対象ウィンドウは存在する
    False :         対象ウィンドウは存在しない
[Exception]
    BackendError :  バックエンドで使用している Qt でエラー発生
'''
def IsWindowVisible(winname):
    try:
        ret = cv2.getWindowProperty(winname, cv2.WND_PROP_VISIBLE)
        if ret == -1:
            raise BackendError('Use Qt as backend to check whether window is visible or not.')

        return bool(ret)

    except cv2.error:
        return False


'''
[Abstract]
    バイナリデータを指定ファイル名で保存する
[Param]
    data :      保存するバイナリデータ
    filename :  ファイル名
'''
def SaveBinaryData(data, filename):
    fout = open(filename, 'wb')
    fout.write(data)
    fout.close()


'''
[Abstract]
    main 関数
'''
if __name__ == '__main__':

    initialized = False
    count = 0
    if not os.path.exists(pathOut):
        os.mkdir(pathOut)

    while True:

        # 画像の取得
        rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw))

        # ファイル保存
        count += 1
        filename = os.path.join(pathOut, 'image_{:06d}.jpg'.format(count))
        SaveBinaryData(rs.content, filename)

        # binary データを ndarray 型へ変換
        img_buf= np.frombuffer(rs.content, dtype=np.uint8)

        # バイナリデータをデコードして画像データにする
        img1 = cv2.imdecode(img_buf, cv2.IMREAD_UNCHANGED)

        # リサイズ
        img2 = cv2.resize(img1, (1280, 760))

        # 映像表示
        cv2.imshow(winname, img2)

        if initialized==False:
            # ウィンドウ表示位置が安定しないので、最初の起動時のみ表示場所を指定
            cv2.moveWindow(winname, 100, 100)
            initialized = True
            
        # "z" キーを押されていたら終了
        k = cv2.waitKey(1)
        if k == ord("z"):
            break

        # 指定ウィンドウが無かったら終了
        if not IsWindowVisible(winname):
            break

 

 

 

6. 映像切断時の再接続処理を追加

ここまでのプログラムは、カメラとの接続を切断すると接続が復活しませんでした。
例えばカメラを再起動するなどしても自動的に再接続を行って映像表示できるようにプログラムを修正してみます。

"connect_with_jpeg_2.py" を元に再接続処理を追加してこの問題を解決してみたいと思います。

 

ポイント

 

NOTE

 

 

[評価環境]

言語 : Python, 3.10.4
OS : Windows 11 home, 21H2

 

 

[プログラムソース "connect_with_jpeg_5.py"]

'''
[Abstract]
    Try connecting to an i-PRO camera with JPEG(1 shot).
    JPEG(1 shot) で i-PRO カメラと接続してみる

[Details]
    Add reconnection when video is disconnected to "connect_with_jpeg_2.py".
    "connect_with_jpeg_2.py" へ映像切断時の再接続処理を追加する。

[Author]
    kinoshita hidetoshi (木下英俊)

[Library install]
    pip install opencv-python
    pip install numpy
    pip install requests
'''

import requests
from requests.auth import HTTPDigestAuth
import numpy as np
import cv2
from urllib3 import HTTPConnectionPool

user_id     = "user-id"         # Change to match your camera setting
user_pw     = "password"        # Change to match your camera setting
host        = "192.168.0.10"    # Change to match your camera setting
winname     = "VIDEO"           # Window title
resolution  = 1920              # Resolution

# URL
url = f"http://{host}/cgi-bin/camera?resolution={resolution}"

#
initialized = False

# Exception Definition
BackendError = type('BackendError', (Exception,), {})

'''
[Abstract]
    Check if the target window exists.
    対象ウィンドウが存在するかを確認する。
[Param]
    winname :       Window title
[Return]
    True :          exist
                    存在する
    False :         not exist
                    存在しない
[Exception]
    BackendError :
'''
def IsWindowVisible(winname):
    try:
        ret = cv2.getWindowProperty(winname, cv2.WND_PROP_VISIBLE)
        if ret == -1:
            raise BackendError('Use Qt as backend to check whether window is visible or not.')

        return bool(ret)

    except cv2.error:
        return False


while True:
    try:
        # Request and receive image from camera.
        rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw), timeout=10)

        # Convert from binary to ndarray.
        img_buf= np.frombuffer(rs.content, dtype=np.uint8)

        # Convert from ndarray to OpenCV image.
        img1 = cv2.imdecode(img_buf, cv2.IMREAD_UNCHANGED)

        # Please modify the value to fit your PC screen size.
        img2 = cv2.resize(img1, (1280, 760))

        # Display image.
        cv2.imshow(winname, img2)

        if initialized==False:
            # Specify window position only once at startup.
            cv2.moveWindow(winname, 100, 100)
            initialized = True
            
        # Press the "z" key to finish.
        k = cv2.waitKey(1)      # necessary to display the video by imshow ()
        if k == ord("z"):
            break

        # Exit the program if there is no specified window.
        if not IsWindowVisible(winname):
            break

    except KeyboardInterrupt:
        # Press '[ctrl] + [c]' on the console to exit the program.
        print("KeyboardInterrupt")
        break

    except requests.ConnectTimeout as e:
        print(e)

    except requests.exceptions.Timeout as e:
        print(e)

    except Exception as e:
        print(e)

cv2.destroyAllWindows()

 

 

 

7. GUIで映像表示してみる(tkinter)

ここまでのプログラムは全てOpenCVが作成するウィンドウ表示でした。
ここでは独自の GUI を作成してここに映像表示する例を示します。

GUI 表示の実現方法もいろいろありますが、ここでは Python 標準の tkinter を使用してみます。

 

tkinter のインストール方法は環境により異なるようです。各人の環境にあった方法をインターネットで調べて実施してください。

 

ポイント

 

記事「RTSPで画像を取得する」中で既に GUI 版を作成済みなので、プログラム "connect_with_rtsp_6_3.py" をベースに変更箇所のみをわかるように以下で記載します。ほとんど同じ内容で実現できます。

 

[評価環境]

言語 : Python, 3.10.4
  Tcl/Tk, 8.6
OS : Windows 11 home, 21H2

 

[プログラムソース "connect_with_mjpeg_6.py"]

'''
[Abstract]
    Try connecting to an i-PRO camera with JPEG.
    JPEG で i-PRO カメラと接続してみる。

[Details]
    Display the video with GUI using tkinter.
    Add menus and buttons to make it look like a GUI app.
    
    tkinter を使ったGUIで映像を表示します。
    メニューとボタンを追加してGUIアプリらしくします。
    
[Library install]
    I think it will probably only work with built-in modules in Python.
'''

import tkinter as tk
from tkinter import messagebox
from PIL import Image, ImageTk, ImageOps

import multiprocessing as mp
import time
import io
import requests
from requests.auth import HTTPDigestAuth


user_id     = "user-id"         # Change to match your camera setting
user_pw     = "password"        # Change to match your camera setting
host        = "192.168.0.10"    # Change to match your camera setting
winname     = "VIDEO"           # Window title
resolution  = 1920              # Resolution
url         = f"http://{host}/cgi-bin/camera?resolution={resolution}"


class Application(tk.Frame):
    def __init__(self, master = None):
        super().__init__(master)
        self.pack()

        # Window settings.
        self.master.title("Display i-PRO camera with tkinter")      # Window title
        self.master.geometry("800x600+100+100")                     # Window size, position

        # Event registration for window termination.
        self.master.protocol("WM_DELETE_WINDOW", self.on_closing_window)

        # Create menu.
        menubar = tk.Menu(self.master)
        self.master.configure(menu=menubar)
        filemenu = tk.Menu(menubar)
        menubar.add_cascade(label='File', menu=filemenu)
        filemenu.add_command(label='Quit', command = self.on_closing_window)

        # Create button_frame
        self.button_frame = tk.Frame(self.master, padx=10, pady=10, relief=tk.RAISED, bd=2)
        self.button_frame.pack(side = tk.BOTTOM, fill=tk.X)

        # Create quit_button
        self.quit_button = tk.Button(self.button_frame, text='Quit', width=10, command = self.on_closing_window)
        self.quit_button.pack(side=tk.RIGHT)
        
        # Create canvas.
        self.canvas = tk.Canvas(self.master)

        # Add mouse click event to canvas.
        self.canvas.bind('<Button-1>', self.canvas_click)

        # Place canvas.
        self.canvas.pack(expand = True, fill = tk.BOTH)

        # Create image receiving process and queue
        self.imageQueue = mp.Queue()
        self.request = mp.Value('i', 0)     # -1 : Exit ReceiveImageProcess.
                                            #  0 : Normal.
                                            #  1 : Connect camera.
                                            #  2 : Release camera.
        self.p = mp.Process(target=ReceiveImageProcess, args=(self.imageQueue, self.request))
        self.p.start()

        # Raise a video display event (disp_image) after 500m
        self.disp_id = self.after(500, self.disp_image)

    def on_closing_window(self):
        ''' Window closing event. '''

        if messagebox.askokcancel("QUIT", "Do you want to quit?"):
            # Request terminate process self.p.
            self.request.value = -1

            # Waiting for process p to finish
            time.sleep(1)

            # Flash buffer.
            # The program cannot complete p.join() unless the imageQueue is emptied.
            for i in range(self.imageQueue.qsize()):
                pil_image = self.imageQueue.get()

            # Wait for process p to be terminated.
            self.p.join()
            self.master.destroy()
            print("Finish Application.")

    def canvas_click(self, event):
        ''' Event handling with mouse clicks on canvas '''

        if self.disp_id is None:
            # Connect camera.
            self.request.value = 1
            # Display image.
            self.disp_image()

        else:
            # Release camera.
            self.request.value = 2
            # Cancel scheduling
            self.after_cancel(self.disp_id)
            self.disp_id = None

    def disp_image(self):
        ''' Display image on Canvas '''

        # If there is data in the imageQueue, the program receives the data and displays the video.
        num = self.imageQueue.qsize()
        if num > 0:
            if (num > 5):
                num -= 1
            for i in range(num):
                pil_image = self.imageQueue.get()

            # Get canvas size.
            canvas_width = self.canvas.winfo_width()
            canvas_height = self.canvas.winfo_height()

            # Resize the image to the size of the canvas without changing the aspect ratio.
            # アスペクトを維持したまま画像を Canvas と同じサイズにリサイズ
            pil_image = ImageOps.pad(pil_image, (canvas_width, canvas_height))

            # Convert image from PIL.Image to PhotoImage
            # PIL.Image から PhotoImage へ変換する
            self.photo_image = ImageTk.PhotoImage(image=pil_image)

            # Display image on the canvas.
            self.canvas.create_image(
                canvas_width / 2,       # Image display position (center of the canvas)
                canvas_height / 2,                   
                image=self.photo_image  # image data
                )
            
        else:
            pass

        # Raise a video display event (disp_image) after 1ms.
        self.disp_id = self.after(1, self.disp_image)


def ReceiveImageProcess(imageQueue, request):
    '''
    Receive Image Process.

    Args:
        imageQueue      [o] This process stores the received image data in the imageQueue.
        request         [i] Shared memory for receiving requests from the main process.
                            -1: Terminate process.
                             2: Do not get camera image.
                             *: At other values, the program gets the camera image.
    Returns:
        None
    Raises
        None
    '''

    while request.value != -1:
        if request.value != 2:
            try:
                # Request and receive image from camera.
                rs = requests.get(url, auth=HTTPDigestAuth(user_id, user_pw), timeout=10)
                if imageQueue.qsize() < 10:
                    image_bin = io.BytesIO(rs.content)
                    pil_image = Image.open(image_bin)
                    imageQueue.put(pil_image)

            except requests.ConnectTimeout as e:
                print(e)

            except requests.exceptions.Timeout as e:
                print(e)

            except Exception as e:
                print(e)

        else:
            time.sleep(1)


if __name__ == "__main__":
    '''
    __main__ function.
    '''
    root = tk.Tk()
    app = Application(master = root)
    app.mainloop()

 

 

 

ライセンス

本ページの情報は、特記無い限り下記 MIT ライセンスで提供されます。

MIT License

Copyright (c) 2022 Kinoshita Hidetoshi

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 

 

参考

 

 


 

記載

2022-05-14 - 「7. GUIで映像表示してみる(tkinter)」を追加
  - 「6. 映像切断時の再接続処理を追加 」を追加
2022-04-23 - 「5. 連番の JPEG ファイルで保存する」を追加
2022-04-17 - ライセンスを追加
2022-03-26 - 3章を挿入
2022-03-19 - 新規作成

 

Programming Items トップページ

プライバシーポリシー