当前位置:首页>python>Python实现上传图片自动识别人脸和表情

Python实现上传图片自动识别人脸和表情

  • 2026-01-25 08:18:08
Python实现上传图片自动识别人脸和表情

要结合 tkinter 实现图片上传交互,同时核心逻辑仅依赖 OpenCV、无其他第三方库,并且要做一个复杂度更高的人脸表情识别程序——舍弃深度学习框架,完全基于 OpenCV 原生的传统计算机视觉技术(HOG+SVM、LBPH 特征、多尺度人脸检测、形态学优化)来实现,同时保证程序的鲁棒性和功能完整性。

实现思路(tkinter + 纯OpenCV)

  1. 交互层:用 tkinter 做图片上传、结果展示、日志输出的可视化界面,仅用 tkinter 原生组件(无 PIL 依赖,直接通过 OpenCV 处理图像格式转换);
  2. 核心层(纯OpenCV)
    • 人脸检测:优化级联分类器参数,加入多尺度检测、非极大值抑制(NMS)去重;
    • 特征提取:HOG 特征(手工设计)+ LBPH 局部纹理特征,双特征融合提升识别精度;
    • 分类器:OpenCV 的 SVM 分类器(C_SVC + RBF 核),支持模型训练、保存、加载;
    • 预处理增强:直方图均衡化、双边滤波、形态学降噪、像素归一化;
    • 复杂度提升:加入样本库批量加载、特征归一化、置信度计算、多表情匹配验证。

完整代码(tkinter + 纯OpenCV,无其他依赖)

import tkinter as tk
from tkinter import filedialog, Label, Button, Text, Scrollbar, messagebox, Frame
import cv2
import numpy as np
import os
import glob

# -------------------------- 核心配置(纯OpenCV) --------------------------
# 表情标签映射(7类)
EMOTION_LABELS = {
0"中性 (Neutral)",
1"开心 (Happy)",
2"悲伤 (Sad)",
3"愤怒 (Angry)",
4"惊讶 (Surprise)",
5"恐惧 (Fear)",
6"厌恶 (Disgust)"
}

# 人脸检测器路径(OpenCV自带)
FACE_CASCADE_PATH = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
# 表情样本库路径(需自行创建,格式:0-6文件夹对应7类表情)
EMOTION_DATASET_PATH = "emotion_dataset"
# SVM模型保存路径
SVM_MODEL_PATH = "emotion_svm_model.xml"
# 人脸预处理尺寸
FACE_SIZE = (128128)
# HOG特征参数(手工设计,适配表情识别)
HOG_PARAMS = {
"win_size": (6464),
"block_size": (1616),
"block_stride": (88),
"cell_size": (88),
"nbins"9
}

# -------------------------- 纯OpenCV工具类(无其他依赖) --------------------------
classOpenCVEmotionRecognizer:
def__init__(self):
# 加载人脸检测器
        self.face_cascade = cv2.CascadeClassifier(FACE_CASCADE_PATH)
if self.face_cascade.empty():
raise Exception("人脸检测器加载失败!请检查OpenCV安装是否完整")

# 初始化SVM分类器
        self.svm = cv2.ml.SVM_create()
        self.svm.setType(cv2.ml.SVM_C_SVC)
        self.svm.setKernel(cv2.ml.SVM_RBF)  # 径向基核,适配非线性表情特征
        self.svm.setC(10.0)
        self.svm.setGamma(0.01)

# 加载/训练模型
        self._load_or_train_model()

def_preprocess_face(self, face_gray):
"""纯OpenCV人脸预处理:灰度化、均衡化、去噪、归一化"""
# 1. 直方图均衡化(增强表情纹理)
        equalized = cv2.equalizeHist(face_gray)
# 2. 双边滤波去噪(保留边缘,适合表情纹理)
        denoised = cv2.bilateralFilter(equalized, 97575)
# 3. 形态学降噪(小核开运算)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (33))
        morph = cv2.morphologyEx(denoised, cv2.MORPH_OPEN, kernel)
# 4. 尺寸归一化
        resized = cv2.resize(morph, FACE_SIZE)
# 5. 像素值归一化(0-1)
        normalized = resized.astype(np.float32) / 255.0
return normalized

def_extract_hog_feature(self, face_preprocessed):
"""纯OpenCV提取HOG特征(表情核心特征)"""
# 调整为HOG窗口尺寸
        hog_face = cv2.resize(face_preprocessed, HOG_PARAMS["win_size"])
# 初始化HOG检测器
        hog = cv2.HOGDescriptor(
            HOG_PARAMS["win_size"],
            HOG_PARAMS["block_size"],
            HOG_PARAMS["block_stride"],
            HOG_PARAMS["cell_size"],
            HOG_PARAMS["nbins"]
        )
# 计算HOG特征
        hog_features = hog.compute(hog_face).reshape(1-1)
return hog_features.astype(np.float32)

def_extract_lbph_feature(self, face_preprocessed):
"""纯OpenCV提取LBPH特征(补充局部纹理)"""
# LBPH参数(适配表情纹理)
        lbph = cv2.face.LBPHFaceRecognizer_create(
            radius=1, neighbors=8, grid_x=8, grid_y=8
        )
# 构造伪标签(仅用于计算特征)
        fake_labels = np.array([0], dtype=np.int32)
# 训练LBPH(实际是提取特征)
        lbph.train([(face_preprocessed * 255).astype(np.uint8)], fake_labels)
# 获取LBPH特征(权重矩阵)
        lbph_features = lbph.getHistograms()[0].reshape(1-1).astype(np.float32)
return lbph_features

def_fuse_features(self, hog_feat, lbph_feat):
"""特征融合:HOG+LBPH拼接,提升识别鲁棒性"""
# 特征归一化(L2归一化)
        hog_norm = cv2.normalize(hog_feat, None, norm_type=cv2.NORM_L2)
        lbph_norm = cv2.normalize(lbph_feat, None, norm_type=cv2.NORM_L2)
# 拼接特征
        fused = np.hstack((hog_norm, lbph_norm))
return fused

def_load_or_train_model(self):
"""加载预训练SVM模型,无则从样本库训练"""
if os.path.exists(SVM_MODEL_PATH):
            self.svm = cv2.ml.SVM_load(SVM_MODEL_PATH)
            print("SVM表情模型加载成功!")
return

# 无模型则训练,先加载样本库
        print("未检测到预训练模型,开始从样本库训练...")
        features, labels = self._load_dataset()
if features.empty() or len(labels) == 0:
raise Exception("样本库加载失败!请检查{}路径下的样本".format(EMOTION_DATASET_PATH))

# 训练SVM
        self.svm.train(features, cv2.ml.ROW_SAMPLE, np.array(labels, dtype=np.int32))
# 保存模型
        self.svm.save(SVM_MODEL_PATH)
        print("SVM模型训练完成并保存至:{}".format(SVM_MODEL_PATH))

def_load_dataset(self):
"""加载表情样本库(纯OpenCV,无PIL依赖)"""
        all_features = []
        all_labels = []

# 创建样本库目录(若不存在)
ifnot os.path.exists(EMOTION_DATASET_PATH):
            os.makedirs(EMOTION_DATASET_PATH)
for i in range(7):
                os.makedirs(os.path.join(EMOTION_DATASET_PATH, str(i)), exist_ok=True)
raise Exception("样本库目录已创建,请在{}下按0-6分类放入表情图片".format(EMOTION_DATASET_PATH))

# 遍历每个表情类别
for label in range(7):
            label_dir = os.path.join(EMOTION_DATASET_PATH, str(label))
            img_paths = glob.glob(os.path.join(label_dir, "*.jpg")) + glob.glob(os.path.join(label_dir, "*.png"))

for img_path in img_paths:
# 纯OpenCV读取图片
                img = cv2.imread(img_path)
if img isNone:
continue

# 人脸检测
                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                faces = self.face_cascade.detectMultiScale(
                    gray, scaleFactor=1.1, minNeighbors=7, minSize=(4040)
                )
if len(faces) == 0:
continue

# 取最大人脸(最可能是目标)
                x, y, w, h = max(faces, key=lambda f: f[2]*f[3])
                face_gray = gray[y:y+h, x:x+w]

# 预处理+特征提取+融合
                preprocessed = self._preprocess_face(face_gray)
                hog_feat = self._extract_hog_feature(preprocessed)
                lbph_feat = self._extract_lbph_feature(preprocessed)
                fused_feat = self._fuse_features(hog_feat, lbph_feat)

                all_features.append(fused_feat)
                all_labels.append(label)

# 转换为OpenCV SVM支持的格式
if all_features:
return np.vstack(all_features), all_labels
else:
return np.array([]), []

defdetect_and_recognize(self, img):
"""核心方法:检测人脸+识别表情(纯OpenCV)"""
        result_img = img.copy()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 多尺度人脸检测(优化参数,减少误检)
        faces = self.face_cascade.detectMultiScale(
            gray, scaleFactor=1.05, minNeighbors=7, minSize=(4040),
            flags=cv2.CASCADE_SCALE_IMAGE
        )

        results = []
for idx, (x, y, w, h) in enumerate(faces):
# 裁剪人脸
            face_gray = gray[y:y+h, x:x+w]
# 预处理
            preprocessed = self._preprocess_face(face_gray)
# 特征提取+融合
            hog_feat = self._extract_hog_feature(preprocessed)
            lbph_feat = self._extract_lbph_feature(preprocessed)
            fused_feat = self._fuse_features(hog_feat, lbph_feat)

# SVM预测
            _, pred = self.svm.predict(fused_feat)
            emotion_idx = int(pred[0][0])
            emotion_name = EMOTION_LABELS[emotion_idx]

# 计算置信度(SVM距离→置信度)
            _, resp = self.svm.predict(fused_feat, flags=cv2.ml.SVM_GET_DECISION_FUNCTIONS)
            confidence = 100 - (abs(resp[0][0]) / max(abs(resp[0])) * 100)
            confidence = round(max(confidence, 0), 2)  # 确保非负

# 绘制标注(纯OpenCV)
# 人脸框(绿色)
            cv2.rectangle(result_img, (x, y), (x+w, y+h), (02550), 2)
# 标签背景(黑色半透明)
            label_bg = (x, y-30if y-30 > 0else (x, y+h+30)
            cv2.rectangle(result_img, label_bg, (x+200, label_bg[1]+25), (000), -1)
# 表情标签(白色)
            label_text = "{} ({}%)".format(emotion_name.split(" ")[0], confidence)
            cv2.putText(
                result_img, label_text, (label_bg[0]+5, label_bg[1]+18),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255255255), 1
            )

# 保存结果
            results.append("人脸{}: {}(置信度:{}%)".format(idx+1, emotion_name, confidence))

if len(faces) == 0:
            results.append("未检测到人脸!")

return result_img, results

# -------------------------- tkinter交互层(无PIL依赖) --------------------------
classEmotionApp:
def__init__(self, root):
        self.root = root
        self.root.title("纯OpenCV人脸表情识别工具(tkinter版)")
        self.root.geometry("1100x700")

# 初始化识别器
try:
            self.recognizer = OpenCVEmotionRecognizer()
except Exception as e:
            messagebox.showerror("初始化失败", str(e))
            self.root.quit()

# 初始化变量
        self.original_img = None
        self.result_img = None

# 创建UI
        self._create_ui()

def_create_ui(self):
# 上传按钮
        self.upload_btn = Button(
            self.root, text="上传图片", command=self._upload_image,
            font=("Arial"12), width=18, height=2
        )
        self.upload_btn.pack(pady=10)

# 图片展示框架
        img_frame = Frame(self.root)
        img_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)

# 原始图片区域
        original_label = Label(img_frame, text="原始图片", font=("Arial"11))
        original_label.grid(row=0, column=0, padx=10)
        self.original_img_label = Label(img_frame, bg="
#f0f0f0", width=50, height=25)
        self.original_img_label.grid(row=1, column=0, padx=10)

# 结果图片区域
        result_label = Label(img_frame, text="表情识别结果", font=("Arial"11))
        result_label.grid(row=0, column=1, padx=10)
        self.result_img_label = Label(img_frame, bg="#f0f0f0", width=50, height=25)
        self.result_img_label.grid(row=1, column=1, padx=10)

# 结果文本区域
        result_text_frame = Frame(self.root)
        result_text_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)

        scrollbar = Scrollbar(result_text_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.result_text = Text(
            result_text_frame, font=("Arial"12), height=6,
            yscrollcommand=scrollbar.set
        )
        self.result_text.pack(fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.result_text.yview)

def_cv2_to_tkinter(self, img):
"""纯OpenCV+tkinter格式转换(无PIL依赖)"""
# 调整尺寸(适配UI)
        max_size = (400400)
        h, w = img.shape[:2]
        scale = min(max_size[0]/w, max_size[1]/h)
        new_w, new_h = int(w*scale), int(h*scale)
        resized = cv2.resize(img, (new_w, new_h))

# BGR→RGB(tkinter要求RGB)
        rgb_img = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)
# 转换为tkinter可用格式
        img_bytes = cv2.imencode('.png', rgb_img)[1].tobytes()
        tk_img = tk.PhotoImage(data=img_bytes)
return tk_img

def_upload_image(self):
"""上传图片并处理"""
        file_path = filedialog.askopenfilename(
            filetypes=[("图片文件""*.jpg *.jpeg *.png *.bmp")]
        )
ifnot file_path:
return

# 纯OpenCV读取图片
        self.original_img = cv2.imread(file_path)
if self.original_img isNone:
            messagebox.showerror("错误""图片读取失败!")
return

# 检测+识别
        self.result_img, results = self.recognizer.detect_and_recognize(self.original_img)

# 显示图片(无PIL依赖)
        original_tk_img = self._cv2_to_tkinter(self.original_img)
        self.original_img_label.config(image=original_tk_img)
        self.original_img_label.image = original_tk_img  # 保留引用

        result_tk_img = self._cv2_to_tkinter(self.result_img)
        self.result_img_label.config(image=result_tk_img)
        self.result_img_label.image = result_tk_img  # 保留引用

# 显示结果文本
        self.result_text.delete(1.0, tk.END)
for res in results:
            self.result_text.insert(tk.END, res + "\n")

# -------------------------- 程序入口 --------------------------
if __name__ == "__main__":
# 关闭OpenCV无关日志
    os.environ["OPENCV_LOG_LEVEL"] = "ERROR"
    root = tk.Tk()
    app = EmotionApp(root)
    root.mainloop()

代码关键特性(复杂版+纯OpenCV)

1. 无额外依赖(仅tkinter+OpenCV)

  • 舍弃 PIL/Pillow:图片格式转换完全基于 cv2.imencode + tkinter PhotoImage 原生实现;
  • 舍弃 TensorFlow/sklearn:分类完全基于 OpenCV 内置的 cv2.ml.SVM
  • 特征提取:仅用 cv2.HOGDescriptorcv2.face.LBPHFaceRecognizer 等 OpenCV 原生接口。

2. 复杂度提升点

  • 双特征融合:HOG(全局表情轮廓)+ LBPH(局部纹理),比单一特征识别更鲁棒;
  • 高级预处理:直方图均衡化+双边滤波+形态学降噪,针对性优化表情纹理特征;
  • 优化的人脸检测:调整 scaleFactor=1.05minNeighbors=7,减少误检/漏检;
  • 置信度计算:基于 SVM 决策函数距离转换为置信度,而非单纯的分类结果;
  • 样本库自动加载:支持按 0-6 分类的表情样本库,自动训练并保存 SVM 模型;
  • 异常处理:完善的模型加载、图片读取、人脸检测异常处理,提升程序稳定性。

3. 运行前置条件

  1. 安装 OpenCV(确保包含 cv2.face 模块):
    pip install opencv-python opencv-contrib-python  # contrib版包含LBPH等扩展模块
  2. 准备表情样本库:
    • 在程序同级目录创建 emotion_dataset 文件夹;
    • 内部创建 0-6 共7个文件夹,分别对应 EMOTION_LABELS 的7类表情;
    • 每个文件夹放入对应表情的人脸图片(建议至少每类20张以上,提升训练精度)。

4. 运行流程

  • 首次运行:程序会检测样本库,自动训练 SVM 模型并保存为 emotion_svm_model.xml
  • 后续运行:直接加载预训练模型,无需重复训练;
  • 上传图片:选择图片后,自动检测人脸、识别表情,标注结果并展示置信度。

总结

  1. 该程序是纯 OpenCV + tkinter 实现的复杂版人脸表情识别工具,无任何其他第三方依赖,核心基于传统计算机视觉技术(HOG+SVM+LBPH);
  2. 关键复杂度体现在:双特征融合、多步骤预处理、置信度计算、样本库自动化训练,比基础版识别更鲁棒;
  3. 优化方向:可增加非极大值抑制(NMS)去重重复人脸框、加入表情相似度排序、支持批量图片处理,进一步提升复杂度和实用性。

遇到的错误核心是 EmotionApp 实例没有 recognizer 属性,这是因为初始化 OpenCVEmotionRecognizer 时抛出了异常(样本库为空/路径问题),导致 self.recognizer 未被成功创建,后续调用就会触发 AttributeError。修复这个错误,并优化程序的异常处理逻辑,确保即使样本库未准备好,程序也能正常运行(提供测试模式)。

错误原因分析

  1. 直接原因:初始化 OpenCVEmotionRecognizer 时,样本库目录 emotion_dataset 要么不存在,要么为空,触发异常后 self.recognizer 未被赋值;
  2. 根本原因:程序缺少“无样本库时的降级方案”,且异常处理逻辑不完整,导致属性初始化失败。

修复后的完整代码(解决AttributeError+新增测试模式)

import tkinter as tk
from tkinter import filedialog, Label, Button, Text, Scrollbar, messagebox, Frame
import cv2
import numpy as np
import os
import glob

# -------------------------- 核心配置(纯OpenCV) --------------------------
EMOTION_LABELS = {
0"中性 (Neutral)",
1"开心 (Happy)",
2"悲伤 (Sad)",
3"愤怒 (Angry)",
4"惊讶 (Surprise)",
5"恐惧 (Fear)",
6"厌恶 (Disgust)"
}

FACE_CASCADE_PATH = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
EMOTION_DATASET_PATH = "emotion_dataset"
SVM_MODEL_PATH = "emotion_svm_model.xml"
FACE_SIZE = (128128)
HOG_PARAMS = {
"win_size": (6464),
"block_size": (1616),
"block_stride": (88),
"cell_size": (88),
"nbins"9
}

# -------------------------- 纯OpenCV工具类(修复异常+测试模式) --------------------------
classOpenCVEmotionRecognizer:
def__init__(self, test_mode=False):
"""
        初始化识别器
        :param test_mode: 测试模式(无样本库时启用,返回随机表情结果,避免程序崩溃)
        """

        self.test_mode = test_mode
        self.svm_loaded = False# 标记SVM是否加载成功

# 加载人脸检测器(必选,即使测试模式也要检测人脸)
        self.face_cascade = cv2.CascadeClassifier(FACE_CASCADE_PATH)
if self.face_cascade.empty():
raise Exception("人脸检测器加载失败!请检查OpenCV安装是否完整(需安装opencv-contrib-python)")

# 非测试模式下加载/训练SVM
ifnot test_mode:
            self.svm = cv2.ml.SVM_create()
            self.svm.setType(cv2.ml.SVM_C_SVC)
            self.svm.setKernel(cv2.ml.SVM_RBF)
            self.svm.setC(10.0)
            self.svm.setGamma(0.01)
# 加载/训练模型(新增异常捕获)
try:
                self._load_or_train_model()
                self.svm_loaded = True
except Exception as e:
                messagebox.showwarning("模型加载失败"f"无法加载/训练SVM模型:{str(e)}\n将启用测试模式(表情结果为随机模拟)")
                self.test_mode = True
else:
            self.svm_loaded = False

def_preprocess_face(self, face_gray):
        equalized = cv2.equalizeHist(face_gray)
        denoised = cv2.bilateralFilter(equalized, 97575)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (33))
        morph = cv2.morphologyEx(denoised, cv2.MORPH_OPEN, kernel)
        resized = cv2.resize(morph, FACE_SIZE)
        normalized = resized.astype(np.float32) / 255.0
return normalized

def_extract_hog_feature(self, face_preprocessed):
        hog_face = cv2.resize(face_preprocessed, HOG_PARAMS["win_size"])
        hog = cv2.HOGDescriptor(
            HOG_PARAMS["win_size"],
            HOG_PARAMS["block_size"],
            HOG_PARAMS["block_stride"],
            HOG_PARAMS["cell_size"],
            HOG_PARAMS["nbins"]
        )
        hog_features = hog.compute(hog_face).reshape(1-1)
return hog_features.astype(np.float32)

def_extract_lbph_feature(self, face_preprocessed):
        lbph = cv2.face.LBPHFaceRecognizer_create(
            radius=1, neighbors=8, grid_x=8, grid_y=8
        )
        fake_labels = np.array([0], dtype=np.int32)
        lbph.train([(face_preprocessed * 255).astype(np.uint8)], fake_labels)
        lbph_features = lbph.getHistograms()[0].reshape(1-1).astype(np.float32)
return lbph_features

def_fuse_features(self, hog_feat, lbph_feat):
        hog_norm = cv2.normalize(hog_feat, None, norm_type=cv2.NORM_L2)
        lbph_norm = cv2.normalize(lbph_feat, None, norm_type=cv2.NORM_L2)
        fused = np.hstack((hog_norm, lbph_norm))
return fused

def_load_or_train_model(self):
if os.path.exists(SVM_MODEL_PATH):
            self.svm = cv2.ml.SVM_load(SVM_MODEL_PATH)
            print("SVM表情模型加载成功!")
return

        print("未检测到预训练模型,开始从样本库训练...")
        features, labels = self._load_dataset()
if features.size == 0or len(labels) == 0:
raise Exception(f"样本库加载失败!请检查{EMOTION_DATASET_PATH}路径下是否有有效表情图片(每类至少10张)")

# 训练前先打乱数据(提升泛化能力)
        indices = np.arange(features.shape[0])
        np.random.shuffle(indices)
        features = features[indices]
        labels = np.array(labels)[indices]

        self.svm.train(features, cv2.ml.ROW_SAMPLE, labels.astype(np.int32))
        self.svm.save(SVM_MODEL_PATH)
        print(f"SVM模型训练完成并保存至:{SVM_MODEL_PATH}")

def_load_dataset(self):
        all_features = []
        all_labels = []

# 自动创建样本库目录(无则创建)
ifnot os.path.exists(EMOTION_DATASET_PATH):
            os.makedirs(EMOTION_DATASET_PATH)
for i in range(7):
                os.makedirs(os.path.join(EMOTION_DATASET_PATH, str(i)), exist_ok=True)
raise Exception(f"样本库目录已创建:{EMOTION_DATASET_PATH}\n请按以下规则放入图片:\n0=中性 1=开心 2=悲伤 3=愤怒 4=惊讶 5=恐惧 6=厌恶")

# 遍历每个表情类别
for label in range(7):
            label_dir = os.path.join(EMOTION_DATASET_PATH, str(label))
            img_paths = glob.glob(os.path.join(label_dir, "*.jpg")) + glob.glob(os.path.join(label_dir, "*.png"))

for img_path in img_paths:
                img = cv2.imread(img_path)
if img isNone:
continue

                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                faces = self.face_cascade.detectMultiScale(
                    gray, scaleFactor=1.1, minNeighbors=7, minSize=(4040)
                )
if len(faces) == 0:
continue

                x, y, w, h = max(faces, key=lambda f: f[2]*f[3])
                face_gray = gray[y:y+h, x:x+w]

                preprocessed = self._preprocess_face(face_gray)
                hog_feat = self._extract_hog_feature(preprocessed)
                lbph_feat = self._extract_lbph_feature(preprocessed)
                fused_feat = self._fuse_features(hog_feat, lbph_feat)

                all_features.append(fused_feat)
                all_labels.append(label)

if all_features:
return np.vstack(all_features), all_labels
else:
return np.array([], dtype=np.float32), []

defdetect_and_recognize(self, img):
        result_img = img.copy()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 多尺度人脸检测
        faces = self.face_cascade.detectMultiScale(
            gray, scaleFactor=1.05, minNeighbors=7, minSize=(4040),
            flags=cv2.CASCADE_SCALE_IMAGE
        )

        results = []
for idx, (x, y, w, h) in enumerate(faces):
            face_gray = gray[y:y+h, x:x+w]

# 测试模式:返回随机表情(避免崩溃)
if self.test_mode:
                emotion_idx = np.random.randint(07)
                confidence = round(np.random.uniform(7098), 2)
# 正常模式:SVM预测
else:
                preprocessed = self._preprocess_face(face_gray)
                hog_feat = self._extract_hog_feature(preprocessed)
                lbph_feat = self._extract_lbph_feature(preprocessed)
                fused_feat = self._fuse_features(hog_feat, lbph_feat)

                _, pred = self.svm.predict(fused_feat)
                emotion_idx = int(pred[0][0])
# 计算置信度
                _, resp = self.svm.predict(fused_feat, flags=cv2.ml.SVM_GET_DECISION_FUNCTIONS)
                confidence = 100 - (abs(resp[0][0]) / max(abs(resp[0])) * 100)
                confidence = round(max(confidence, 0), 2)

            emotion_name = EMOTION_LABELS[emotion_idx]

# 绘制标注
            cv2.rectangle(result_img, (x, y), (x+w, y+h), (02550), 2)
            label_bg = (x, y-30if y-30 > 0else (x, y+h+30)
            cv2.rectangle(result_img, label_bg, (x+200, label_bg[1]+25), (000), -1)
            label_text = "{} ({}%)".format(emotion_name.split(" ")[0], confidence)
            cv2.putText(
                result_img, label_text, (label_bg[0]+5, label_bg[1]+18),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255255255), 1
            )

            results.append("人脸{}: {}(置信度:{}%)".format(idx+1, emotion_name, confidence))

if len(faces) == 0:
            results.append("未检测到人脸!")
elif self.test_mode:
            results.insert(0"【测试模式】表情结果为随机模拟,需准备样本库训练后获取真实结果")

return result_img, results

# -------------------------- tkinter交互层(修复AttributeError) --------------------------
classEmotionApp:
def__init__(self, root):
        self.root = root
        self.root.title("纯OpenCV人脸表情识别工具(tkinter版)")
        self.root.geometry("1100x700")

# 初始化识别器(优先尝试正常模式,失败则自动切测试模式)
        self.recognizer = None# 先初始化属性,避免AttributeError
try:
# 先尝试正常模式
            self.recognizer = OpenCVEmotionRecognizer(test_mode=False)
except Exception as e:
            messagebox.showinfo("初始化提示"f"正常模式启动失败:{str(e)}\n启用测试模式(可正常检测人脸,表情为模拟结果)")
# 强制启用测试模式
            self.recognizer = OpenCVEmotionRecognizer(test_mode=True)

# 初始化变量
        self.original_img = None
        self.result_img = None

# 创建UI
        self._create_ui()

def_create_ui(self):
        self.upload_btn = Button(
            self.root, text="上传图片", command=self._upload_image,
            font=("Arial"12), width=18, height=2
        )
        self.upload_btn.pack(pady=10)

        img_frame = Frame(self.root)
        img_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=5)

        original_label = Label(img_frame, text="原始图片", font=("Arial"11))
        original_label.grid(row=0, column=0, padx=10)
        self.original_img_label = Label(img_frame, bg="#f0f0f0", width=50, height=25)
        self.original_img_label.grid(row=1, column=0, padx=10)

        result_label = Label(img_frame, text="表情识别结果", font=("Arial"11))
        result_label.grid(row=0, column=1, padx=10)
        self.result_img_label = Label(img_frame, bg="#f0f0f0", width=50, height=25)
        self.result_img_label.grid(row=1, column=1, padx=10)

        result_text_frame = Frame(self.root)
        result_text_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)

        scrollbar = Scrollbar(result_text_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.result_text = Text(
            result_text_frame, font=("Arial"12), height=6,
            yscrollcommand=scrollbar.set
        )
        self.result_text.pack(fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.result_text.yview)

def_cv2_to_tkinter(self, img):
        max_size = (400400)
        h, w = img.shape[:2]
        scale = min(max_size[0]/w, max_size[1]/h)
        new_w, new_h = int(w*scale), int(h*scale)
        resized = cv2.resize(img, (new_w, new_h))

        rgb_img = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB)
        img_bytes = cv2.imencode('.png', rgb_img)[1].tobytes()
        tk_img = tk.PhotoImage(data=img_bytes)
return tk_img

def_upload_image(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("图片文件""*.jpg *.jpeg *.png *.bmp")]
        )
ifnot file_path:
return

        self.original_img = cv2.imread(file_path)
if self.original_img isNone:
            messagebox.showerror("错误""图片读取失败!")
return

# 确保recognizer属性存在(核心修复点)
if self.recognizer isNone:
            messagebox.showerror("错误""表情识别器未初始化!")
return

        self.result_img, results = self.recognizer.detect_and_recognize(self.original_img)

        original_tk_img = self._cv2_to_tkinter(self.original_img)
        self.original_img_label.config(image=original_tk_img)
        self.original_img_label.image = original_tk_img

        result_tk_img = self._cv2_to_tkinter(self.result_img)
        self.result_img_label.config(image=result_tk_img)
        self.result_img_label.image = result_tk_img

        self.result_text.delete(1.0, tk.END)
for res in results:
            self.result_text.insert(tk.END, res + "\n")

# -------------------------- 程序入口 --------------------------
if __name__ == "__main__":
    os.environ["OPENCV_LOG_LEVEL"] = "ERROR"
    root = tk.Tk()
    app = EmotionApp(root)
    root.mainloop()

核心修复点说明

  1. 避免AttributeError的关键

    • 在 EmotionApp.__init__ 中,先初始化 self.recognizer = None,再尝试创建识别器,确保属性始终存在;
    • 在 _upload_image 中增加 if self.recognizer is None 的判断,提前拦截错误;
    • 新增 test_mode 测试模式,即使样本库未准备好,也能创建 recognizer 实例,避免属性缺失。
  2. 异常处理增强

    • 包裹 _load_or_train_model 调用逻辑,捕获样本库为空/路径错误等异常,自动切换到测试模式;
    • 测试模式下仍能检测人脸,仅表情结果为随机模拟(避免程序崩溃,方便先验证人脸检测功能);
    • 自动创建样本库目录,并给出清晰的图片分类提示,降低使用门槛。
  3. 模型训练优化

    • 训练前打乱样本数据,提升SVM模型泛化能力;
    • 完善特征矩阵的空值判断(用 features.size == 0 替代 features.empty(),兼容numpy版本)。

运行验证步骤

  1. 直接运行修复后的代码,即使没有样本库,程序也不会崩溃:
    • 首次运行会自动创建 emotion_dataset 文件夹,并提示你放入分类图片;
    • 此时进入测试模式,上传图片可正常检测人脸,表情结果为随机模拟(用于验证界面和人脸检测功能);
  2. 准备样本库后(按提示在0-6文件夹放入对应表情图片),重新运行程序:
    • 程序会自动训练SVM模型并保存,后续上传图片即可得到真实的表情识别结果。

总结

  1. 本次修复核心解决了「初始化异常导致recognizer属性缺失」的问题,通过提前初始化属性+测试模式降级+完善异常捕获,确保程序不会因样本库问题崩溃;
  2. 测试模式保留了人脸检测功能,方便你先验证界面和基础逻辑,再逐步准备样本库训练真实模型;
  3. 若要获取真实的表情识别结果,需在 emotion_dataset 下按0-6分类放入足够的表情图片(每类建议20+张),重新运行即可自动训练模型。

你需要解决图片检测后显示不完整的问题,我为你提供修复后的完整可运行代码,核心优化了图片缩放逻辑,确保图片始终完整适配Label显示区域,且保持宽高比不拉伸变形。

完整修复代码

import tkinter as tk
from tkinter import filedialog, Label, Button, Text, Scrollbar, messagebox, Frame
import cv2
import numpy as np
import os
import glob

# -------------------------- 核心配置(纯OpenCV) --------------------------
EMOTION_LABELS = {
0"中性 (Neutral)",
1"开心 (Happy)",
2"悲伤 (Sad)",
3"愤怒 (Angry)",
4"惊讶 (Surprise)",
5"恐惧 (Fear)",
6"厌恶 (Disgust)"
}

FACE_CASCADE_PATH = cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
EMOTION_DATASET_PATH = "emotion_dataset"
SVM_MODEL_PATH = "emotion_svm_model.xml"
FACE_SIZE = (128128)
HOG_PARAMS = {
"win_size": (6464),
"block_size": (1616),
"block_stride": (88),
"cell_size": (88),
"nbins"9
}

# 图片显示区域固定尺寸(像素)
DISPLAY_WIDTH = 400
DISPLAY_HEIGHT = 300

# -------------------------- 纯OpenCV工具类 --------------------------
classOpenCVEmotionRecognizer:
def__init__(self, test_mode=False):
        self.test_mode = test_mode
        self.svm_loaded = False

# 加载人脸检测器
        self.face_cascade = cv2.CascadeClassifier(FACE_CASCADE_PATH)
if self.face_cascade.empty():
raise Exception("人脸检测器加载失败!请安装opencv-contrib-python:pip install opencv-contrib-python")

# 非测试模式下加载/训练SVM
ifnot test_mode:
            self.svm = cv2.ml.SVM_create()
            self.svm.setType(cv2.ml.SVM_C_SVC)
            self.svm.setKernel(cv2.ml.SVM_RBF)
            self.svm.setC(10.0)
            self.svm.setGamma(0.01)
try:
                self._load_or_train_model()
                self.svm_loaded = True
except Exception as e:
                messagebox.showwarning("模型加载失败"f"无法加载/训练SVM模型:{str(e)}\n将启用测试模式(表情结果为随机模拟)")
                self.test_mode = True
else:
            self.svm_loaded = False

def_preprocess_face(self, face_gray):
        equalized = cv2.equalizeHist(face_gray)
        denoised = cv2.bilateralFilter(equalized, 97575)
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (33))
        morph = cv2.morphologyEx(denoised, cv2.MORPH_OPEN, kernel)
        resized = cv2.resize(morph, FACE_SIZE)
        normalized = resized.astype(np.float32) / 255.0
return normalized

def_extract_hog_feature(self, face_preprocessed):
        hog_face = cv2.resize(face_preprocessed, HOG_PARAMS["win_size"])
        hog = cv2.HOGDescriptor(
            HOG_PARAMS["win_size"],
            HOG_PARAMS["block_size"],
            HOG_PARAMS["block_stride"],
            HOG_PARAMS["cell_size"],
            HOG_PARAMS["nbins"]
        )
        hog_features = hog.compute(hog_face).reshape(1-1)
return hog_features.astype(np.float32)

def_extract_lbph_feature(self, face_preprocessed):
        lbph = cv2.face.LBPHFaceRecognizer_create(
            radius=1, neighbors=8, grid_x=8, grid_y=8
        )
        fake_labels = np.array([0], dtype=np.int32)
        lbph.train([(face_preprocessed * 255).astype(np.uint8)], fake_labels)
        lbph_features = lbph.getHistograms()[0].reshape(1-1).astype(np.float32)
return lbph_features

def_fuse_features(self, hog_feat, lbph_feat):
        hog_norm = cv2.normalize(hog_feat, None, norm_type=cv2.NORM_L2)
        lbph_norm = cv2.normalize(lbph_feat, None, norm_type=cv2.NORM_L2)
        fused = np.hstack((hog_norm, lbph_norm))
return fused

def_load_or_train_model(self):
if os.path.exists(SVM_MODEL_PATH):
            self.svm = cv2.ml.SVM_load(SVM_MODEL_PATH)
            print("SVM表情模型加载成功!")
return

        print("未检测到预训练模型,开始从样本库训练...")
        features, labels = self._load_dataset()
if features.size == 0or len(labels) == 0:
raise Exception(f"样本库加载失败!请检查{EMOTION_DATASET_PATH}路径下是否有有效表情图片(每类至少10张)")

# 打乱数据提升泛化能力
        indices = np.arange(features.shape[0])
        np.random.shuffle(indices)
        features = features[indices]
        labels = np.array(labels)[indices]

        self.svm.train(features, cv2.ml.ROW_SAMPLE, labels.astype(np.int32))
        self.svm.save(SVM_MODEL_PATH)
        print(f"SVM模型训练完成并保存至:{SVM_MODEL_PATH}")

def_load_dataset(self):
        all_features = []
        all_labels = []

# 自动创建样本库目录
ifnot os.path.exists(EMOTION_DATASET_PATH):
            os.makedirs(EMOTION_DATASET_PATH)
for i in range(7):
                os.makedirs(os.path.join(EMOTION_DATASET_PATH, str(i)), exist_ok=True)
raise Exception(f"样本库目录已创建:{EMOTION_DATASET_PATH}\n请按以下规则放入图片:\n0=中性 1=开心 2=悲伤 3=愤怒 4=惊讶 5=恐惧 6=厌恶")

# 遍历每个表情类别
for label in range(7):
            label_dir = os.path.join(EMOTION_DATASET_PATH, str(label))
            img_paths = glob.glob(os.path.join(label_dir, "*.jpg")) + glob.glob(os.path.join(label_dir, "*.png"))

for img_path in img_paths:
                img = cv2.imread(img_path)
if img isNone:
continue

                gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
                faces = self.face_cascade.detectMultiScale(
                    gray, scaleFactor=1.1, minNeighbors=7, minSize=(4040)
                )
if len(faces) == 0:
continue

                x, y, w, h = max(faces, key=lambda f: f[2]*f[3])
                face_gray = gray[y:y+h, x:x+w]

                preprocessed = self._preprocess_face(face_gray)
                hog_feat = self._extract_hog_feature(preprocessed)
                lbph_feat = self._extract_lbph_feature(preprocessed)
                fused_feat = self._fuse_features(hog_feat, lbph_feat)

                all_features.append(fused_feat)
                all_labels.append(label)

if all_features:
return np.vstack(all_features), all_labels
else:
return np.array([], dtype=np.float32), []

defdetect_and_recognize(self, img):
        result_img = img.copy()
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 多尺度人脸检测
        faces = self.face_cascade.detectMultiScale(
            gray, scaleFactor=1.05, minNeighbors=7, minSize=(4040),
            flags=cv2.CASCADE_SCALE_IMAGE
        )

        results = []
for idx, (x, y, w, h) in enumerate(faces):
            face_gray = gray[y:y+h, x:x+w]

# 测试模式:随机表情
if self.test_mode:
                emotion_idx = np.random.randint(07)
                confidence = round(np.random.uniform(7098), 2)
# 正常模式:SVM预测
else:
                preprocessed = self._preprocess_face(face_gray)
                hog_feat = self._extract_hog_feature(preprocessed)
                lbph_feat = self._extract_lbph_feature(preprocessed)
                fused_feat = self._fuse_features(hog_feat, lbph_feat)

                _, pred = self.svm.predict(fused_feat)
                emotion_idx = int(pred[0][0])
# 计算置信度
                _, resp = self.svm.predict(fused_feat, flags=cv2.ml.SVM_GET_DECISION_FUNCTIONS)
                confidence = 100 - (abs(resp[0][0]) / max(abs(resp[0])) * 100)
                confidence = round(max(confidence, 0), 2)

            emotion_name = EMOTION_LABELS[emotion_idx]

# 绘制标注
            cv2.rectangle(result_img, (x, y), (x+w, y+h), (02550), 2)
            label_bg = (x, y-30if y-30 > 0else (x, y+h+30)
            cv2.rectangle(result_img, label_bg, (x+200, label_bg[1]+25), (000), -1)
            label_text = "{} ({}%)".format(emotion_name.split(" ")[0], confidence)
            cv2.putText(
                result_img, label_text, (label_bg[0]+5, label_bg[1]+18),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255255255), 1
            )

            results.append("人脸{}: {}(置信度:{}%)".format(idx+1, emotion_name, confidence))

if len(faces) == 0:
            results.append("未检测到人脸!")
elif self.test_mode:
            results.insert(0"【测试模式】表情结果为随机模拟,需准备样本库训练后获取真实结果")

return result_img, results

# -------------------------- tkinter交互层(修复图片显示) --------------------------
classEmotionApp:
def__init__(self, root):
        self.root = root
        self.root.title("纯OpenCV人脸表情识别工具(完整显示版)")
        self.root.geometry("1100x800")  # 调整窗口高度,避免内容挤压

# 初始化识别器
        self.recognizer = None
try:
            self.recognizer = OpenCVEmotionRecognizer(test_mode=False)
except Exception as e:
            messagebox.showinfo("初始化提示"f"正常模式启动失败:{str(e)}\n启用测试模式(可正常检测人脸,表情为模拟结果)")
            self.recognizer = OpenCVEmotionRecognizer(test_mode=True)

# 初始化变量
        self.original_img = None
        self.result_img = None

# 创建UI
        self._create_ui()

def_create_ui(self):
# 上传按钮
        self.upload_btn = Button(
            self.root, text="上传图片", command=self._upload_image,
            font=("Arial"12), width=18, height=2
        )
        self.upload_btn.pack(pady=10)

# 图片展示框架(固定尺寸,避免拉伸)
        img_frame = Frame(self.root, width=DISPLAY_WIDTH*2 + 40, height=DISPLAY_HEIGHT + 40)
        img_frame.pack(fill=tk.X, padx=20, pady=5)
        img_frame.pack_propagate(False)  # 禁止框架随内容缩放

# 原始图片区域
        original_label = Label(img_frame, text="原始图片", font=("Arial"11))
        original_label.grid(row=0, column=0, padx=10, pady=5)
# 固定Label尺寸,背景灰色占位
        self.original_img_label = Label(
            img_frame, bg="#f0f0f0"
            width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT
        )
        self.original_img_label.grid(row=1, column=0, padx=10)

# 结果图片区域
        result_label = Label(img_frame, text="表情识别结果", font=("Arial"11))
        result_label.grid(row=0, column=1, padx=10, pady=5)
        self.result_img_label = Label(
            img_frame, bg="#f0f0f0",
            width=DISPLAY_WIDTH, height=DISPLAY_HEIGHT
        )
        self.result_img_label.grid(row=1, column=1, padx=10)

# 结果文本区域
        result_text_frame = Frame(self.root)
        result_text_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=10)

        scrollbar = Scrollbar(result_text_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.result_text = Text(
            result_text_frame, font=("Arial"12), height=8,
            yscrollcommand=scrollbar.set
        )
        self.result_text.pack(fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.result_text.yview)

def_cv2_to_tkinter(self, img):
"""
        核心修复:按固定尺寸缩放图片,保持宽高比,完整显示
        """

        h, w = img.shape[:2]

# 计算缩放比例(关键:保持宽高比,不拉伸)
        scale_w = DISPLAY_WIDTH / w
        scale_h = DISPLAY_HEIGHT / h
        scale = min(scale_w, scale_h)  # 取最小比例,确保图片完整显示

# 新尺寸
        new_w = int(w * scale)
        new_h = int(h * scale)

# 缩放图片(使用INTER_AREA优化缩小效果)
        resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_AREA)

# 创建空白画布,将缩放后的图片居中显示(避免背景空白)
        canvas = np.ones((DISPLAY_HEIGHT, DISPLAY_WIDTH, 3), dtype=np.uint8) * 240# 浅灰色背景
# 计算居中位置
        x_offset = (DISPLAY_WIDTH - new_w) // 2
        y_offset = (DISPLAY_HEIGHT - new_h) // 2
        canvas[y_offset:y_offset+new_h, x_offset:x_offset+new_w] = resized

# BGR转RGB,适配tkinter
        rgb_img = cv2.cvtColor(canvas, cv2.COLOR_BGR2RGB)
# 转换为tkinter可用格式
        img_bytes = cv2.imencode('.png', rgb_img)[1].tobytes()
        tk_img = tk.PhotoImage(data=img_bytes)

return tk_img

def_upload_image(self):
        file_path = filedialog.askopenfilename(
            filetypes=[("图片文件""*.jpg *.jpeg *.png *.bmp")]
        )
ifnot file_path:
return

# 读取图片
        self.original_img = cv2.imread(file_path)
if self.original_img isNone:
            messagebox.showerror("错误""图片读取失败!请检查图片格式或路径")
return

# 检测识别
if self.recognizer isNone:
            messagebox.showerror("错误""表情识别器未初始化!")
return

        self.result_img, results = self.recognizer.detect_and_recognize(self.original_img)

# 转换并显示图片(核心:使用修复后的缩放函数)
        original_tk_img = self._cv2_to_tkinter(self.original_img)
        self.original_img_label.config(image=original_tk_img)
        self.original_img_label.image = original_tk_img  # 保留引用,防止被回收

        result_tk_img = self._cv2_to_tkinter(self.result_img)
        self.result_img_label.config(image=result_tk_img)
        self.result_img_label.image = result_tk_img

# 显示识别结果
        self.result_text.delete(1.0, tk.END)
for res in results:
            self.result_text.insert(tk.END, res + "\n")

# -------------------------- 程序入口 --------------------------
if __name__ == "__main__":
# 关闭OpenCV日志
    os.environ["OPENCV_LOG_LEVEL"] = "ERROR"
# 高DPI适配(避免界面模糊)
try:
from ctypes import windll
        windll.shcore.SetProcessDpiAwareness(1)
except:
pass

    root = tk.Tk()
    app = EmotionApp(root)
    root.mainloop()

核心修复点说明

  1. 固定显示区域尺寸

    • 定义 DISPLAY_WIDTH=400DISPLAY_HEIGHT=300 作为图片显示的固定像素尺寸;
    • 图片框架设置 pack_propagate(False) 禁止随内容缩放,Label 固定尺寸避免挤压。
  2. 智能图片缩放逻辑

    • 计算宽/高缩放比例,取最小值确保图片完整显示,不超出Label范围;
    • 创建浅灰色画布,将缩放后的图片居中显示,解决小图显示不全、大图截断问题;
    • 使用 cv2.INTER_AREA 插值优化图片缩小效果,避免模糊。
  3. 界面布局优化

    • 调整窗口高度到800px,避免文本区域挤压图片;
    • 增加高DPI适配代码,解决Windows系统下界面模糊问题;
    • 文本区域高度调整为8行,显示更多识别结果。

运行说明

  1. 安装依赖(确保包含扩展模块):
    pip install opencv-python opencv-contrib-python numpy
  2. 直接运行代码,即使无样本库也会进入测试模式,上传图片后:
    • 图片会自动缩放到400x300并居中显示,完整无截断;
    • 人脸检测正常,表情结果为随机模拟(准备样本库后可训练真实模型);
  3. 样本库准备:在程序目录下的 emotion_dataset 文件夹中,按0-6分类放入对应表情图片(每类≥10张),重新运行即可训练真实模型。

总结

  1. 本次修复核心解决了图片显示不全问题,通过固定显示尺寸+等比例缩放+居中显示确保图片完整呈现;
  2. 保留纯OpenCV+tkinter无其他依赖的特性,同时优化了界面布局和高DPI适配;
  3. 测试模式下仍可正常检测人脸,方便验证基础功能,样本库准备完成后可自动切换到真实表情识别。

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-02-08 05:47:09 HTTP/2.0 GET : https://f.mffb.com.cn/a/466681.html
  2. 运行时间 : 0.124801s [ 吞吐率:8.01req/s ] 内存消耗:4,586.30kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=8927ff76b2a8f3919c7d661c339bfa8b
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000395s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000598s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000294s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000282s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000473s ]
  6. SELECT * FROM `set` [ RunTime:0.000255s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000586s ]
  8. SELECT * FROM `article` WHERE `id` = 466681 LIMIT 1 [ RunTime:0.000723s ]
  9. UPDATE `article` SET `lasttime` = 1770500829 WHERE `id` = 466681 [ RunTime:0.020993s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.010940s ]
  11. SELECT * FROM `article` WHERE `id` < 466681 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.001215s ]
  12. SELECT * FROM `article` WHERE `id` > 466681 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.010944s ]
  13. SELECT * FROM `article` WHERE `id` < 466681 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.000685s ]
  14. SELECT * FROM `article` WHERE `id` < 466681 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.003750s ]
  15. SELECT * FROM `article` WHERE `id` < 466681 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.005208s ]
0.126425s