説明
import cv2
import numpy as np
import argparse
import os
class ImageFaceMosaic:
def __init__(self):
“””画像用顔モザイク処理クラスの初期化”””
# カスケード分類器を読み込み
self.face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + ‘haarcascade_frontalface_default.xml’)
self.eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + ‘haarcascade_eye.xml’)
# 鼻のカスケードファイルが存在するかチェック
nose_cascade_path = cv2.data.haarcascades + ‘haarcascade_nose.xml’
if os.path.exists(nose_cascade_path):
self.nose_cascade = cv2.CascadeClassifier(nose_cascade_path)
self.nose_available = True
else:
print(“警告: 鼻の検出用ファイルが見つかりません。鼻の検出は無効化されます。”)
self.nose_cascade = None
self.nose_available = False
# モザイク処理のパラメータ
self.block_size = 15
self.blur_strength = 50
def apply_mosaic(self, image, x, y, w, h, method=’pixelate’):
“””
指定された領域にモザイク処理を適用する関数
Args:
image: 入力画像
x, y, w, h: モザイク処理を適用する領域の座標とサイズ
method: モザイク処理の方法 (‘pixelate’, ‘blur’, ‘black’, ‘white’)
“””
# 処理対象の領域を切り出し
roi = image[y:y+h, x:x+w]
# 領域が空でないことを確認
if roi.size == 0:
return image
if method == ‘pixelate’:
# ピクセル化によるモザイク処理
small_roi = cv2.resize(roi, (w//self.block_size, h//self.block_size))
mosaic_roi = cv2.resize(small_roi, (w, h), interpolation=cv2.INTER_NEAREST)
elif method == ‘blur’:
# ぼかしによるモザイク処理
mosaic_roi = cv2.GaussianBlur(roi, (self.blur_strength, self.blur_strength), 0)
elif method == ‘black’:
# 黒塗り
mosaic_roi = np.zeros_like(roi)
elif method == ‘white’:
# 白塗り
mosaic_roi = np.ones_like(roi) * 255
else:
# デフォルトはピクセル化
small_roi = cv2.resize(roi, (w//self.block_size, h//self.block_size))
mosaic_roi = cv2.resize(small_roi, (w, h), interpolation=cv2.INTER_NEAREST)
# 元の画像にモザイク処理された領域を戻す
image[y:y+h, x:x+w] = mosaic_roi
return image
def detect_faces_and_features(self, gray_image):
“””
顔と顔の特徴(目、鼻)を検出する関数
Args:
gray_image: グレースケール画像
Returns:
faces: 検出された顔の座標リスト
features: 検出された特徴の座標リスト
“””
# 顔を検出
faces = self.face_cascade.detectMultiScale(
gray_image,
scaleFactor=1.1,
minNeighbors=5,
minSize=(30, 30)
)
features = []
# 各顔に対して特徴を検出
for (x, y, w, h) in faces:
face_roi = gray_image[y:y+h, x:x+w]
# 目を検出
eyes = self.eye_cascade.detectMultiScale(face_roi)
for (ex, ey, ew, eh) in eyes:
features.append((x + ex, y + ey, ew, eh, ‘eye’))
# 鼻を検出(利用可能な場合のみ)
if self.nose_available and self.nose_cascade is not None:
try:
noses = self.nose_cascade.detectMultiScale(face_roi)
for (nx, ny, nw, nh) in noses:
features.append((x + nx, y + ny, nw, nh, ‘nose’))
except cv2.error:
# 鼻の検出でエラーが発生した場合はスキップ
pass
return faces, features
def generate_output_filename(self, input_path, method=’pixelate’):
“””
入力ファイル名から出力ファイル名を生成する関数
Args:
input_path: 入力ファイルのパス
method: モザイク処理の方法
Returns:
output_path: 出力ファイルのパス
“””
# ファイル名と拡張子を分離
base_name, ext = os.path.splitext(input_path)
# 拡張子が存在しない場合は.jpgを追加
if not ext:
ext = ‘.jpg’
# モザイク処理方法に応じてサフィックスを変更
if method == ‘pixelate’:
suffix = ‘_mosaic’
elif method == ‘blur’:
suffix = ‘_blur’
elif method == ‘black’:
suffix = ‘_black’
elif method == ‘white’:
suffix = ‘_white’
else:
suffix = ‘_mosaic’
# 出力ファイル名を生成
output_path = f”{base_name}{suffix}{ext}”
return output_path
def process_image(self, input_path, output_path=None, method=’pixelate’, show_detection=False, show_result=True):
“””
画像ファイルを処理して顔モザイク処理を適用する関数
Args:
input_path: 入力画像のパス
output_path: 出力画像のパス(Noneの場合は自動生成)
method: モザイク処理の方法
show_detection: 検出領域を表示するかどうか
show_result: 結果を表示するかどうか
“””
# 画像を読み込み
image = cv2.imread(input_path)
if image is None:
print(f”画像の読み込みに失敗しました: {input_path}”)
return None
print(f”画像サイズ: {image.shape[1]} x {image.shape[0]}”)
# グレースケールに変換
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 顔と特徴を検出
faces, features = self.detect_faces_and_features(gray)
print(f”{len(faces)}個の顔を検出しました。”)
if len(faces) == 0:
print(“顔が検出されませんでした。”)
if show_result:
cv2.imshow(‘Original Image’, image)
cv2.waitKey(0)
cv2.destroyAllWindows()
return image
# 検出された顔にモザイク処理を適用
for i, (x, y, w, h) in enumerate(faces):
print(f”顔 {i+1}: 位置({x}, {y}), サイズ({w} x {h})”)
image = self.apply_mosaic(image, x, y, w, h, method=method)
if show_detection:
# 顔の検出領域を矩形で表示
cv2.rectangle(image, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(image,f’Face {i+1}’,(x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
# 特徴の検出領域も表示(オプション)
if show_detection:
for (x, y, w, h, feature_type) in features:
color = (255, 0, 0) if feature_type == ‘eye’ else (0, 0, 255)
cv2.rectangle(image, (x, y), (x+w, y+h), color, 1)
# 出力パスが指定されていない場合は自動生成
if output_path is None:
output_path = self.generate_output_filename(input_path, method)
# 結果を表示
if show_result:
cv2.imshow(‘Processed Image’, image)
print(“画像を表示しています。任意のキーを押すと終了します。”)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 画像を保存
cv2.imwrite(output_path, image)
print(f”処理された画像を保存しました: {output_path}”)
return image
def batch_process(self, input_dir, output_dir, method=’pixelate’, show_detection=False):
“””
ディレクトリ内の複数画像を一括処理する関数
Args:
input_dir: 入力画像ディレクトリ
output_dir: 出力ディレクトリ
method: モザイク処理の方法
show_detection: 検出領域を表示するかどうか
“””
# 出力ディレクトリを作成
os.makedirs(output_dir, exist_ok=True)
# 対応する画像形式
image_extensions = [‘.jpg’, ‘.jpeg’, ‘.png’, ‘.bmp’, ‘.tiff’, ‘.tif’]
# 入力ディレクトリ内の画像ファイルを処理
for filename in os.listdir(input_dir):
if any(filename.lower().endswith(ext) for ext in image_extensions):
input_path = os.path.join(input_dir, filename)
# 出力ファイル名を自動生成
output_filename = self.generate_output_filename(filename, method)
output_path = os.path.join(output_dir, output_filename)
print(f”\n処理中: {filename}”)
self.process_image(input_path, output_path, method, show_detection, show_result=False)
print(f”\n一括処理が完了しました。結果は {output_dir} に保存されました。”)
def main():
parser = argparse.ArgumentParser(description=’画像用顔モザイク処理プログラム’)
parser.add_argument(‘input’, type=str, help=’入力画像ファイルまたはディレクトリのパス’)
parser.add_argument(‘–output’, type=str, help=’出力画像ファイルまたはディレクトリのパス(指定しない場合は自動生成)’)
parser.add_argument(‘–method’,type=str,default=’pixelate’,
choices=[‘pixelate’, ‘blur’, ‘black’, ‘white’],
help=’モザイク処理の方法 (デフォルト: pixelate)’)
parser.add_argument(‘–show-detection’, action=’store_true’, help=’検出領域を表示する’)
parser.add_argument(‘–block-size’, type=int, default=15, help=’ピクセル化モザイクのブロックサイズ’)
parser.add_argument(‘–blur-strength’, type=int, default=50, help=’ぼかしの強度’)
parser.add_argument(‘–batch’, action=’store_true’, help=’ディレクトリ内の全画像を一括処理’)
args = parser.parse_args()
# モザイク処理クラスを初期化
mosaic_processor = ImageFaceMosaic()
mosaic_processor.block_size = args.block_size
mosaic_processor.blur_strength = args.blur_strength
if args.batch or os.path.isdir(args.input):
# 一括処理
input_dir = args.input
output_dir = args.output if args.output else ‘output_mosaic’
mosaic_processor.batch_process(input_dir, output_dir, args.method, args.show_detection)
else:
# 単一画像処理
mosaic_processor.process_image(args.input, args.output, args.method, args.show_detection)
if __name__ == “__main__”:
main()