高通手機跑AI之——實時頭發識別
(原創作者@CSDN_伊利丹~怒風)
環境準備
手機
測試手機型號:Redmi K60 Pro
處理器:第二代驍龍8移動--8gen2
運行內存:8.0GB ,LPDDR5X-8400,67.0 GB/s
攝像頭:前置16MP+后置50MP+8MP+2MP
AI算力:NPU 48Tops INT8 && GPU 1536ALU x 2 x 680MHz = 2.089 TFLOPS
提示:任意手機均可以,性能越好的手機速度越快
軟件
APP:AidLux2.0
系統環境:Ubuntu 20.04.3 LTS
提示:AidLux登錄后代碼運行更流暢,在代碼運行時保持AidLux APP在前臺運行,避免代碼運行過程中被系統回收進程,另外屏幕保持常亮,一般息屏后一段時間,手機系統會進入休眠狀態,如需長駐后臺需要給APP權限。
算法Demo
Demo代碼介紹
主要功能
這段代碼實現了一個實時人物分割應用,主要功能包括:
-
攝像頭初始化與管理:
- 支持自動檢測和選擇 USB 攝像頭
- 提供備用方案,在無 USB 攝像頭時使用設備內置攝像頭
- 處理攝像頭打開失敗的情況并進行重試
-
AI 模型加載與配置:
- 使用 aidlite 框架加載輕量級神經網絡模型
- 配置模型輸入輸出格式和加速選項(GPU)
- 初始化模型解釋器并準備進行推理
-
實時圖像處理流程:
- 捕獲攝像頭視頻幀
- 圖像預處理(調整大小、顏色空間轉換)
- 模型推理,獲取前景和背景分割結果
- 后處理生成二值掩碼
- 將掩碼與原始圖像疊加顯示
-
性能監控:
- 記錄并打印各個處理階段的耗時
- 計算總處理時間,評估系統性能
這個應用可以用于簡單的實時背景替換、虛擬試衣、視頻會議中的背景虛化等場景。通過調整掩碼生成算法和疊加方式,可以獲得更豐富的視覺效果。
Aidlite 框架
Aidlite 框架 是一個用于模型推理的框架,而非具體模型。它提供了模型加載、加速和執行的功能。
框架功能:
? 模型管理:創建實例、設置輸入輸出格式
? 硬件加速:支持 CPU/GPU/NPU 等多種后端
? 張量操作:輸入數據傳遞、輸出結果獲取
OpenCV 計算機視覺功能
代碼中使用了 OpenCV 的圖像處理功能(如cv2.resize、cv2.addWeighted),但這些屬于傳統計算機視覺算法,并非 AI 模型。
關鍵功能:
- 圖像預處理:調整尺寸、顏色空間轉換
- 圖像合成:將分割掩碼與原始圖像疊加
- 視頻處理:攝像頭捕獲、幀顯示
總結
代碼中實際使用的 AI 模型只有1 個(segmentation.tnnmodel),用于人物分割。另外兩個組件是:
- Aidlite 框架:負責模型推理的執行環境
- OpenCV 庫:提供傳統計算機視覺處理功能
這種架構體現了典型的 AI 應用模式:AI 模型負責核心任務(分割),傳統算法負責前后處理(圖像調整、結果可視化)。
AI模型介紹
這段代碼中的模型是一個輕量級的圖像分割模型,專門用于實時人物與背景的分離。下面從模型架構、功能和應用場景三個方面進行詳細分析:
1. 模型架構
根據代碼中的參數配置,可以推測該模型的特點:
-
輸入要求:
- 輸入尺寸為 256×256×3(RGB 圖像)
- 數據類型為 uint8(0-255 像素值)
- 需將 BGR 格式轉為 RGB(符合大多數深度學習模型的輸入要求)
-
輸出結構:
- 兩個并行輸出張量,形狀均為 256×256
- 數據類型為 float32(浮點數值)
- 結合后處理代碼,推測兩個輸出分別對應:
- pred_0:背景分割概率圖
- pred_1:前景(人物)分割概率圖
-
模型格式:
- 使用 .tnnmodel 格式,表明基于 TNN(Tencent Neural Network)框架優化
- 適合移動端或邊緣設備部署(如 Aidlux 平臺)
2. 應用場景
基于人物分割功能,該模型可用于:
-
視頻會議:背景虛化、虛擬背景替換
-
直播與短視頻:綠幕摳像替代方案
-
AR 試衣 / 美妝:實時疊加虛擬服裝或妝容
-
安防監控:人物檢測與跟蹤的預處理步驟
3. 性能與限制
-
優勢:
-
實時性強:單幀處理時間約幾十毫秒(取決于設備性能)
-
資源占用低:輕量級模型適合嵌入式設備運行
-
效果自然:能處理復雜場景(如頭發、透明物體)
-
-
局限性:
-
依賴光照條件:強光或弱光環境可能降低分割精度
-
邊界細節不足:在復雜邊緣(如薄紗、眼鏡)可能出現鋸齒
-
僅支持單人物:多人物場景可能需要額外的實例分割模型
-
Demo代碼
import cv2
import time
from time import sleep
import subprocess
import remi
import sys
import numpy as np
import aidlite
import os
# 獲取USB攝像頭ID函數
def get_cap_id():
try:
# 執行shell命令獲取所有USB視頻設備的ID
cmd = "ls -l /sys/class/video4linux | awk -F ' -> ' '/usb/{sub(/.*video/, \"\", $2); print $2}'"
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
output = result.stdout.strip().split()
# 將設備ID轉換為整數并返回最小值(優先使用第一個檢測到的USB攝像頭)
video_numbers = list(map(int, output))
if video_numbers:
return min(video_numbers)
else:
return None
except Exception as e:
print(f"獲取攝像頭ID時出錯: {e}")
return None
# 圖像合成函數:將分割掩碼與原始圖像疊加
def transfer(image, mask):
# 調整掩碼大小與原始圖像一致
mask = cv2.resize(mask, (image.shape[1], image.shape[0]))
# 創建與原始圖像相同尺寸的三通道掩碼
mask_n = np.zeros_like(image)
mask_n[:, :, 0] = mask # 將掩碼值賦給藍色通道
# 設置透明度參數
alpha = 0.7 # 原始圖像透明度
beta = (1.0 - alpha) # 掩碼圖像透明度
# 加權疊加原始圖像和掩碼圖像
dst = cv2.addWeighted(image, alpha, mask_n, beta, 0.0)
return dst
# 模型輸入輸出參數配置
w = 256 # 模型輸入寬度
h = 256 # 模型輸入高度
# 定義模型輸入輸出形狀
inShape = [[1, w, h, 3]] # 輸入: 1張RGB圖像,尺寸256x256
outShape = [[1, w, h], [1, w, h]] # 輸出: 兩個256x256的分割圖
model_path = "models/segmentation.tnnmodel" # 模型文件路徑
# 加載模型
model = aidlite.Model.create_instance(model_path)
if model is None:
print("模型創建失敗!")
# 設置模型屬性:輸入為uint8類型,輸出為float32類型
model.set_model_properties(inShape, aidlite.DataType.TYPE_UINT8, outShape, aidlite.DataType.TYPE_FLOAT32)
# 配置模型加速類型為GPU
config = aidlite.Config.create_instance()
config.accelerate_type = aidlite.AccelerateType.TYPE_GPU
# 構建并初始化模型解釋器
fast_interpreter = aidlite.InterpreterBuilder.build_interpretper_from_model_and_config(model, config)
if fast_interpreter is None:
print("解釋器創建失敗!")
result = fast_interpreter.init()
if result != 0:
print("解釋器初始化失敗!")
result = fast_interpreter.load_model()
if result != 0:
print("模型加載失敗!")
print("模型加載成功!")
# 設備類型和攝像頭ID配置
aidlux_type = "basic"
# 0-后置攝像頭,1-前置攝像頭
camId = 1
opened = False
# 嘗試打開攝像頭,失敗則重試
while not opened:
if aidlux_type == "basic":
# 基本設備使用MIPI接口打開前置攝像頭
cap = cv2.VideoCapture(camId, device='mipi')
else:
# 其他設備優先使用USB攝像頭
capId = get_cap_id()
print("USB攝像頭ID: ", capId)
if capId is None:
print("未找到USB攝像頭")
# 默認使用前置攝像頭
cap = cv2.VideoCapture(1, device='mipi')
else:
camId = capId
cap = cv2.VideoCapture(camId)
# 設置視頻編碼格式為MJPG
cap.set(6, cv2.VideoWriter.fourcc('M', 'J', 'P', 'G'))
# 檢查攝像頭是否成功打開
if cap.isOpened():
opened = True
else:
print("攝像頭打開失敗")
cap.release() # 釋放攝像頭資源
time.sleep(0.5) # 等待0.5秒后重試
# 主循環:實時捕獲、處理和顯示視頻
while True:
# 讀取一幀圖像
ret, frame = cap.read()
if not ret:
continue # 讀取失敗則跳過當前幀
if frame is None:
continue # 空幀則跳過
# 如果使用前置攝像頭,水平翻轉圖像以獲得鏡像效果
if camId == 1:
frame = cv2.flip(frame, 1)
# 記錄處理時間點
t0 = time.time()
# 圖像預處理
img = cv2.resize(frame, (w, w)) # 調整圖像大小為模型輸入尺寸
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 轉換顏色空間從BGR到RGB
t1 = time.time()
print('tnn: 開始設置輸入')
# 將預處理后的圖像數據傳入模型
result = fast_interpreter.set_input_tensor(0, img.data)
print(result)
if result != 0:
print("設置輸入張量失敗")
t2 = time.time()
print('tnn: 開始推理')
# 執行模型推理
result = fast_interpreter.invoke()
if result != 0:
print("模型推理失敗")
t3 = time.time()
# 獲取模型輸出結果
pred_1 = fast_interpreter.get_output_tensor(1)
if pred_1 is None:
print("獲取輸出張量1失敗!")
pred_0 = fast_interpreter.get_output_tensor(0)
if pred_0 is None:
print("獲取輸出張量0失敗!")
print('預測結果形狀:', pred_0.shape, pred_1.shape)
t4 = time.time()
# 重塑輸出張量為二維數組
pred0 = (pred_0).reshape(w, h)
pred1 = (pred_1).reshape(w, h)
# 提取背景和前景預測結果
back = ((pred0)).copy()
front = ((pred1)).copy()
t5 = time.time()
# 計算前景掩碼:前景分數減去背景分數
mask = front - back
print('掩碼值范圍:', mask)
# 二值化掩碼:大于0的區域為前景(255),小于等于0的區域為背景(0)
mask[mask > 0] = 255
mask[mask <= 0] = 0
# 將掩碼與原始圖像疊加
dst = transfer(frame, mask)
# 打印各處理階段耗時
print('預處理耗時:%f===設置輸入耗時:%f===推理耗時:%f===獲取輸出耗時:%f===后處理耗時:%f' % (
t1 - t0, t2 - t1, t3 - t2, t4 - t3, t5 - t4))
print('總耗時', t5 - t0)
# 顯示處理結果
cv2.imshow("", dst)
# 按ESC鍵退出程序
key = cv2.waitKey(1)
if key == 27: # ESC鍵
break
# 釋放資源
cap.release()
cv2.destroyAllWindows()
模型位置
/opt/aidlux/app/aid-examples/hair_seg
模型效果


浙公網安備 33010602011771號