返回文章列表

HOG特徵與SVM實作人臉偵測技術

本文探討使用方向梯度直方圖(HOG)特徵和支援向量機(SVM)實作人臉偵測技術。從HOG特徵提取、資料集準備、模型訓練到滑動視窗偵測,完整闡述人臉偵測流程,並使用Scikit-learn和Scikit-image等工具進行實作與分析,最終達到近99%的準確率。同時也探討了模型的最佳化方向和未來改進的可能性。

機器學習 電腦視覺

HOG 特徵描述影像區域性梯度方向的統計資訊,對於人臉等目標的形狀描述非常有效。結合線性 SVM 分類別器,可以有效地區分人臉和非人臉影像。透過滑動視窗方法,可以在新的影像上檢測人臉位置。實作上,我們使用 Scikit-image 提取 HOG 特徵,並使用 Scikit-learn 的 LinearSVC 訓練模型,在測試資料集上取得了良好的效果。更進一步,我們利用網格搜尋(GridSearchCV)找到最佳的模型引數,並使用交叉驗證評估模型的泛化能力,確保模型在實際應用中具有穩健的效能。然而,滑動視窗方法的計算成本較高,未來可以考慮使用更有效率的目標檢測演算法,例如根據深度學習的方法,來提升偵測速度和準確度。

直方圖的本質

直方圖可以被視為一堆積木,每個積木代表著資料集中的一個點。每個積木的高度對應著該bin中的點的數量。圖49-3展示了這種視角,積木堆積疊在一起形成了直方圖。

import numpy as np
import matplotlib.pyplot as plt

# 生成資料
x = np.random.randn(1000)

# 繪製直方圖
fig, ax = plt.subplots()
bins = np.arange(-3, 8)
ax.plot(x, np.full_like(x, -0.1), '|k', markeredgewidth=1)

for count, edge in zip(*np.histogram(x, bins)):
    for i in range(count):
        ax.add_patch(plt.Rectangle((edge, i), 1, 1, ec='black', alpha=0.5))

ax.set_xlim(-4, 8)
ax.set_ylim(-0.2, 8)

直方圖的問題

直方圖的問題在於,積木的高度往往反映的是bin的位置與資料點的巧合,而不是資料點的真實密度。這種bin位置與資料點的不對齊可能導致直方圖結果不佳。

改進方法

如果我們能夠以資料點為中心堆積疊積木,而不是以bin為中心呢?這樣,積木就不會對齊,但是我們可以在每個位置沿著x軸新增它們的貢獻來找到結果。讓我們試試看(見圖49-4)。

x_d = np.linspace(-4, 8, 2000)

這種方法可以更好地反映資料的真實分佈,避免了傳統直方圖的侷限性。透過以資料點為中心堆積疊積木,我們可以獲得更準確的密度估計。

圖表翻譯:

上述程式碼和方法展示瞭如何以資料點為中心堆積疊積木來改進直方圖。透過使用np.linspace生成密集的x軸點,我們可以計算每個點的貢獻,並將其新增到最終的結果中。這種方法可以更好地捕捉資料的真實分佈特性。

內容解密:

  • np.histogram函式用於計算直方圖的bin位置和高度。
  • plt.Rectangle函式用於繪製積木,代表著資料集中的一個點。
  • np.linspace函式用於生成密集的x軸點,以便計算每個點的貢獻。
  • 透過以資料點為中心堆積疊積木,我們可以獲得更準確的密度估計,避免了傳統直方圖的侷限性。

核心密度估計(Kernel Density Estimation,KDE)簡介

核密度估計是一種用於估計資料分佈的方法,透過在每個資料點上放置一個核(kernel),然後將這些核的總和作為密度估計。這種方法可以提供比傳統直方圖更為精確和平滑的資料分佈影像。

基本概念

核密度估計的基本思想是將每個資料點視為一個核的中心,然後計算每個核對應的密度值。這些密度值的總和就代表了資料的整體分佈。

實踐中的核密度估計

在實踐中,核密度估計有兩個重要的引數:核函式(kernel)和頻寬(bandwidth)。核函式決定了核的形狀,而頻寬則控制了核的大小。

Scikit-Learn 中的核密度估計

Scikit-Learn 提供了一個高效且靈活的核密度估計工具,名為 KernelDensity。這個工具支援多種核函式和距離度量,並且可以處理多維資料。

示例程式碼

from sklearn.neighbors import KernelDensity
import numpy as np
import matplotlib.pyplot as plt

# 資料生成
np.random.seed(0)
x = np.random.normal(0, 1, 100)

# 核密度估計模型
kde = KernelDensity(bandwidth=1.0, kernel='gaussian')
kde.fit(x[:, None])

# 評估樣本
x_d = np.linspace(-4, 4, 1000)[:, None]
logprob = kde.score_samples(x_d)

# 繪製結果
plt.fill_between(x_d.flatten(), np.exp(logprob), alpha=0.5)
plt.plot(x, np.full_like(x, -0.01), '|k', markeredgewidth=1)
plt.ylim(-0.02, 0.22)
plt.show()

結果分析

透過上述程式碼,可以得到一個平滑的資料分佈影像,該影像可以更好地反映資料的真實分佈特性。

圖表翻譯:

此圖示為使用 Scikit-Learn 的 KernelDensity 工具進行核密度估計的結果。圖中,x 軸代表資料點,y 軸代表對應的密度值。透過這個影像,可以清晰地看到資料的分佈特性。

核密度估計是一種強大的資料分析工具,未來可以繼續研究和改進其在不同領域的應用,例如在高維資料分析、異常檢測等方面。同時,也可以探索新的核函式和距離度量,以提高核密度估計的準確性和效率。

選擇頻寬_via_交叉驗證

玄貓最終產生的估計結果取決於控制偏差-變異數權衡的頻寬。頻寬太窄會導致高變異數估計(即過度擬合),其中單個點的存在或不存在會產生很大的差異。頻寬太寬會導致高偏差估計(即不足擬合),其中資料中的結構被玄貓的平滑化所掩蓋。

在統計學中,根據對資料的嚴格假設,有許多快速估計最佳頻寬的方法:例如,檢視SciPy和statsmodels包中的KDE實作,您將看到根據這些規則的實作。

在機器學習背景下,我們已經看到這種超引數調整通常透過交叉驗證方法進行。有了這個想法,Scikit-Learn的KernelDensity估計器被設計為可以直接在包的標準網格搜尋工具中使用。以下,我們將使用GridSearchCV來最佳化前面資料集的頻寬。由於我們正在檢視一個非常小的資料集,我們將使用留一法交叉驗證,這可以最小化每次交叉驗證試驗中訓練集大小的減少:

from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import LeaveOneOut

頻寬 = 10 ** np.linspace(-1, 1, 100)
網格 = GridSearchCV(KernelDensity(kernel='gaussian'),
                   {'bandwidth': 頻寬},
                   cv=LeaveOneOut())
網格.fit(x[:, None])

現在,我們可以找到使得評分(在本例中預設為對數似然)最大化的頻寬選擇:

grid.best_params_

最佳頻寬恰好接近我們在前面的示例圖中使用的頻寬,即1.0(即scipy.stats.norm的預設寬度)。

示例:非天真的貝葉斯

此示例研究了使用KDE進行貝葉斯生成分類別,並展示瞭如何使用Scikit-Learn架構建立自定義估計器。

在第41章中,我們探討了天真的貝葉斯分類別,其中我們為每個類別建立一個簡單的生成模型,並使用這些模型構建一個快速分類別器。對於高斯天真的貝葉斯,生成模型是一個簡單的軸向對齊高斯模型。使用像KDE這樣的密度估計演算法,我們可以刪除“天真”的元素,並使用每個類別更複雜的生成模型進行相同的分類別。它仍然是貝葉斯分類別,但不再是天真。

生成分類別的一般方法是:

  1. 將訓練資料按玄貓分割。
  2. 對於每個集合,適應KDE以獲得資料的生成模型。這允許您計算任何觀察x和標籤y的似然P(x | y)。
  3. 從訓練集中每個類別的示例數計算類別先驗P(y)。
  4. 對於未知點x,每個類別的後驗機率為P(y | x) ∝ P(x | y)P(y)。指派給點的標籤是最大化此後驗機率的類別。

演算法很簡單,易於理解;更具挑戰性的部分是將其嵌入Scikit-Learn框架中,以便利用網格搜尋和交叉驗證架構。

以下是實作演算法的程式碼,我們將在程式碼塊後逐步解釋:

from sklearn.base import BaseEstimator, ClassifierMixin

class KDEClassifier(BaseEstimator, ClassifierMixin):
    """根據KDE的貝葉斯生成分類別
    
    引數
    
---
-
---
---
    bandwidth : float
        每個類別內的核頻寬
    kernel : str
        核名稱,傳遞給KernelDensity
    """
    def __init__(self, bandwidth=1.0, kernel='gaussian'):
        #...

內容解密:

上述程式碼定義了一個名為KDEClassifier的類別,該類別繼承自BaseEstimatorClassifierMixin。這個類別用於根據KDE的貝葉斯生成分類別。它有一個__init__方法,用於初始化頻寬和核引數。

圖表翻譯:

此圖表展示了根據KDE的貝葉斯生成分類別過程。首先,初始化KDEClassifier,然後計算頻寬,適應KDE,計算後驗機率,最後指派標籤。

自定義估計器解析

在這個例子中,我們將實作一個根據核密度估計(KDE)的貝葉斯生成式分類別器。這個分類別器的核心思想是使用核密度估計來模擬每個類別的分佈,並根據這些分佈進行分類別。

初始化引數

self.bandwidth = bandwidth
self.kernel = kernel

在初始化階段,我們設定了兩個重要的引數:bandwidthkernelbandwidth決定了核密度估計中的平滑程度,而kernel則決定了使用哪種核函式。

訓練模型

def fit(self, X, y):
    self.classes_ = np.sort(np.unique(y))
    training_sets = [X[y == yi] for yi in self.classes_]
    self.models_ = [KernelDensity(bandwidth=self.bandwidth, kernel=self.kernel).fit(Xi) for Xi in training_sets]
    self.logpriors_ = [np.log(Xi.shape[0] / X.shape[0]) for Xi in training_sets]
    return self

在訓練階段,我們首先根據標籤y將資料分成不同的類別。然後,對於每個類別,我們使用核密度估計來模擬其分佈,並計算每個類別的先驗機率。

預測機率

def predict_proba(self, X):
    logprobs = np.array([model.score_samples(X) for model in self.models_]).T
    result = np.exp(logprobs + self.logpriors_)
    return result / result.sum(axis=1, keepdims=True)

在預測階段,我們使用每個類別的核密度估計模型計算輸入資料X的對數機率。然後,根據貝葉斯定理,我們計算每個類別的後驗機率,並傳回結果。

預測類別

def predict(self, X):
    return self.classes_[np.argmax(self.predict_proba(X), 1)]

最終,根據預測的機率,我們傳回每個輸入資料最可能屬於的類別。

自定義估計器架構

from sklearn.base import BaseEstimator, ClassifierMixin
class KDEClassifier(BaseEstimator, ClassifierMixin):
    """Bayesian generative classification based on KDE
    
    Parameters
    
---
-
---
---
    bandwidth : float
        the kernel bandwidth within each class
    kernel : str
        the kernel name, passed to KernelDensity
    """

這個自定義估計器繼承了Scikit-learn的BaseEstimatorClassifierMixin類別,實作了根據核密度估計的貝葉斯生成式分類別器。使用者可以透過設定bandwidthkernel引數來控制模型的行為。

圖表翻譯:

這個流程圖描述了自定義估計器的工作流程,從初始化到預測結果的傳回。

自定義估計器類別

在 Scikit-Learn 中,每個估計器都是一個類別,最方便的方式是讓這個類別繼承自 BaseEstimator 類別以及適當的 mixin,後者提供了標準功能。例如,BaseEstimator 包含了(在其他東西中)複製估計器以用於交叉驗證程式的邏輯,而 ClassifierMixin 定義了一個預設的評分方法,用於評估分類別器的效能。

類別定義

class KDEClassifier(BaseEstimator, ClassifierMixin):
    """
    自定義核密度估計分類別器。
    
    引數
    
---
-
---
    bandwidth : float, optional
        核密度估計的頻寬。
    kernel : str, optional
        核函式型別。
    """
    
    def __init__(self, bandwidth=1.0, kernel='gaussian'):
        """
        初始化類別。
        
        引數
        
---
-
---
        bandwidth : float, optional
            核密度估計的頻寬。
        kernel : str, optional
            核函式型別。
        """
        self.bandwidth = bandwidth
        self.kernel = kernel

訓練方法

def fit(self, X, y):
    """
    訓練模型。
    
    引數
    
---
-
---
    X : array-like, shape (n_samples, n_features)
        訓練資料。
    y : array-like, shape (n_samples,)
        訓練標籤。
    
    傳回
    
---
-
---
    self
    """
    self.classes_ = np.sort(np.unique(y))
    training_sets = [X[y == yi] for yi in self.classes_]
    self.models_ = [KernelDensity(bandwidth=self.bandwidth, kernel=self.kernel).fit(Xi) for Xi in training_sets]
    self.logpriors_ = [np.log(Xi.shape[0] / X.shape[0]) for Xi in training_sets]
    
    return self

預測方法

def predict_proba(self, X):
    """
    預測機率。
    
    引數
    
---
-
---
    X : array-like, shape (n_samples, n_features)
        測試資料。
    
    傳回
    
---
-
---
    array-like, shape (n_samples, n_classes)
        預測機率。
    """
    #... (此處省略實作細節)

使用範例

# 建立例項
kde_classifier = KDEClassifier(bandwidth=1.0, kernel='gaussian')

# 訓練模型
kde_classifier.fit(X_train, y_train)

# 預測機率
proba = kde_classifier.predict_proba(X_test)

內容解密:

上述程式碼定義了一個自定義的核密度估計分類別器類別 KDEClassifier,它繼承自 BaseEstimatorClassifierMixin。類別初始化方法 __init__ 設定了頻寬和核函式型別。訓練方法 fit 計算了類別先驗機率和訓練每個類別的核密度模型。預測方法 predict_proba 則使用訓練好的模型預測新的資料的機率。

圖表翻譯:

此圖表展示了 KDEClassifier 類別的工作流程,從初始化到預測機率。每一步驟都對應到程式碼中的特定部分。

自訂估計器的實作與應用

在上一節中,我們實作了一個自訂的核密度估計(KDE)分類別器。現在,我們將深入探討如何使用這個自訂估計器,並將其應用於手寫數字分類別問題。

預測方法的實作

首先,我們需要實作 predict_proba 方法,這個方法傳回一個陣列,其中包含每個樣本屬於每個類別的後驗機率。然後,我們可以使用這些機率來簡單地實作 predict 方法,該方法傳回每個樣本最可能屬於的類別。

def predict_proba(self, X):
    logprobs = np.vstack([model.score_samples(X) for model in self.models_]).T
    result = np.exp(logprobs + self.logpriors_)
    return result / result.sum(axis=1, keepdims=True)

def predict(self, X):
    return self.classes_[np.argmax(self.predict_proba(X), axis=1)]

應用於手寫數字分類別

現在,我們將使用這個自訂估計器來解決手寫數字分類別問題。首先,我們載入數字資料集,然後使用 GridSearchCV 來查詢最佳的頻寬引數。

from sklearn.datasets import load_digits
from sklearn.model_selection import GridSearchCV

digits = load_digits()
grid = GridSearchCV(KDEClassifier(), {'bandwidth': np.logspace(0, 2, 100)})
grid.fit(digits.data, digits.target)

接下來,我們可以繪製交叉驗證得分與頻寬的關係圖,來視覺化地觀察模型的效能。

fig, ax = plt.subplots()
ax.semilogx(np.array(grid.cv_results_['param_bandwidth']), grid.cv_results_['mean_test_score'])
ax.set(title='KDE Model Performance', ylim=(0, 1), xlabel='bandwidth', ylabel='accuracy')
print(f'best param: {grid.best_params_}')
print(f'accuracy = {grid.best_score_}')

結果顯示,自訂的KDE分類別器可以達到超過96%的交叉驗證準確率,這遠超過了天真貝葉斯分類別器的約80%準確率。

from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score
cross_val_score(GaussianNB(), digits.data, digits.target).mean()

這個例子展示瞭如何使用自訂估計器來解決特定的問題,並如何使用交叉驗證來評估模型的效能。同時,也強調了選擇合適的模型和引數對於取得良好結果的重要性。

圖片特徵提取與人臉偵測

人臉偵測是一個令人著迷的應用,結合了機器學習和影像處理技術。為了將圖片中的資訊轉換成機器學習模型可以理解的格式,我們需要進行特徵提取。其中一個有效的方法是使用方向梯度直方圖(Histogram of Oriented Gradients, HOG)。

HOG特徵提取步驟

  1. 圖片預處理:對圖片進行預先的歸一化處理,可以減少光照變化對特徵的影響。
  2. 梯度計算:使用兩個濾波器分別計算水平和垂直方向的亮度梯度,捕捉邊緣、輪廓和紋理資訊。
  3. 細分單元:將圖片分成小的細分單元(cells),然後在每個單元中計算梯度方向的直方圖。
  4. 直方圖歸一化:對每個細分單元的直方圖進行歸一化,以進一步減少光照變化的影響。
  5. 特徵向量構建:從每個細分單元的直方圖資訊中構建出一維的特徵向量。

實作HOG特徵提取

Scikit-Image函式庫中提供了一個快速且高效的HOG特徵提取器,可以用來簡化這個過程。

import numpy as np
from skimage import io, feature
import matplotlib.pyplot as plt

# 載入圖片
image = io.imread('image.jpg', as_gray=True)

# 計算HOG特徵
hog_features = feature.hog(image, orientations=9, pixels_per_cell=(8, 8),
                            cells_per_block=(3, 3), block_norm='L1')

# 顯示原始圖片和HOG特徵
plt.figure(figsize=(10, 5))

plt.subplot(1, 2, 1)
plt.imshow(image, cmap='gray')
plt.title('原始圖片')

plt.subplot(1, 2, 2)
plt.bar(range(len(hog_features)), hog_features)
plt.title('HOG特徵')

plt.show()

人臉偵測應用

使用HOG特徵提取後,可以將這些特徵作為輸入,訓練一個機器學習模型來進行人臉偵測。支援向量機(SVM)是一種常用的分類別器,可以用於這個任務。

from sklearn import svm
from sklearn.model_selection import train_test_split

# 載入人臉和非人臉圖片的HOG特徵
face_features =...
non_face_features =...

# 合併特徵和標籤
X = np.vstack((face_features, non_face_features))
y = np.hstack((np.ones(len(face_features)), np.zeros(len(non_face_features))))

# 切分訓練和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 訓練SVM模型
svm_model = svm.SVC(kernel='linear', probability=True)
svm_model.fit(X_train, y_train)

# 使用模型進行人臉偵測
def detect_face(image):
    hog_features = feature.hog(image, orientations=9, pixels_per_cell=(8, 8),
                              cells_per_block=(3, 3), block_norm='L1')
    prediction = svm_model.predict(hog_features.reshape(1, -1))
    return prediction

# 測試人臉偵測功能
test_image = io.imread('test_image.jpg', as_gray=True)
result = detect_face(test_image)
if result == 1:
    print("偵測到人臉")
else:
    print("未偵測到人臉")

圖表翻譯:

以上流程展示瞭如何使用HOG特徵提取和SVM模型來實作人臉偵測。這是一個基本的例子,實際應用中可能需要根據具體情況進行調整和最佳化。

人臉偵測技術:HOG 特徵與簡單面部偵測器

簡介

人臉偵測是一種常見的電腦視覺技術,旨在自動識別影像或影片中的面部。其中,一種重要的特徵提取方法是方向梯度直方圖(Histogram of Oriented Gradients, HOG)。本文將介紹如何使用 HOG 特徵和 Scikit-Learn 中的線性支援向量機(SVM)建立一個簡單的面部偵測器。

HOG 特徵提取

HOG 特徵是一種描述影像中物體形狀和方向的方法。它透過計算影像中每個細胞的方向梯度直方圖來實作。以下是使用 Scikit-Image 中的 feature.hog 函式提取 HOG 特徵的示例:

from skimage import data, color, feature
import matplotlib.pyplot as plt

image = color.rgb2gray(data.chelsea())
hog_vec, hog_vis = feature.hog(image, visualize=True)

fig, ax = plt.subplots(1, 2, figsize=(12, 6))
ax[0].imshow(image, cmap='gray')
ax[0].set_title('輸入影像')
ax[1].imshow(hog_vis)
ax[1].set_title('HOG 特徵視覺化')
plt.show()

這段程式碼將輸入影像轉換為灰度影像,然後提取 HOG 特徵和視覺化結果。

建立簡單面部偵測器

要建立一個簡單的面部偵測器,我們需要進行以下步驟:

  1. 取得一組正面影像(即面部影像)作為訓練樣本。
  2. 取得一組負面影像(即非面部影像)作為訓練樣本。
  3. 從這些訓練樣本中提取 HOG 特徵。
  4. 使用線性 SVM 分類別器對這些樣本進行訓練。
  5. 對於一個未知影像,使用滑動視窗法掃描整個影像,並使用模型評估每個視窗是否包含面部。
  6. 如果檢測結果重疊,合併成一個單一視窗。

步驟 1:取得正面影像

我們可以使用 Labeled Faces in the Wild 資料集作為正面影像。這個資料集可以透過 Scikit-Learn 中的 fetch_lfw_people 函式下載:

from sklearn.datasets import fetch_lfw_people

faces = fetch_lfw_people()
positive_patches = faces.images
print(positive_patches.shape)

這將給我們 13,000 個面部影像作為訓練樣本。

步驟 2:取得負面影像

負面影像是非面部影像,我們可以使用其他資料集或自己收集。假設我們已經有了一組負面影像,以下是如何提取 HOG 特徵和訓練模型的示例:

from sklearn.svm import LinearSVC
from sklearn.model_selection import train_test_split

# 提取 HOG 特徵
hog_features = []
for image in positive_patches:
    hog_vec, _ = feature.hog(image, visualize=False)
    hog_features.append(hog_vec)

# 訓練模型
X_train, X_test, y_train, y_test = train_test_split(hog_features, [1] * len(hog_features), test_size=0.2, random_state=42)

svm = LinearSVC()
svm.fit(X_train, y_train)

這段程式碼提取正面影像的 HOG 特徵,然後使用線性 SVM 分類別器對這些特徵進行訓練。

步驟 3:檢測面部

對於一個未知影像,我們可以使用滑動視窗法掃描整個影像,並使用模型評估每個視窗是否包含面部:

def detect_face(image):
    # 提取 HOG 特徵
    hog_vec, _ = feature.hog(image, visualize=False)

    # 評估模型
    prediction = svm.predict([hog_vec])

    # 如果預測結果為 1,則表示視窗包含面部
    if prediction == 1:
        return True
    else:
        return False

# 測試檢測功能
image = color.rgb2gray(data.chelsea())
if detect_face(image):
    print("檢測到面部")
else:
    print("未檢測到面部")

這段程式碼定義了一個 detect_face 函式,該函式提取輸入影像的 HOG 特徵,然後使用模型評估是否包含面部。如果預測結果為 1,則表示視窗包含面部。

圖片縮圖生成

為了取得一組大小相似的縮圖,我們需要從一組圖片中提取出沒有臉部的圖片區域。這裡,我們使用 Scikit-Image 中的圖片,結合 Scikit-Learn 的 PatchExtractor 來實作這個目標。

首先,我們需要載入一組圖片。這裡,我們使用 Scikit-Image 中的 data 模組來載入幾張示例圖片。

import numpy as np
from skimage import data, transform
from sklearn.feature_extraction.image import PatchExtractor

# 載入示例圖片
imgs_to_use = ['camera', 'text', 'coins', 'moon', 
               'page', 'clock', 'immunohistochemistry', 
               'chelsea', 'coffee', 'hubble_deep_field']
raw_images = (getattr(data, name)() for name in imgs_to_use)

# 將彩色圖片轉換為灰度圖片
images = [transform.color.rgb2gray(image) if image.ndim == 3 else image 
          for image in raw_images]

接下來,我們定義一個函式 extract_patches 來從圖片中提取出大小為 patch_size 的圖片區域(patches)。這個函式使用 PatchExtractor 來實作。

def extract_patches(img, N, scale=1.0, patch_size=(20, 20)):
    # 計算提取的patch大小
    extracted_patch_size = tuple((scale * np.array(patch_size)).astype(int))
    
    # 建立PatchExtractor
    extractor = PatchExtractor(patch_size=extracted_patch_size, 
                              max_patches=N, random_state=0)
    
    # 提取patches
    patches = extractor.transform(img[np.newaxis])
    
    # 如果scale不是1,則將patches調整到原始大小
    if scale!= 1:
        patches = np.array([transform.resize(patch, patch_size) for patch in patches])
    
    return patches

最後,我們使用這個函式來從圖片中提取出多個尺度的patches,並將它們堆積疊起來形成最終的負樣本資料集。

negative_patches = np.vstack([extract_patches(im, 1000, scale) 
                             for im in images for scale in [0.5, 1.0, 2.0]])

圖表翻譯:

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title HOG特徵與SVM實作人臉偵測技術

package "Docker 架構" {
    actor "開發者" as dev

    package "Docker Engine" {
        component [Docker Daemon] as daemon
        component [Docker CLI] as cli
        component [REST API] as api
    }

    package "容器運行時" {
        component [containerd] as containerd
        component [runc] as runc
    }

    package "儲存" {
        database [Images] as images
        database [Volumes] as volumes
        database [Networks] as networks
    }

    cloud "Registry" as registry
}

dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置

@enduml

這個過程可以視覺化為上述的流程圖,從載入圖片開始,到形成最終的負樣本資料集。每一步驟都對應到特定的操作,最終實作了從圖片中提取出沒有臉部的patches的目標。

合成負面影像patches並計算HOG特徵

在上一步驟中,我們成功地從圖片中提取出了不包含人臉的影像patches,總計有30,000個patches。現在,我們將這些patches視覺化,以便更好地瞭解它們的外觀。

import matplotlib.pyplot as plt

# 建立子圖
fig, ax = plt.subplots(6, 10)

# 迭代patches並顯示
for i, axi in enumerate(ax.flat):
    axi.imshow(negative_patches[500 * i], cmap='gray')
    axi.axis('off')

這些負面影像patches涵蓋了演算法可能遇到的非人臉影像的空間。接下來,我們將正面樣本(包含人臉的patches)和負面樣本(不包含人臉的patches)合併,並計算它們的HOG(Histogram of Oriented Gradients)特徵。

合併資料集並提取HOG特徵

from itertools import chain
import numpy as np

# 合併正面和負面patches
X_train = np.array([feature.hog(im) for im in chain(positive_patches, negative_patches)])

# 建立標籤陣列
y_train = np.zeros(X_train.shape[0])
y_train[:positive_patches.shape[0]] = 1

在這一步驟中,我們使用了itertools.chain來合併正面和負面patches。然後,我們計算了每個patch的HOG特徵,並將結果儲存在X_train中。同時,我們建立了一個標籤陣列y_train,其中正面樣本的標籤為1,負面樣本的標籤為0。

訓練支援向量機(SVM)

from sklearn import svm

# 建立SVM分類別器
clf = svm.SVC()

# 訓練SVM模型
clf.fit(X_train, y_train)

現在,我們已經成功地訓練了一個SVM模型,可以用於區分人臉和非人臉影像。這個模型可以應用於各種實際應用中,例如人臉檢測、影像分類別等。

使用機器學習模型進行人臉偵測

在人臉偵測任務中,選擇合適的機器學習模型至關重要。考慮到高維度的二元分類別問題,線性支援向量機(Linear Support Vector Machine)是一個不錯的選擇。為了達到更好的效能,特別是在大資料集上,我們選擇使用Scikit-Learn的LinearSVC。

建立基準模型

首先,我們使用簡單的高斯樸素貝葉斯估計器(Gaussian Naive Bayes)來建立一個基準模型,以快速評估模型的效能。

from sklearn.naive_bayes import GaussianNB
from sklearn.model_selection import cross_val_score

# 輸入訓練資料
cross_val_score(GaussianNB(), X_train, y_train)

結果表明,即使用簡單的樸素貝葉斯演算法,我們也能夠在訓練資料上達到95%以上的準確率。

支援向量機模型

接下來,我們嘗試使用支援向量機(SVM)模型,並進行網格搜尋以找到最佳的C引數。

from sklearn.svm import LinearSVC
from sklearn.model_selection import GridSearchCV

# 定義網格搜尋引數
grid = GridSearchCV(LinearSVC(), {'C': [1.0, 2.0, 4.0, 8.0]})

# 進行網格搜尋
grid.fit(X_train, y_train)

# 取得最佳分數和最佳引數
print(grid.best_score_)
print(grid.best_params_)

結果顯示,最佳的C引數是1.0,並且能夠達到近99%的準確率。

模型評估

我們取出最佳的估計器,並在整個訓練資料集上進行重新訓練。

# 取出最佳估計器
model = grid.best_estimator_

# 重新訓練模型
model.fit(X_train, y_train)

人臉偵測

現在,我們有了一個成熟的模型,可以用於人臉偵測任務。讓我們使用一張新的影像來測試模型的效能。

# 載入測試影像
test_image = skimage.data.astronaut()
test_image = skimage.transform.rescale(test_image, 0.5)

# 選擇影像的一部分
test_image = test_image[:160, 40:180]

# 顯示影像
plt.imshow(test_image, cmap='gray')
plt.axis('off')

滑動視窗方法

為了偵測人臉,我們使用滑動視窗方法,在影像上滑動一個視窗,並計算每個視窗的HOG特徵。

def sliding_window(img, patch_size=positive_patches[0].shape):
    # 初始化視窗位置
    for i in range(0, img.shape[0] - patch_size[0], 10):
        for j in range(0, img.shape[1] - patch_size[1], 10):
            # 視窗擷取
            patch = img[i:i+patch_size[0], j:j+patch_size[1]]
            
            # 計算HOG特徵
            hog_feature = hog(patch, orientations=9, pixels_per_cell=(8, 8), cells_per_block=(3, 3))
            
            # 預測結果
            prediction = model.predict(hog_feature)
            
            # 畫出預測結果
            if prediction == 1:
                plt.Rectangle((j, i), patch_size[1], patch_size[0], fill=False)
                
    plt.show()

這個滑動視窗方法可以幫助我們找到影像中的人臉位置。

影像處理與特徵提取

在進行影像處理時,首先需要將影像分割成多個小塊,稱為patches。這些patches可以用來提取特徵,例如使用HOG(Histogram of Oriented Gradients)演算法。以下是實作影像分割和特徵提取的步驟:

import numpy as np
from skimage import transform
from skimage.feature import hog

def sliding_window(img, patch_size, istep=2, jstep=2, scale=1.0):
    Ni, Nj = (int(scale * s) for s in patch_size)
    for i in range(0, img.shape[0] - Ni, istep):
        for j in range(0, img.shape[1] - Nj, jstep):
            patch = img[i:i + Ni, j:j + Nj]
            if scale!= 1:
                patch = transform.resize(patch, patch_size)
            yield (i, j), patch

# 載入測試影像
test_image =...

# 設定patch大小
patch_size = (16, 16)

# 進行滑動視窗處理
indices, patches = zip(*sliding_window(test_image, patch_size))

# 對patches進行HOG特徵提取
patches_hog = np.array([hog(patch) for patch in patches])

print(patches_hog.shape)

模型預測與結果視覺化

接下來,使用訓練好的模型對提取的HOG特徵進行預測,以判斷每個patch是否包含人臉。然後,根據預測結果,將包含人臉的patches在原始影像上繪製出來。

# 載入訓練好的模型
model =...

# 對patches進行預測
labels = model.predict(patches_hog)

# 繪製包含人臉的patches
fig, ax = plt.subplots()
ax.imshow(test_image)
for (i, j), label in zip(indices, labels):
    if label == 1:
        rect = plt.Rectangle((j, i), patch_size[1], patch_size[0], fill=False)
        ax.add_patch(rect)
plt.show()

圖表翻譯:

此圖示展示瞭如何將影像分割成小塊,提取HOG特徵,並使用模型預測每個小塊是否包含人臉。最終,根據預測結果,在原始影像上繪製出包含人臉的小塊。這個過程展示瞭如何結合影像處理、特徵提取和機器學習模型來實作人臉檢測。

人臉偵測技術探討

在上述程式碼中,我們使用了直方圖梯度(HOG)特徵提取和支援向量機(SVM)分類別器來實作人臉偵測。雖然這個方法取得了不錯的結果,但仍有一些需要改進的地方。

從使用者經驗的角度來看,根據 HOG 特徵與線性 SVM 的人臉偵測技術展現了其在影像識別領域的應用價值。分析其核心流程,從影像預處理、HOG 特徵提取、模型訓練到滑動視窗偵測,每個環節都對最終效能有著重要影響。精確的 HOG 特徵提取捕捉了影像的關鍵結構資訊,而線性 SVM 則提供了高效的分類別能力。然而,此方法也存在一些限制。例如,滑動視窗方法的計算成本較高,尤其在處理高解析度影像時,效率會受到影響。此外,該方法對人臉姿態、光照變化和遮擋等因素的魯棒性仍有待提升。

展望未來,深度學習技術,特別是卷積神經網路(CNN),為人臉偵測提供了更強大的解決方案。CNN 可以自動學習更具區分性的特徵,並在複雜場景下表現出更佳的魯棒性。同時,根據 GPU 的加速技術也大幅提升了深度學習模型的運算效率。考慮到深度學習的快速發展和優異效能,玄貓認為,將深度學習技術整合至人臉偵測系統將是未來的主要趨勢,並能有效提升偵測準確率和效率。對於追求更高效能和更廣泛應用場景的開發者而言,積極探索深度學習方法將是重要的策略。