OpenCV でビデオカメラ画像の表示,ファイル書き出し,濃淡画像処理(Python を使用)
【目次】
【関連する外部ページ】
- OpenCV の公式ページ: https://opencv.org
 - GitHub の OpenCV のページ: https://github.com/opencv/opencv/releases
 
【付記】 本ページのプログラムはAIのアシストを受けて作成しています
【サイト内の関連ページ】
- OpenCV について [PDF] , [パワーポイント]
 - OpenCVとPythonを活用した画像・ビデオ処理プログラム: 別ページ »
 
前準備
Python のインストール(Windows,Ubuntu 上)
- Windows での Python 3.10,関連パッケージ,Python 開発環境のインストール(winget を使用しないインストール): 別ページ »で説明
 - Ubuntu では,システム Pythonを使うことができる.Python3 開発用ファイル,pip, setuptools のインストール: 別ページ »で説明
 
【サイト内の関連ページ】
- Python のまとめ: 別ページ »にまとめ
 - Google Colaboratory の使い方など: 別ページ »で説明
 
【関連する外部ページ】 Python の公式ページ: https://www.python.org/
Python 開発環境のインストール
- Windows での Python 開発環境として,Jupyter Qt Console, Jupyter ノートブック (Jupyter Notebook), Jupyter Lab, Nteract, spyder のインストール: 別ページ »で説明
 - Windows での PyCharm のインストール: 別ページ »で説明
 - Windows での PyScripter のインストール: 別ページ »で説明
 
OpenCV Python のインストール
Python で OpenCV を動かすためのもの.
OpenCV Python のインストールは:別ページ »で説明1~2 コマンドの実行でインストールできる.
カメラ画像の表示
USB接続できるビデオカメラを準備し,パソコンに接続しておく.
Python プログラムの実行
- Windows では python (Python ランチャーは py)
 - Ubuntu では python3
 
【サイト内の関連ページ】 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 プログラムの実行
- Windows では python (Python ランチャーは py)
 - Ubuntu では python3
 
【サイト内の関連ページ】 Python のまとめ: 別ページ »
【プログラムの機能】
カメラからリアルタイムで映像を取得し,指定した幅(640ピクセル)にリサイズして表示する.アスペクト比を保持しながらフレームをリサイズする.'q'キーで終了する.
【プログラムの説明】
OpenCVを使用したリアルタイムカメラ映像のリサイズと表示
-  opencv-python の使用
opencv-python は,OpenCV を Python から利用するためのもの
 -  リサイズを行う resize_frame 関数
resize_frame関数は,入力された画像を指定した幅にリサイズする.この際,アスペクト比を保持するように高さを計算する.
 -  メインループ
while True ループ内で以下の処理を繰り返す.
- カメラから1フレーム読み取り
 - フレームをリサイズ
 - リサイズしたフレームを表示
 - キー入力のチェック('q'キーで終了)」
 
 -  エラー処理
カメラが開けない場合や,フレームの読み取りに失敗した場合にはエラーメッセージを表示し,プログラムを終了する.
 -  リソースの解放
プログラム終了時には,v.release()でカメラを解放し,cv2.destroyAllWindows()で作成したウィンドウを閉じる.
 -  カスタマイズ
resize_width変数を変更することで,リサイズする幅を調整できる.また,cv2.VideoCapture(0)の0を変更することで,異なるカメラを選択できる.
 
【プログラムの実行法】
このプログラムを実行すると,カメラの映像が表示され,'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
上のコマンドがうまく 
* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる
 
ここでの輪郭抽出は、2値化の結果を利用して輪郭を抽出している
 
* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる
 
次のことを行う
 
* 止めたいとき,右上の「x」をクリックしない.画面の中をクリックしてから,「q」のキーを押して閉じる
 
1列目:フレーム番号、2列目:OTSU の方法による2値化の結果白色となった画素数、3列目:L成分の平均、4列目:a成分の平均、5列目:b成分の平均
OTSU の方法による2値化
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()
輪郭抽出
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()
OTSU の方法による2値化のあと, 数え上げと画素値の平均
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()