OpenCV ガウシアンフィルタによるぼかし(入力:静止画)(ソースコードと説明と利用ガイド)

プログラム利用ガイド

1. このプログラムの利用シーン

静止画像のノイズ除去や平滑化が必要な場面で使用する。カメラで撮影した画像や既存の画像ファイルに対して、エッジを保持したまま不要なノイズを除去する処理を実行できる。

2. 主な機能

3. 基本的な使い方

  1. 起動と入力の選択:

    プログラム起動後、0(画像ファイル)、1(カメラ)、2(サンプル画像)のいずれかを入力してEnterキーを押す。

  2. 画像ファイルの選択(0を選択した場合):

    ファイル選択ダイアログが表示されるため、処理したい画像ファイルを1つまたは複数選択する。

  3. カメラでの撮影(1を選択した場合):

    カメラ映像が表示される。スペースキーを押すと撮影と処理が実行される。複数回撮影可能である。qキーで終了する。

  4. 結果の確認:

    処理完了後、元画像と処理後画像が個別のウィンドウで表示される。任意のキーを押すとウィンドウが閉じる。

4. 便利な機能

Python開発環境,ライブラリ類

ここでは、最低限の事前準備について説明する。機械学習や深層学習を行う場合は、NVIDIA CUDA、Visual Studio、Cursorなどを追加でインストールすると便利である。これらについては別ページ https://www.kkaneko.jp/cc/dev/aiassist.htmlで詳しく解説しているので、必要に応じて参照してください。

Python 3.12 のインストール

インストール済みの場合は実行不要。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要である。

REM Python をシステム領域にインストール
winget install --scope machine --id Python.Python.3.12 -e --silent
REM Python のパス設定
set "PYTHON_PATH=C:\Program Files\Python312"
set "PYTHON_SCRIPTS_PATH=C:\Program Files\Python312\Scripts"
echo "%PATH%" | find /i "%PYTHON_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_PATH%" /M >nul
echo "%PATH%" | find /i "%PYTHON_SCRIPTS_PATH%" >nul
if errorlevel 1 setx PATH "%PATH%;%PYTHON_SCRIPTS_PATH%" /M >nul

関連する外部ページ

Python の公式ページ: https://www.python.org/

AI エディタ Windsurf のインストール

Pythonプログラムの編集・実行には、AI エディタの利用を推奨する。ここでは,Windsurfのインストールを説明する。

管理者権限でコマンドプロンプトを起動(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行して、Windsurfをシステム全体にインストールする。管理者権限は、wingetの--scope machineオプションでシステム全体にソフトウェアをインストールするために必要となる。

winget install --scope machine Codeium.Windsurf -e --silent

関連する外部ページ

Windsurf の公式ページ: https://windsurf.com/

必要なライブラリのインストール

コマンドプロンプトを管理者として実行(手順:Windowsキーまたはスタートメニュー > cmd と入力 > 右クリック > 「管理者として実行」)し、以下を実行する


pip install opencv-python numpy

ガウシアンぼかし画像処理プログラム

概要

このプログラムは、複数の静止画像に対してガウシアンぼかし処理を適用する。倍精度浮動小数点演算(float64)を用いることで、uint8型での処理時に発生する丸め誤差を最小化し、処理精度を向上させる[1][2]。

主要技術

Gaussian Blur(ガウシアンぼかし)

ガウシアン関数を用いた畳み込み演算により画像を平滑化する技術である[1][3]。中心ピクセルに最大の重みを与え、距離に応じて重みを減少させることで、エッジを保持しながらノイズを除去する特性を持つ[2][3]。

倍精度浮動小数点演算

uint8型からfloat64型へ変換して演算を行うことで、丸め誤差の蓄積を抑制する手法である。処理完了後に元のデータ型へ戻す。

技術的特徴

実装の特色

参考文献

[1] Bradski, G., & Kaehler, A. (2008). Learning OpenCV: Computer vision with the OpenCV library. O'Reilly Media, Inc.

[2] OpenCV Documentation. (2025). Image Filtering - GaussianBlur. https://docs.opencv.org/4.x/d4/d86/group__imgproc__filter.html

[3] Gaussian filter. (2025). In Wikipedia. https://en.wikipedia.org/wiki/Gaussian_filter

ソースコード


# ガウシアンぼかし画像処理プログラム
#
# 特徴技術名: Gaussian Blur(ガウシアンぼかし)
#
# 出典:
#   Bradski, G., & Kaehler, A. (2008). Learning OpenCV: Computer vision with the OpenCV library. O'Reilly Media, Inc.
#   OpenCV Documentation. (2025). Image Filtering. https://docs.opencv.org/4.x/d4/d86/group__imgproc__filter.html
#
# 特徴機能:
#   エッジ保持型ノイズ除去
#   ガウシアン関数による重み付け平均により、中心ピクセルに最大の重みを与え、
#   距離に応じて重みを減少させることで、境界やエッジを保持しながらノイズを除去する。
#   均一な平均化フィルタと比較して自然なぼかし効果を実現。
#
# 学習済みモデル: なし
#
# 特徴技術および学習済みモデルの利用制限:
#   OpenCV 4.5.0以降はApache 2ライセンスで商用利用可能。
#   必ず利用者自身で最新のライセンス情報を確認すること。
#   参考: https://opencv.org/license/
#
# 方式設計:
#   - 関連利用技術:
#     * OpenCV (cv2): 画像入出力、ガウシアンぼかし処理、画像表示
#     * tkinter: ファイル選択ダイアログ
#     * NumPy: 画像データ型変換(倍精度浮動小数点処理)
#
#   - 入力と出力:
#     入力: 複数の静止画像,カメラ(ユーザは「0:画像ファイル,1:カメラ,2:サンプル画像」のメニューで選択.
#           0:画像ファイルの場合はtkinterで複数ファイル選択可能.
#           1の場合はOpenCVでカメラが開き,スペースキーで撮影(複数回可能).
#           2の場合はhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg と
#           https://raw.githubusercontent.com/opencv/opencv/master/samples/data/messi5.jpgと
#           https://raw.githubusercontent.com/opencv/opencv/master/samples/data/aero3.jpgと
#           https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpgを使用)
#     出力: OpenCV画面でのぼかし処理結果表示、print()での処理結果表示、result.txtへの保存
#
#   - 処理手順:
#     1. 入力画像をuint8からfloat64(倍精度浮動小数点)へ変換
#     2. カーネルサイズ15×15のガウシアンカーネルを生成
#     3. 標準偏差sigmaXを明示的に計算(OpenCV内部計算式: sigma = 0.3*((ksize-1)*0.5-1)+0.8)
#     4. 倍精度でガウシアンぼかし処理を実行
#     5. 処理結果を元のデータ型(uint8)へ変換
#
#   - 前処理、後処理:
#     前処理: uint8からfloat64への型変換により、丸め誤差を最小化し精度向上
#     後処理: float64からuint8への型変換により、画像表示と保存を可能にする
#
#   - 追加処理:
#     標準偏差の明示的指定: sigmaXとsigmaYを明示的に計算・指定することで、
#     OpenCVの将来的な仕様変更の影響を回避し、処理の再現性を保証
#
#   - 調整を必要とする設定値:
#     * ksize(カーネルサイズ): 現在15×15に固定
#       値が大きいほどぼかし効果が強くなるが、処理時間も増加
#       用途に応じて3, 5, 7, 9, 11, 13, 15などの奇数値に調整が必要
#
# 将来方策:
#   カーネルサイズの自動最適化機能
#   画像のノイズレベルを自動検出し、適切なカーネルサイズを算出する機能を追加可能。
#   例: ノイズ分散の推定に基づくカーネルサイズの動的決定
#
# その他の重要事項:
#   * Windows環境での動作を前提
#   * Python 3.10以上で動作
#
# 前準備:
#   pip install opencv-python numpy

import cv2
import tkinter as tk
from tkinter import filedialog
import urllib.request
import time
from datetime import datetime

results_log = []

def image_processing(bgr_img):
    # 倍精度浮動小数点へ変換
    img_float64 = bgr_img.astype('float64')

    # sigmaXを明示的に計算 (ksize=15の場合)
    ksize = 15
    sigmaX = 0.3 * ((ksize - 1) * 0.5 - 1) + 0.8

    # ガウシアンぼかし処理(倍精度)
    processed_float64 = cv2.GaussianBlur(img_float64, (ksize, ksize), sigmaX, sigmaX)

    # 元の型へ戻す
    processed_img = processed_float64.astype(bgr_img.dtype)

    return processed_img, "ガウシアンぼかし適用", time.time()

def process_and_display_images(image_sources, source_type):
    display_index = 1
    processed_results = []

    for source in image_sources:
        img = cv2.imread(source) if source_type == 'file' else source
        if img is None:
            continue
        processed_img, result, current_time = image_processing(img)
        processed_results.append({
            'original': img,
            'processed': processed_img,
            'result': result,
            'time': current_time,
            'index': display_index
        })
        print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3], result)
        results_log.append(result)
        display_index += 1

    # すべての処理完了後に画像を一括表示
    for item in processed_results:
        cv2.imshow(f'Image_{item["index"]}', item['original'])
        cv2.imshow(f'ぼかし処理_{item["index"]}', item['processed'])

print("画像処理プログラム")
print("操作方法:")
print("0: 画像ファイルを選択")
print("1: カメラを使用(スペースキーで処理、qキーで終了)")
print("2: サンプル画像を使用")
choice = input("選択: ")

try:
    if choice == '0':
        root = tk.Tk()
        root.withdraw()
        if not (paths := filedialog.askopenfilenames()):
            exit()
        process_and_display_images(paths, 'file')
        cv2.waitKey(0)

    elif choice == '1':
        cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
        if not cap.isOpened():
            cap = cv2.VideoCapture(0)
        cap.set(cv2.CAP_PROP_BUFFERSIZE, 1)
        try:
            while True:
                ret, frame = cap.read()
                if not ret:
                    break
                cv2.imshow('Camera', frame)
                key = cv2.waitKey(1) & 0xFF
                if key == ord(' '):
                    processed_img, result, current_time = image_processing(frame)
                    cv2.imshow('ぼかし処理', processed_img)
                    print(datetime.fromtimestamp(current_time).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3], result)
                    results_log.append(result)
                elif key == ord('q'):
                    break
        finally:
            cap.release()

    else:
        urls = [
            "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/fruits.jpg",
            "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/messi5.jpg",
            "https://raw.githubusercontent.com/opencv/opencv/master/samples/data/aero3.jpg",
            "https://upload.wikimedia.org/wikipedia/commons/3/3a/Cat03.jpg"
        ]
        downloaded_files = []
        for i, url in enumerate(urls):
            try:
                urllib.request.urlretrieve(url, f"sample_{i}.jpg")
                downloaded_files.append(f"sample_{i}.jpg")
            except:
                print(f"画像のダウンロードに失敗しました: {url}")
        process_and_display_images(downloaded_files, 'file')
        cv2.waitKey(0)

finally:
    print('\n=== プログラム終了 ===')
    cv2.destroyAllWindows()
    if results_log:
        with open('result.txt', 'w', encoding='utf-8') as f:
            f.write('=== 結果 ===\n')
            f.write('\n'.join(results_log))
        print(f'\n処理結果をresult.txtに保存しました')