OpenCV でビデオカメラ画像の表示,ファイル書き出し,濃淡画像処理(Python を使用)

目次

【関連する外部ページ】

【付記】 本ページのプログラムはAIのアシストを受けて作成しています

【サイト内の関連ページ】

前準備

Python のインストール(Windows,Ubuntu 上)

サイト内の関連ページ

関連する外部ページPython の公式ページ: https://www.python.org/

Python 開発環境のインストール

OpenCV Python のインストール

Python で OpenCV を動かすためのもの.

OpenCV Python のインストールは:別ページ »で説明1~2 コマンドの実行でインストールできる.

カメラ画像の表示

USB接続できるビデオカメラを準備し,パソコンに接続しておく.

Python プログラムの実行

【サイト内の関連ページ】 Python のまとめ: 別ページ »

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    cv2.imshow("カメラ映像", f)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

表示サイズを変える

ビデオカメラを準備し,パソコンに接続しておく.

Python プログラムの実行

【サイト内の関連ページ】 Python のまとめ: 別ページ »

プログラムの機能

カメラからリアルタイムで映像を取得し,指定した幅(640ピクセル)にリサイズして表示する.アスペクト比を保持しながらフレームをリサイズする.'q'キーで終了する.

プログラムの説明

OpenCVを使用したリアルタイムカメラ映像のリサイズと表示

プログラムの実行法

このプログラムを実行すると,カメラの映像が表示され,'q'キーで終了できる. 注意点として,カメラへのアクセス権限が必要で,既定(デフォルト)ではインデックス0のカメラを使用する.リサイズ幅はプログラムを書き換えることで調整可能である.また,プログラムは適切にリソースを解放するが,強制終了時('q'キーで終了しなかった場合)にはカメラが解放されない場合がある.

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # フレームをリサイズ
    resized_frame = resize_frame(f, resize_width)
    
    cv2.imshow("カメラ映像", resized_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

カラー画像から濃淡画像への変換

USB接続できるビデオカメラを準備し,パソコンに接続しておく.

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def convert_to_grayscale(frame):
    return cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # フレームをリサイズ
    resized_frame = resize_frame(f, resize_width)
    
    # カラー画像を濃淡画像に変換
    gray_frame = convert_to_grayscale(resized_frame)
    
    # カラー画像を表示
    cv2.imshow("カラーカメラ映像", resized_frame)
    
    # 濃淡画像を表示
    cv2.imshow("濃淡カメラ映像", gray_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

動画ファイルの書き出し

USB接続できるビデオカメラを準備し,パソコンに接続しておく.

import cv2
import numpy as np
import time

# 表示用にリサイズする幅を指定(例:640ピクセル)
display_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def create_video_writer(output_filename, fps, frame_size):
    fourcc = cv2.VideoWriter_fourcc(*'XVID')
    return cv2.VideoWriter(output_filename, fourcc, fps, frame_size)

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

# 動画ファイルの設定
fps = 20.0
frame_width = int(v.get(cv2.CAP_PROP_FRAME_WIDTH))
frame_height = int(v.get(cv2.CAP_PROP_FRAME_HEIGHT))
frame_size = (frame_width, frame_height)
output_filename = f"output_{time.strftime('%Y%m%d_%H%M%S')}.avi"
video_writer = create_video_writer(output_filename, fps, frame_size)

print(f"録画を開始します: {output_filename}")

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # オリジナルのフレームを動画ファイルに書き込む
    video_writer.write(f)
    
    # 表示用にフレームをリサイズ
    display_frame = resize_frame(f, display_width)
    
    cv2.imshow("カメラ映像", display_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
video_writer.release()
cv2.destroyAllWindows()

print(f"録画を終了しました: {output_filename}")

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

止めると、動画ファイル C:/image/output.avi ができる。ディレクトリ C:/image は前もって作っておくこと

ヒストグラム平坦化 (histogram equalization)

ヒストグラム平坦化は、モノクロ画像の表示をあざやかにするなどで役に立つ方法

USB接続できるビデオカメラを準備し,パソコンに接続しておく.

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def apply_clahe(frame):
    # グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # CLAHEオブジェクトを作成
    # clipLimit: コントラスト制限のしきい値。デフォルトは40.0
    # tileGridSize: 局所領域のサイズ。デフォルトは(8, 8)
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    
    # CLAHEを適用
    equalized = clahe.apply(gray)
    
    return equalized

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # フレームをリサイズ
    resized_frame = resize_frame(f, resize_width)
    
    # ヒストグラム平坦化を適用
    equalized_frame = apply_clahe(resized_frame)
    
    # 元の画像とヒストグラム平坦化後の画像を横に並べて表示
    combined_frame = np.hstack((cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY), equalized_frame))
    cv2.imshow("Original vs Equalized", combined_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

2値化

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def apply_adaptive_threshold(frame):
    # グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # 適応的閾値処理を適用
    # パラメータの説明:
    # cv2.ADAPTIVE_THRESH_GAUSSIAN_C: ガウス重み付け(周辺ピクセルの重要度が中心から離れるほど小さくなる)
    # 11: 閾値を計算する際の近傍領域のサイズ(奇数である必要がある)
    # 2: 計算された閾値から引く定数
    binary = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, 
                                   cv2.THRESH_BINARY, 11, 2)
    
    return binary

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # フレームをリサイズ
    resized_frame = resize_frame(f, resize_width)
    
    # 二値化を適用
    binary_frame = apply_adaptive_threshold(resized_frame)
    
    # 元の画像と二値化後の画像を横に並べて表示
    combined_frame = np.hstack((cv2.cvtColor(resized_frame, cv2.COLOR_BGR2GRAY), binary_frame))
    cv2.imshow("Original vs Binary", combined_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

輪郭抽出

import cv2
import numpy as np

# リサイズする幅を指定(例:640ピクセル)
resize_width = 640

def resize_frame(frame, width):
    height = int(frame.shape[0] * (width / float(frame.shape[1])))
    return cv2.resize(frame, (width, height), interpolation=cv2.INTER_AREA)

def extract_contours(frame):
    # グレースケールに変換
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    # Cannyエッジ検出を適用
    # パラメータの説明:
    # 100: 下側の閾値。この値以下の勾配はエッジとして検出されません。
    # 200: 上側の閾値。この値以上の勾配は確実にエッジとして検出されます。
    edges = cv2.Canny(gray, 100, 200)
    
    # 輪郭を検出
    # cv2.RETR_EXTERNAL: 最も外側の輪郭のみを検出
    # cv2.CHAIN_APPROX_SIMPLE: 輪郭を表現する点を削減し、メモリを節約
    contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    
    # 輪郭を描画
    contour_image = np.zeros_like(frame)
    cv2.drawContours(contour_image, contours, -1, (0, 255, 0), 2)
    
    return contour_image

v = cv2.VideoCapture(0)

if not v.isOpened():
    print("カメラを開けませんでした。")
    exit()

while True:
    r, f = v.read()
    if not r:
        print("フレームの読み取りに失敗しました。")
        break
    
    # フレームをリサイズ
    resized_frame = resize_frame(f, resize_width)
    
    # 輪郭抽出を適用
    contour_frame = extract_contours(resized_frame)
    
    # 元の画像と輪郭抽出後の画像を横に並べて表示
    combined_frame = np.hstack((resized_frame, contour_frame))
    cv2.imshow("Original vs Contours", combined_frame)
    
    # 'q'キーで終了
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

動画ファイルを使って試す

前準備として,動画ファイルを準備する

ここで使用する動画ファイル:sample1.mp4

この動画ファイルのダウンロードは, Windows でコマンドプロンプトを管理者として開き 次のコマンドを実行する.

mkdir c:\image
cd c:\image
curl -O https://www.kkaneko.jp/sample/face/sample1.mp4

上のコマンドがうまく実行できないときは, sample1.mp4 ダウンロードし、C:/image に置いておく

OTSU の方法による2値化

Ubuntu, RaspberryPi のときは,「IMROOT=os.environ['LOCALAPPDATA'] + '/'」の行を,「IMROOT="/usr/local/image/"」のように書き換える.
import os
import numpy as np
import cv2
IMROOT=os.environ['LOCALAPPDATA'] + '/'

v = cv2.VideoCapture(IMROOT + 'sample1.mp4')
while(v.isOpened()):
    r, f = v.read()
    if ( r == False ):
        break
    mono = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    r, dst = cv2.threshold(mono, 0, 255, cv2.THRESH_OTSU)
    cv2.imshow("f", f)
    cv2.imshow("dst", dst)
    # Press Q to exit
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

輪郭抽出

ここでの輪郭抽出は、2値化の結果を利用して輪郭を抽出している

Ubuntu, RaspberryPi のときは,「IMROOT=os.environ['LOCALAPPDATA'] + '/'」の行を,「IMROOT="/usr/local/image/"」のように書き換える.
import os
import numpy as np
import cv2
IMROOT=os.environ['LOCALAPPDATA'] + '/'

v = cv2.VideoCapture(IMROOT + 'sample1.mp4')
while(v.isOpened()):
    r, f = v.read()
    if ( r == False ):
        break
    mono = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    r, dst = cv2.threshold(mono, 0, 255, cv2.THRESH_OTSU)
    contours, hierarchy = cv2.findContours(dst, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cv2.drawContours(f, contours, -1, (0, 255, 0), 3)
    cv2.imshow("f", f)
    # Press Q to exit
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
cv2.destroyAllWindows()

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

OTSU の方法による2値化のあと, 数え上げと画素値の平均

次のことを行う

Ubuntu, RaspberryPi のときは,「IMROOT=os.environ['LOCALAPPDATA'] + '/'」の行を,「IMROOT="/usr/local/image/"」のように書き換える.
import os
import numpy as np
import cv2
IMROOT=os.environ['LOCALAPPDATA'] + '/'

v = cv2.VideoCapture(IMROOT + 'sample1.mp4')
i = 0
res = open(IMROOT + 'result.csv', mode='a')
while(v.isOpened()):
    r, f = v.read()
    if ( r == False ):
        break
    mono = cv2.cvtColor(f, cv2.COLOR_BGR2GRAY)
    r, dst = cv2.threshold(mono, 0, 255, cv2.THRESH_OTSU)
    lab = cv2.cvtColor(f, cv2.COLOR_BGR2LAB)
    cv2.imshow("f", f)
    cv2.imshow("dst", dst)
    cv2.imshow("L", lab[:,:,0])
    cv2.imshow("A", lab[:,:,1])
    cv2.imshow("B", lab[:,:,2])
    total = np.sum(dst)
    print( "%d, %d, %f, %f, %f" % (i, total, np.sum(lab[:,:,0])/total, np.sum(lab[:,:,1])/total, np.sum(lab[:,:,2])/total ) )
    res.write( "%d, %d, %f, %f, %f\n" % (i, total, np.sum(lab[:,:,0])/total, np.sum(lab[:,:,1])/total, np.sum(lab[:,:,2])/total ) )
    i = i + 1
    # Press Q to exit
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

v.release()
res.close()
cv2.destroyAllWindows()

* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる

1列目:フレーム番号、2列目:OTSU の方法による2値化の結果白色となった画素数、3列目:L成分の平均、4列目:a成分の平均、5列目:b成分の平均