

機械学習を用いて画像から人の顔を検出するプログラム
今回、画像から人の顔を検出するプログラムを作ってみました。
画像から人の顔を検出するアルゴリズムは、最近流行りの生成AIやディープラーニングとはちょっと違います。
今回は”Haar Cascade”というアルゴリズムを活用します。
Haar Cascadeとは
画像から物体検出を行うために用いられるアルゴリズムです。
画像のピクセルの明るさの差と位置関係から、物体を検知・識別するアルゴリズムです。
機械学習にはPython!
機械学習ではお馴染みのPythonを活用します。
なぜならライブラリが充実してるからです。
Pythonが言語仕様的に特別機械学習に向いているのかわかりませんが、機械学習に関連したライブラリが充実していることは確かです。
しかも、Pythonはクライアント環境でもサーバー環境でも動作するという強みがあります。
顔検出プログラムの全コード
とりあえず、私が作った画像から人の顔を検知するプログラムを掲載しておきます。
import cv2
import numpy as np
import requests
from matplotlib import pyplot as plt
# Haar Cascadeの事前ロード(1回だけでOK)
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
def detect_faces_from_url(image_url):
try:
response = requests.get(image_url)
response.raise_for_status()
img_array = np.frombuffer(response.content, np.uint8)
image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
except Exception as e:
print(f"画像の取得または読み込みに失敗: {e}")
return None, None
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
return image, faces
# 画像URL
image_url = "画像URL"
result_image, faces = detect_faces_from_url(image_url)
if faces is not None and len(faces) > 0:
print("検出された顔の座標:", faces)
# 表示
plt.imshow(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB))
plt.axis("off")
plt.show()
else:
print("顔が検出されませんでした")
元画像

顔検出処理後

使用ライブラリ
import cv2
import numpy as np
import requests
from io import BytesIO
from matplotlib import pyplot as plt
cv2はOpenCVライブラリであり、今回のプログラムは画像を読み込んで解析するために使用します。
numpyはAI開発でよく出てくる数値計算やデータ処理ライブラリです。
requestsは画像をWEB上からダウンロードするために使用します。
実際に何かのプロジェクトで活用する時にも、S3にアップロードされた画像データを読み込んで解析したりすると思うので、こういう方法にしました。
matplotlibはグラフや図形を描くライブラリで、解析結果の画像に図形を描きこむために使用します。
顔検出モデルを読み込み
今回、顔を検出のにHaar Cascadeを利用するので、モデルを読み込みます。
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
ここで読み込んでいるモデル”haarcascade_frontalface_default.xml”は正面の顔を検出するモデルです。
他にも以下のようなモデルがあります。
顔(精度高め) | haarcascade_frontalface_alt2.xml |
横顔 | haarcascade_profileface.xml |
目 | haarcascade_eye.xml |
笑顔 | haarcascade_smile.xml |
上半身 | haarcascade_upperbody.xml |
全身 | haarcascade_fullbody.xml |
猫の顔 | haarcascade_frontalcatface.xml |
今回のサンプルでは正面の顔のモデルを活用するので、横顔は検出できないということになります。
なので、集合写真など被写体がカメラを意識している場面での検出に活用できます。
顔を検出する関数を定義
今回はサンプルプログラムなので、1回しかコールしませんが、プロジェクトで再利用することを考慮して、顔を検出する処理を関数でラッピングしておきます。
def detect_faces_from_url(image_url):
try:
response = requests.get(image_url)
response.raise_for_status()
img_array = np.frombuffer(response.content, np.uint8)
image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
except Exception as e:
print(f"画像の取得または読み込みに失敗: {e}")
return None, None
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
return image, faces
画像をダウンロードして読み込みを行う
今回、関数にはURLを渡しているので、URLから画像の読み込みます。
try:
response = requests.get(image_url)
response.raise_for_status()
img_array = np.frombuffer(response.content, np.uint8)
image = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
except Exception as e:
print(f"画像の取得または読み込みに失敗: {e}")
return None, None
画像の読み込みに失敗したら、顔の検出もできないので、Noneを返して関数を抜けます。
グレースケール処理を実施
“なぜグレースケールするのか?”
Haar Cascadeはピクセルの明るさと位置関係から物体を検知する仕組みです。
色情報が含まれていると、明るいのか?暗いのか?を判別しにくいのです。
なので、ノイズになる可能性のある情報を除外するために、cv2ライブラリを使って、グレースケール処理を行います。
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
顔の検出
detectMultiScale関数で顔の検出を行います。
関数を実行すると、検出された顔の位置情報が返ってきます。
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
gray:グレースケールした画像オブジェクト
scaleFactor:顔を検出するために画像サイズを縮小する倍率
minNeighbors:顔と判断するための最小隣接矩形数
minSize:検出する顔の最小サイズ
検出した顔を図形で描画
顔を検出した時に、座標情報が返ってくるので、その座標情報を元に、画像に矩形を描画します。
for (x, y, w, h) in faces:
cv2.rectangle(image, (x, y), (x + w, y + h), (0, 255, 0), 2)
画像URLを顔検出関数に渡して、結果を受け取る
先ほど定義した関数に画像URLを渡して、顔部分に図形を描画した画像データと検出した顔の座標データを受け取ります。
# 画像URL
image_url = "画像URL"
result_image, faces = detect_faces_from_url(image_url)
顔の検出結果を表示する
関数を実行して、関数から受け取った顔の座標データをチェックします。
データ件数が0件なら、顔は検出できなかったということになります。
顔が検出されたら、座標データと画像を表示するというプログラムになります。
if faces is not None and len(faces) > 0:
print("検出された顔の座標:", faces)
# 表示
plt.imshow(cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB))
plt.axis("off")
plt.show()
else:
print("顔が検出されませんでした")
顔検出の処理時間について
精度と処理速度はトレードオフです。
以下の顔を検出する処理のところで、精度と処理速度に関係する値が出てきます。
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30))
scaleFactor:顔を検出するために画像サイズを縮小する倍率
minNeighbors:顔と判断するための最小隣接矩形数
minSize:検出する顔の最小サイズ
この値を変えた時に、どの程度処理時間が変わるのか試してみたいと思います。
基準値
種類 | 値 |
---|---|
scaleFactor | 1.1 |
minNeighbors | 5 |
minSize | (30, 30) |
実行時間は2.4333 秒~3.6321 秒
この数値を基準として調査してみます。
各数値を細かくした場合
より精度を高める方向で、数値を小さくします。
種類 | 値 |
---|---|
scaleFactor | 1.05 |
minNeighbors | 3 |
minSize | (20, 20) |
実行時間は3.8842 秒~4.0793 秒
処理時間は増えました。
しかも、精度を高めるつもりでしたが、関係ない箇所まで検出されるようになってしまいました。
細かくしすぎるとダメなようです。

scaleFactorだけを大きくした場合
scaleFactorを1.1→1.3に変更してみました。
種類 | 値 |
---|---|
scaleFactor | 1.3 |
minNeighbors | 5 |
minSize | (30, 30) |
実行時間は1.6966 秒~1.8845 秒
実行時間は早くなりました。
しかし、数値を大きくしたことで、画像によってはこれまで検出されていた顔が検出されなくなりました。(今回のフリー画像では検出されています。)

scaleFactorだけを小さくした場合
種類 | 値 |
---|---|
scaleFactor | 1.05 |
minNeighbors | 5 |
minSize | (30, 30) |
実行時間は3.4067 秒~4.3813 秒
時間も増えたし、顔じゃない物も検出されるようになりました。
背景部分に顔検出マークが出てますよね。

minNeighborsだけを大きくした場合
種類 | 値 |
---|---|
scaleFactor | 1.1 |
minNeighbors | 10 |
minSize | (30, 30) |
実行時間は2.3226 秒~3.0270 秒
検出データには変化なしでした。

minNeighborsだけを小さくした場合
種類 | 値 |
---|---|
scaleFactor | 1.1 |
minNeighbors | 3 |
minSize | (30, 30) |
実行時間は2.3915 秒~2.6319 秒
minNeighborsは実行速度には大きな影響を与えないようです。
しかし、minNeighborsを小さくした場合では、顔じゃない物が顔として認識されてしまいました。

他の画像でテストした際は、特に顕著に顔ではない物がたくさん検出される結果となりました。
minNeighborsを小さすぎると、小さな模様まで人の顔に見えるようです。
まるで人間のパレイドリア現象やシミュラクラ現象みたいですね。
minSizeだけを大きくした場合
種類 | 値 |
---|---|
scaleFactor | 1.1 |
minNeighbors | 5 |
minSize | (50, 50) |
実行時間は2.1474 秒~2.1715 秒
検証には元々人の顔が大きめに写っている写真を利用したので、検出結果は基準値の時と違いがみられませんでした。
テーマパークなどでたくさんの人が遠近関係なく写っている写真では違いが出るかもしれません。

minSizeだけを小さくした場合
種類 | 値 |
---|---|
scaleFactor | 1.1 |
minNeighbors | 5 |
minSize | (10, 10) |
実行時間は2.6424 秒~3.7143 秒
今回のフリー画像では差異は見られませんでしたが、他の画像でテストした際、minSizeを小さくしたことで、小さなシミが顔として認識されるようになってしまいました。

このように、設定によって検出精度や実行速度が異なるため、被写体となる物や想定される環境に合わせて最適な設定を行う必要があるようです。
もしお困りごとがありましたら、お問い合わせフォームよりご相談ください。