InsightFace(RetinaFace + ArcFace)人臉識別項目(預訓練模型,魯棒性很好)

背景介紹
這是一個 簡單的人臉識別項目,用 FastApi 在本地實現,使用預訓練模型,直接可用。
新方案比之前的FaceNet強太多了,甚至不用數據增強等操作,就可以識別戴眼鏡、不戴眼鏡、歪著的人臉等。
充分證明了選型的重要性,選對了模型方案,效果直線上升。
細節說明
1、model.py 的 align_face 方法使用的是 仿射變換,不可以換成 透視變換哦,尤其是 ArcFace 這種場景,仿射變換更合適,原因如下:
①ArcFace 訓練是基于仿射對齊后的人臉圖像(五點坐標 + 仿射對齊 +輸出112x112),改變對齊方式容易導致提取的 embedding 無法匹配;
②人臉在拍攝中的角度變化通常較小,仿射變換可以很好地處理這類剛性變換(左右偏轉,微小仰頭低頭,輕微比例失衡等);
③透視變換自由度太高,容易引入圖像畸變,對 ArcFace 這種要求人臉標準姿態輸入的模型來說可能導致 向量化的不穩定;
可改善
1、可增加活體檢測功能;
項目結構
在這里插入圖片描述
核心代碼都放下面了
src/face_rec/model.py

# src/face_rec/model.py
# RetinaFace + ArcFace + 五點對齊;使用cpuimport cv2, os
import numpy as np
from PIL import Image
from datetime import datetime# insightface 庫:arcface 和 retinaface
from insightface.app import FaceAnalysisclass FaceRecModel:def __init__(self, root_dir):"""初始化 RetinaFace 檢測器 + ArcFace 特征提取器"""self.root_dir = root_dir# FaceAnalysis集成了 人臉檢測器(RetinaFace)、人臉識別器(ArcFace)、關鍵點提取器(5點、106點)# providers:這是 onnxruntime 的后端執行器選擇,代表你想使用什么設備運行模型self.app = FaceAnalysis(providers=['CPUExecutionProvider'])  # 也可以用 GPUExecutionProvider、TensorrtExecutionProvider# prepare是對 FaceAnalysis 進行初始化配置,必須調用,否則模型無法運行。# det_size=(320, 320):會影響 檢測速度和檢測精度,太小-快但容易漏檢小人臉;太大-準但慢,資源消耗大;# 實測 det_size=(320, 320) 比較好用self.app.prepare(ctx_id=-1, det_size=(320, 320))  # ctx_id=-1是CPU,0是GPU,det_size是檢測尺寸(寬,高)# 如果你輸入的圖像尺寸不是 (320, 320),會怎么樣?# insightface 會自動 resize 成指定的 det_size(保持長寬比)來檢測。原圖尺寸不會影響使用,只是檢測過程會先縮放成這個尺寸,然后再還原人臉框到原圖坐標。def get_image_face_vector(self, pil_img, name=None):"""檢測人臉,返回對齊后的最大人臉圖像及其特征向量。:param pil_img: PIL.Image RGB 圖像:param name: 用戶名(保存圖片時使用):return: 對齊后的 PIL.Image(或None),對齊人臉保存路徑(或None),特征向量(或None)"""# 將 PIL 圖像轉成 numpy RGB 數組np_img = np.array(pil_img)# faces 是一個人臉列表,每個元素是一個 insightface.Face 對象(可以理解為一個人臉的“數據包”),包含了檢測框、關鍵點、embedding 特征等信息。# app.get() 方法會執行人臉檢測、特征提取。faces = self.app.get(np_img)if len(faces) == 0:print("[INFO] 未在圖像中檢測到人臉,無法進行對齊和特征提取。")return None, None, None# 選擇最大面積人臉face = max(faces, key=lambda x: x.bbox[2] * x.bbox[3])  # W*H=人臉框面積# 五點關鍵點坐標 (5,2) numpy數組kps = face.kps.astype(np.float32)  # 左眼,右眼,鼻子,左嘴角,右嘴角# 仿射對齊人臉,輸出 112x112 RGB 圖像aligned_face_np = self.align_face(np_img, kps)  # align_face 是自定義函數,返回 numpy 數組# 將對齊后的 numpy 圖像轉回 PIL 圖像aligned_pil = Image.fromarray(aligned_face_np)# 保存對齊后圖像save_dir = os.path.join(self.root_dir, 'aligned_faces')os.makedirs(save_dir, exist_ok=True)# 命名方式:name_時間戳.jpg 或 timestamp.jpgtimestamp = datetime.now().strftime("%Y%m%d_%H%M%S")filename = f"{name}_{timestamp}_insert.jpg" if name else f"face_{timestamp}_search.jpg"save_path = os.path.join(save_dir, filename)aligned_pil.save(save_path)print(f"[INFO] 已保存對齊人臉: {save_path}")# --- 在這里整合特征提取邏輯 ---# 優先使用 app.get() 已經提取的 embedding,因為它是在檢測階段就完成了。并且通常是針對原始圖像的更魯棒的特征。# 如果 face.embedding 存在且不為 None,就直接使用它。if hasattr(face, 'embedding') and face.embedding is not None:embedding = face.embedding.astype(np.float32)print("[INFO] 已成功提取人臉特征。", embedding.mean())else:# 如果 face 對象中沒有 embedding,說明 app.get() 未能成功提取特征。# 這可能是由于識別模型加載失敗、圖像質量問題或其他內部原因。print("[ERROR] FaceAnalysis.get() 未能為檢測到的人臉提取特征。")print("請檢查 FaceAnalysis 的初始化配置和輸入圖像質量。")embedding = None # 無法獲取特征,返回 Nonereturn aligned_pil, save_path, embeddingdef align_face(self, np_img, landmark5):"""使用五點關鍵點仿射變換對齊人臉,輸出 112x112 RGB 圖像(ArcFace標準輸入):param np_img: numpy RGB 圖像:param landmark5: numpy array (5,2) 左眼,右眼,鼻子,左嘴角,右嘴角:return: 對齊后 112x112 RGB numpy 圖像"""# ArcFace標準5點模板坐標,單位像素src = np.array([[38.2946, 51.6963],[73.5318, 51.5014],[56.0252, 71.7366],[41.5493, 92.3655],[70.7299, 92.2041]], dtype=np.float32)dst = landmark5.astype(np.float32)# 計算仿射變換矩陣: 計算 從 dst(檢測的人臉五點) 到 src(ArcFace標準模板五點) 的 仿射變換矩陣(2x3)# 使用 LMEDS(最小中值平方)方法計算仿射矩陣,比普通最小二乘法更穩健(可以抵抗部分錯誤點)。tform = cv2.estimateAffinePartial2D(dst, src, method=cv2.LMEDS)[0]# 使用仿射矩陣 tform 對整張人臉圖像做變換(對齊),輸出大小為 (112, 112) 的人臉圖像# borderValue=0:如果變換后圖像邊界有空白部分,用黑色像素(值為0)填充。aligned = cv2.warpAffine(np_img, tform, (112, 112), borderValue=0)return aligned

src/face_rec/vector.py

# src/face_rec/vector.py
# 使用 FAISS + cosine 相似度做人臉特征向量的存儲與檢索import os
import pickle
import numpy as np
import faiss  # Facebook AI 相似度搜索庫,適合處理大規模向量比對class VectorService:def __init__(self, root_dir):# 保存向量的目錄self.root_dir = os.path.join(root_dir, "embeddings")if not os.path.exists(self.root_dir):os.makedirs(self.root_dir)self.dims = 512  # ArcFace 輸出是 512 維向量# 特征向量文件(以 pickle 存儲)self.embedding_file = os.path.join(self.root_dir, "emb.pkl")if os.path.exists(self.embedding_file):# 如果文件存在就加載已有向量self.embeddings = np.asarray(self.load_pickle_data(self.embedding_file)).astype('float32')else:# 否則初始化為一個全0向量(避免空索引時報錯)self.embeddings = np.zeros((1, self.dims), dtype='float32')# 初始化 FAISS 索引:使用 HNSW(圖結構)+ cosine 相似度(歸一化后使用內積)# HNSW:Hierarchical Navigable Small World 一種高效的圖結構索引算法,適合大規模、近似最近鄰查找(Approximate Nearest Neighbor, ANN)# faiss.METRIC_INNER_PRODUCT:表示使用 內積(dot product) 作為距離度量方式。但這并不是歐氏距離,而是為了計算 cosine 相似度self.face_index = faiss.index_factory(self.dims, 'HNSW16', faiss.METRIC_INNER_PRODUCT)# 如果已有向量,歸一化后加入索引中if self.embeddings.shape[0] > 0:faiss.normalize_L2(self.embeddings)  # L2歸一化后,求內積就是cosine 余弦相似度self.face_index.add(self.embeddings)# ID 到用戶姓名的映射表self.idx_2_name_file = os.path.join(self.root_dir, 'id2name.pkl')if os.path.exists(self.idx_2_name_file):self.idx_2_name = self.load_pickle_data(self.idx_2_name_file)else:self.idx_2_name = {}# 當前用戶索引起始值(從已有數量+1開始)self.user_idx = len(self.idx_2_name) + 1def add_embedding(self, vector, name):"""添加一個人臉向量及其對應用戶名"""vector = np.asarray(vector, dtype='float32').reshape((1, self.dims))faiss.normalize_L2(vector)  # cosine相似度必須先歸一化# 更新內存中的向量數組self.embeddings = np.concatenate([self.embeddings, vector], axis=0)self.face_index.add(vector)  # 加入FAISS索引# 保存向量到文件self.save_pickle_data(self.embedding_file, self.embeddings)# 記錄用戶索引與名字的對應關系self.idx_2_name[self.user_idx] = nameself.user_idx += 1self.save_pickle_data(self.idx_2_name_file, self.idx_2_name)def search(self, vector, thred=0.4):"""給定一個向量,返回最相似的用戶名(如果相似度超過閾值):param vector: 512維特征向量:param thred: 相似度閾值,越高越嚴格(默認0.4):return: 最相似用戶名 或 None"""vector = np.asarray(vector, dtype='float32').reshape((1, self.dims))faiss.normalize_L2(vector)# 搜索與該向量最接近的索引。np.ndarray prob.shape=(1, 1), np.ndarray idx.shape=(1, 1)prob, idx = self.face_index.search(vector, 1)  # 返回前1個最相似結果prob = prob[0, 0]  # 相似度(cosine)idx = idx[0, 0]    # 對應索引值print(f"最大相似度:{prob}(判定閾值:{thred})")if prob < thred:return Nonereturn self.idx_2_name.get(idx, None)@staticmethoddef load_pickle_data(path):"""從指定路徑加載pickle對象"""# 因為 pickle 模塊在保存/讀取 Python 對象時,是按二進制格式編碼的,不是普通文本。with open(path, 'rb') as r:  # 以“二進制”的方式打開一個文件,只讀return pickle.load(r)@staticmethoddef save_pickle_data(path, data):"""將對象保存到pickle文件"""# 因為 pickle 模塊在保存/讀取 Python 對象時,是按二進制格式編碼的,不是普通文本。with open(path, 'wb') as w:  # 以“二進制”的方式打開一個文件,可寫pickle.dump(data, w)

src/face_rec/util.py

# src/face_rec/util.py
# 對齊仍使用五點關鍵點,圖像增強接口保留,增強亮度、對比度、銳度,確保模型識別效果更穩定import numpy as np
from PIL import ImageEnhancedef preprocess_image(pil_image):'''圖像預處理函數(可選):增強亮度、對比度、銳度image (PIL.Image.Image): 輸入的 PIL 圖像對象:np.ndarray: 增強后的圖像,格式為 NumPy 數組,可用于 OpenCV 或模型輸入'''# 亮度增強:增強到原始亮度的 1.3 倍enhancer = ImageEnhance.Brightness(pil_image)image_bright = enhancer.enhance(1.3)# 對比度增強:增強到原始對比度的 1.2 倍enhancer_contrast = ImageEnhance.Contrast(image_bright)image_contrast = enhancer_contrast.enhance(1.2)# 銳度增強:增強到原始銳度的 2.0 倍enhancer_sharp = ImageEnhance.Sharpness(image_contrast)image_sharpened = enhancer_sharp.enhance(2.0)# 將 PIL 圖像轉換為 NumPy 格式(OpenCV 可用)image_sharpened = np.array(image_sharpened)return image_sharpened

src/face_rec/app.py

# src/face_rec/app.pyimport os
import io
import base64
from fastapi import FastAPI, File, Form, UploadFile, Request
from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
from fastapi.templating import Jinja2Templates
from PIL import Image, ImageOpsfrom .model import FaceRecModel                # 引入人臉識別模型封裝
from .vector import VectorService              # 引入向量搜索服務
from .util import preprocess_image             # 可選:圖像預處理(增強)# 獲取靜態資源目錄(如 模型文件、向量文件等) C:\baidu_sync\BaiduSyncdisk\PythonFiles\CV_liuyuan\InsightFace\src\face_rec\static
__static_dir_path__ = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static")# 創建 FastAPI 應用
app = FastAPI()# 指定 FastAPI 中 Jinja2 模板文件目錄(用于返回 HTML 頁面) C:\baidu_sync\BaiduSyncdisk\PythonFiles\CV_liuyuan\InsightFace\src\face_rec\templates
# FastAPI 使用 Jinja2 來支持 HTML 模板渲染(跟 Flask 類似)
templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "templates"))# 實例化加載人臉識別模型與向量庫
model = FaceRecModel(root_dir=__static_dir_path__)
vector = VectorService(root_dir=__static_dir_path__)# 網頁標簽圖片
@app.get("/favicon.ico")
async def favicon():return FileResponse(os.path.join(__static_dir_path__, "favicon.ico"))# 首頁:GET / 或 /index,返回歡迎信息
@app.get("/", response_class=HTMLResponse)
@app.get("/index", response_class=HTMLResponse)
# 異步的處理函數(協程)
async def index(request: Request):return HTMLResponse("歡迎使用簡易人臉檢索系統!")# GET 上傳頁面:返回上傳圖片的 HTML 頁面
@app.get("/face/image/upload", response_class=HTMLResponse)
async def upload_get(request: Request):return templates.TemplateResponse("upload.html", {"request": request})# POST 上傳接口:上傳用戶圖像并提取特征,存入向量庫
@app.post("/face/image/upload")
async def upload_post(name: str = Form(...),          # 表單中提供的人名(作為身份標簽)file: UploadFile = File(...)    # 上傳的圖片文件 (此時 file 是一個 UploadFile 對象,它是一個 異步文件類,用于處理客戶端上傳的文件)
):                                  # HTML 表單的 name 名稱和后端參數名一致;后端用 Form(...) 處理文本、用 File(...) 處理文件# 讀取上傳文件內容并轉為 PIL 圖像# await 表示“等待它完成”并獲取返回值,必須在 async def 里使用contents = await file.read()  # file.read() 是一個異步操作(coroutine),會從上傳的文件中讀取所有字節內容(返回 bytes)img = Image.open(io.BytesIO(contents)).convert("RGB")# 處理圖片方向信息(EXIF)img = ImageOps.exif_transpose(img)# 可選預處理(增強亮度/對比度/銳度)# img = preprocess_image(img)# 檢測并裁剪人臉圖像,保存文件,提取人臉特征向量img, save_path, embedding = model.get_image_face_vector(img, name)if img is None:return templates.TemplateResponse("upload.html", {"request": {}, "msg": "未檢測到人臉區域!"})if embedding is None:return templates.TemplateResponse("upload.html", {"request": {}, "msg": "人臉特征提取失敗!"})# 將特征向量存入數據庫vector.add_embedding(embedding, name)return JSONResponse({"code": 0, "msg": "人臉添加成功!"})# GET 檢索頁面:返回搜索頁面的 HTML 頁面
@app.get("/face/image/search", response_class=HTMLResponse)
async def search_get(request: Request):return templates.TemplateResponse("search.html", {"request": request})# POST 檢索接口:上傳圖像 -> 提取特征 -> 搜索庫中最相似用戶
@app.post("/face/image/search")
async def search_post(file: UploadFile = File(...)):contents = await file.read()img = Image.open(io.BytesIO(contents)).convert("RGB")# 處理圖片方向信息(EXIF)img = ImageOps.exif_transpose(img)# 可選預處理(增強亮度/對比度/銳度)# img = preprocess_image(img)# 檢測并裁剪人臉圖像,保存文件,提取人臉特征向量img, save_path, embedding = model.get_image_face_vector(img)if img is None:return templates.TemplateResponse("search.html", {"request": {}, "msg": "未檢測到人臉區域!"})if embedding is None:return templates.TemplateResponse("search.html", {"request": {}, "msg": "人臉特征提取失敗!"})# 在向量庫中進行相似度匹配name = vector.search(embedding)if name is None:return templates.TemplateResponse("search.html", {"request": {}, "msg": "人臉未匹配到用戶!"})return JSONResponse({"code": 0, "msg": "search success!", "name": name})# POST 檢測接口(用于 API 接口)接收 base64 圖像,輸出識別結果
@app.post('/face/image/detect')
async def detect_post(request: Request):data = await request.json()img_b64 = data.get('img')if img_b64 is None:return JSONResponse({'code': 202, 'msg': '必須提供 img 參數!'})# 解碼 base64 圖像并轉換為 PIL 圖像img_data = base64.b64decode(img_b64)img = Image.open(io.BytesIO(img_data)).convert("RGB")# 處理圖片方向信息(EXIF)img = ImageOps.exif_transpose(img)# 可選預處理(增強亮度/對比度/銳度)# img = preprocess_image(img)# 檢測并裁剪人臉圖像,保存文件,提取人臉特征向量img, save_path, embedding = model.get_image_face_vector(img)if img is None:return JSONResponse({'code': 203, 'msg': '未檢測到人臉區域!'})if embedding is None:return JSONResponse({'code': 204, 'msg': '人臉特征提取失敗!'})name = vector.search(embedding)if name is None:return JSONResponse({'code': 205, 'msg': '人臉未匹配到用戶!'})return JSONResponse({'code': 200, 'msg': 'search success!', 'name': name})

test/main.py

# test/main.py
# 項目入口文件,用于啟動 FastAPI 服務(人臉識別 Web 接口)import warnings
warnings.filterwarnings('ignore')  # 忽略所有警告信息,保持輸出干凈import os
import sys
import uvicorn  # 用于運行 FastAPI 的 ASGI 服務器
# ASGI(Asynchronous Server Gateway Interface)是 Python 的一種 Web 應用服務器接口規范,是 WSGI 的升級版,用于支持異步編程。
# ASGI 是連接 Web 框架(如 FastAPI)和底層服務器(如 Uvicorn)的橋梁接口標準,支持異步請求處理。# 避免 OpenMP 多線程庫沖突(常用于 PyTorch 或 NumPy 內部),防止報錯
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"# 將 src 目錄添加到模塊導入路徑,確保可以導入 src 下的自定義模塊
# 例如:C:\baidu_sync\BaiduSyncdisk\PythonFiles\CV_liuyuan\InsightFace\src
sys.path.append(os.path.join(os.path.dirname(__file__), "..", "src"))# 導入 FastAPI 應用實例(app)供 uvicorn 啟動
from src.face_rec.app import app# 運行服務
if __name__ == "__main__":# 啟動 FastAPI 應用,監聽所有 IP,端口為 9999,日志級別為 infouvicorn.run(app, host="0.0.0.0", port=9999, log_level="info")  # 正常啟動# uvicorn.run(app, host="0.0.0.0", port=9999, log_level="info", reload=False)  # 調試啟動(debug)

test/client.py

# test/client.py
"""客戶端測試代碼:向 FastAPI 服務端發送 base64 編碼的人臉圖像,接收識別結果"""import base64
from typing import Optional
import requests
import cv2
import numpy as np
from io import BytesIO
from pathlib import Path
from PIL import Image
from datetime import datetimedef fetch_person_name(img: np.ndarray) -> Optional[str]:"""給定一張圖像(OpenCV格式 BGR),向服務端發送請求,返回識別結果:param img: OpenCV 格式圖像(BGR):return: 返回識別出的姓名字符串,或 None"""# 轉換 OpenCV (BGR) -> PIL (RGB)pil_img = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))# 將圖像保存為 PNG 到內存buffer = BytesIO()pil_img.save(buffer, format='PNG')img_bytes = buffer.getvalue()# 編碼成 base64 字符串img_b64 = base64.b64encode(img_bytes).decode('utf-8')# 構造 JSON 請求體payload = {'img': img_b64}headers = {'Content-Type': 'application/json'}try:# 向本地服務發送 POST 請求(FastAPI)response = requests.post('http://127.0.0.1:9999/face/image/detect', json=payload, headers=headers)if response.status_code == 200:result = response.json()if result.get('code') == 200:return result.get('name')else:print(f"[服務返回] 識別失敗: {result}")else:print(f"[HTTP錯誤] 狀態碼: {response.status_code}")except Exception as e:print(f"[異常] 請求失敗: {e}")return Noneif __name__ == '__main__':# 加載測試圖像img_path = Path('..') / 'data' / 'img0.jpeg'img = cv2.imread(str(img_path))if img is None:print(f"圖像讀取失敗: {img_path}")exit(1)  # 程序異常退出,錯誤碼為 1# 獲取識別結果name = fetch_person_name(img)print(f"識別結果: {name}")# 畫圖、顯示(如有識別結果,在圖像上繪制姓名)if name:cv2.putText(img, name, (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 2)  # BGR,紅色255else:cv2.putText(img, "No Match", (10, 50), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 255, 255), 2)cv2.imshow('Face Recognition Result', img)cv2.waitKey(0)  # 等待任意鍵cv2.destroyAllWindows()# The function is not implemented
# OpenCV 安裝缺少圖形界面后端支持,默認安裝的是一個輕量級的版本,
# 不包含 GUI (圖形用戶界面) 功能,例如 cv2.imshow()、cv2.waitKey() 等。這在服務器環境或不需要顯示圖像的場景下是常見的。

src/face_rec/templates/upload.html

<!-- upload.html 人臉圖像錄入表單頁的模板 --><!DOCTYPE html>
<html lang="zh-CN">  <!-- 設置頁面語言為中文(簡體) --><head><meta charset="UTF-8">  <!-- 設置網頁的字符編碼為 UTF-8,支持中文 --><title>人臉錄入</title>  <!-- 網頁標題:瀏覽器標簽頁上顯示 -->
</head><body><h1>員工人臉打卡系統(人臉錄入)</h1><!-- 若后端傳遞了 msg(通常是錯誤或提示信息),則顯示為紅色加粗字體 -->{% if msg %}<strong style="color:red">{{msg}}</strong>{% endif %}<!-- 上傳表單 --><!-- method="POST" 表示表單提交方式為 POST --><!-- action="/face/image/upload" 表示提交到后端對應的 Flask 路由 --><!-- enctype="multipart/form-data" 表示表單中包含文件上傳字段(如圖像) --><form method="POST" action="/face/image/upload" enctype="multipart/form-data"><!-- 文本輸入框:輸入姓名 -->姓名:<input type="text" name="name"/> <br/><br/><!-- 文件選擇框:上傳圖像文件 -->圖像:<input type="file" name="file"/> <br/><br/><!-- 提交按鈕 --><input type="submit" name="submit" value="錄入"/></form>
</body>
</html>

src/face_rec/templates/search.html

<!--人臉圖像檢索表單頁的模板-->
<!DOCTYPE html>
<html lang="zh-CN">  <!-- 設置頁面語言為中文(簡體) --><head><meta charset="UTF-8">  <!-- 設置網頁的字符編碼為 UTF-8,支持中文 --><title>人臉檢索</title>  <!-- 網頁標題:瀏覽器標簽頁上顯示 -->
</head><body><h1>員工人臉打卡系統(人臉檢索)</h1><!-- 若后端傳遞了 msg(通常是錯誤或提示信息),則顯示為紅色加粗字體 -->{% if msg %}<strong style="color:red">{{msg}}</strong>{% endif %}<!-- 上傳表單 --><!-- method="POST" 表示表單提交方式為 POST --><!-- action="/face/image/search" 表示提交到后端對應的 Flask 路由 --><!-- enctype="multipart/form-data" 表示表單中包含文件上傳字段(如圖像) --><form method="POST" action="/face/image/search" enctype="multipart/form-data"><!-- 文件選擇框:上傳圖像文件 -->圖像:<input type="file" name="file"/> <br/><br/><!-- 提交按鈕 --><input type="submit" name="submit" value="檢索"/></form>
</body>
</html>

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/93232.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/93232.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/93232.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

App Inventor 2 使用 MaterialIcons 圖標字體,快捷展示專業圖標

平時布局的話&#xff0c;如果要使用圖標&#xff0c;一般需要去找 png 圖片&#xff0c;且透明背景的。如果需要根據不同常見圖標進行變色的話&#xff0c;就需要準備多張不同樣式的圖標&#xff0c;還要考慮圖片的分辨率等等因素&#xff0c;非常的麻煩。 這時&#xff0c;如…

C語言——關于指針(逐漸清晰版)

為了更好地理解本篇文章的知識內容&#xff0c;讀者可以將以下文章作為補充知識進行閱讀 &#xff1a; C語言————原碼 補碼 反碼 &#xff08;超絕詳細解釋&#xff09;-CSDN博客 C語言————二、八、十、十六進制的相互轉換-CSDN博客 C語言————斐波那契數列的理解…

SVG 在線編輯器

SVG 在線編輯器 引言 隨著互聯網技術的發展&#xff0c;矢量圖形在網頁設計和數據可視化中扮演著越來越重要的角色。SVG&#xff08;可縮放矢量圖形&#xff09;因其文件小、無限縮放不模糊的特性&#xff0c;成為了網頁設計中常用的圖形格式。SVG 在線編輯器的出現&#xff0c…

libpostproc 已經從 ffmpeg 中移除,導致編譯 ffmpeg 時沒有 libpostproc

今天編譯 ffmpeg 時突然發現 libpostproc 不見了&#xff0c;-enable-postproc 也變成了非法的選項。用搜索引擎搜索相關信息找不到一點&#xff0c;于是去 github 看。 從提交記錄可以看到 libpostproc 已經被移除了 鏈接 主線中已經看不到了 libpostproc 這個目錄了

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通

基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 文章目錄 基于 Dell PowerEdge T440 搭建的 Proxmox VE 配置 RTX 3060 顯卡直通虛擬機、切換直通 1. 前言 2. 前提條件 3. 配置步驟 3.1. 啟用 VT-d 3.2. 激活 IOMMU 3.3. 添加 VFIO 模塊 …

如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題

【Python系列Bug修復PyCharm控制臺pip install報錯】如何解決pip安裝報錯ModuleNotFoundError: No module named ‘voila’問題 摘要 在開發過程中&#xff0c;我們常常會遇到pip安裝包時出現各種錯誤&#xff0c;特別是在使用PyCharm進行開發時。本文將詳細介紹如何解決安裝…

[spring6: @EnableWebMvc]-源碼分析

源碼 EnableWebMvc EnableWebMvc 是用于啟用 Spring MVC 的注解&#xff0c;它通過導入 DelegatingWebMvcConfiguration 來加載默認的 MVC 配置&#xff0c;同時允許開發者通過實現 WebMvcConfigurer 接口來自定義部分配置&#xff1b;若需更高階的控制&#xff0c;則可直接繼承…

Jmeter的元件使用介紹:(四)前置處理器詳解

Jmeter的前置處理器可以用來在取樣器執行前做一些數據準備操作&#xff0c;也需要注意使用的作用域問題。常用的前置處理器有&#xff1a;用戶參數、BeanShell預處理器、JDBC預處理器。一、用戶參數 【用戶參數】與前面介紹過的【用戶定義的變量】有相似之處&#xff0c;先來介…

十七、K8s 可觀測性:全鏈路追蹤

十七、K8s 可觀測性&#xff1a;全鏈路追蹤 文章目錄十七、K8s 可觀測性&#xff1a;全鏈路追蹤1、Skywalking 初識1.1 為什么需要全鏈路追蹤平臺1.2 全鏈路追蹤核心組件及工作原理1.2.1 全鏈路追蹤核心概念1.2.2 全鏈路追蹤工作原理1.3 什么是Skywalking&#xff1f;1.4 Skywa…

2025 Gitee vs. GitLab:全面對比與選擇指南

在軟件研發持續加速、合規要求日益嚴格的背景下&#xff0c;選擇合適的代碼托管平臺成為團隊數字化能力建設的關鍵環節。尤其在中國本土市場&#xff0c;Gitee正憑借其深度本地化能力、全面生態整合和開源社區支撐&#xff0c;成為國內團隊首選的開發協作平臺。 一、Gitee&…

期貨反向跟單忌諱問題(一): 不斷調整盤手交易規則

在期貨反向跟單領域&#xff0c;不少運營者在摸著石頭過河的過程中&#xff0c;容易陷入一個致命誤區——對盤手交易規則的頻繁調整。這種看似“優化策略”的舉動&#xff0c;往往會讓整個跟單體系陷入惡性循環&#xff0c;最終偏離盈利初衷。期貨反向跟單的核心邏輯是&#xf…

Effective C++ 條款07:為多態基類聲明virtual析構函數

Effective C 條款07&#xff1a;為多態基類聲明virtual析構函數核心思想&#xff1a;當通過基類指針刪除派生類對象時&#xff0c;如果基類沒有虛析構函數&#xff0c;會導致派生類資源泄漏。因為此時只會調用基類的析構函數&#xff0c;而不會調用派生類的析構函數。 ?? 1. …

C++進階—C++11

第一章&#xff1a;C11簡介 在2003年C標準委員會曾經提交了一份技術勘誤表(簡稱TC1)&#xff0c;使得C03這個名字已經取代了C98稱為C11之前的最新C標準名稱。不過由于C03(TC1)主要是對C98標準中的漏洞進行修復&#xff0c;語言的核心部分則沒有改動&#xff0c;因此人們習慣性…

把振動數據轉成音頻并播放

把振動數據轉聲音并播放 1、實現流程 安裝第三方庫: pip install numpy==1.23.5 pip install scipy==1.10.1 pip install sounddevice==0.4.6流程: 1、導入振動數據 2、數據歸一化到[-1, 1]范圍 3、重采樣到44.1kHz 4、播放音頻 5、保存音頻為WAV文件(可選)2、代碼示例 …

ServBay 1.15.0 更新,擁抱 Bun Deno 新生態

歷時一個月&#xff0c;ServBay迎來了1.15.0的更新。我們始終堅信&#xff0c;一個優秀的本地開發環境&#xff0c;不僅要穩定、高效&#xff0c;更要緊跟技術的演進脈搏。ServBay 的使命是為開發者掃清開發環境配置的障礙&#xff0c;讓您能聚焦于創造本身。 本次ServBay 1.1…

Java設計模式-通俗舉例

設計模式就像做菜的食譜&#xff0c;告訴我們遇到常見問題時該用什么"烹飪方法"。今天我就用最生活化的例子&#xff0c;帶大家輕松掌握23種設計模式的精髓。一、創建型模式&#xff08;5種&#xff09;&#xff1a;怎么"造東西"1. 單例模式&#xff1a;公…

【跟我學YOLO】YOLO12(3)訓練自己的數據集

歡迎關注『跟我學 YOLO』系列 【跟我學YOLO】&#xff08;1&#xff09;YOLO12&#xff1a;以注意力為中心的物體檢測 【跟我學YOLO】&#xff08;2&#xff09;YOLO12 環境配置與基本應用 【跟我學YOLO】&#xff08;3&#xff09;YOLO12 訓練自己的數據集 【跟我學YOLO】&…

【NLP輿情分析】基于python微博輿情分析可視化系統(flask+pandas+echarts) 視頻教程 - 微博輿情分析實現

大家好&#xff0c;我是java1234_小鋒老師&#xff0c;最近寫了一套【NLP輿情分析】基于python微博輿情分析可視化系統(flaskpandasecharts)視頻教程&#xff0c;持續更新中&#xff0c;計劃月底更新完&#xff0c;感謝支持。今天講解微博輿情分析實現 視頻在線地址&#xff1…

【C++】手搓一個STL風格的vector容器

TOC(手搓一個STL風格的vector容器) 手搓一個STL風格的vector容器 github地址 有夢想的電信狗 0. 前言&#xff1a;動態數組的工程實踐 ? 在C標準庫中&#xff0c;vector容器作為最核心的序列式容器&#xff0c;其設計融合了動態數組的高效性與安全性。本文將通過完整實現…

24. 了解過 webp 嗎

總結 一種圖片格式 一、什么是 WebP&#xff1f; WebP&#xff08;發音為 “weppy”&#xff09;是由 Google 推出的一種現代圖片格式&#xff0c;支持有損壓縮和無損壓縮&#xff0c;旨在提供更小的文件體積和更高質量的圖像顯示。 它兼容常見的圖片功能&#xff0c;如&#…