基于 OpenCV + Qt 的水果智能识别分类系统

一、系统架构设计

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    水果智能识别分类系统                      │
├─────────────────────────────────────────────────────────────┤
│  用户界面层  │  业务逻辑层  │  图像处理层  │  算法核心层   │
│             │             │             │              │
│  • Qt界面   │  • 视频采集  │  • 预处理    │  • 颜色识别   │
│  • 按钮控制 │  • 文件管理  │  • 分割提取  │  • 边缘检测   │
│  • 结果显示 │  • 结果统计  │  • 特征提取  │  • 形状分析   │
│  • 参数调节 │  • 日志记录  │  • 形态学操作 │  • SVM分类器  │
└─────────────────────────────────────────────────────────────┘

二、开发环境配置

2.1 环境要求

bash 复制代码
# 操作系统:Windows 10 / Ubuntu 18.04+
# 开发工具:Qt Creator 5.14+ / Visual Studio 2019+
# OpenCV版本:OpenCV 4.5+

# Ubuntu安装命令
sudo apt update
sudo apt install qt5-default libopencv-dev cmake build-essential

2.2 CMakeLists.txt 配置

cmake 复制代码
cmake_minimum_required(VERSION 3.10)
project(FruitRecognitionSystem VERSION 1.0)

# 设置C++标准
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# 查找Qt5
find_package(Qt5 COMPONENTS Widgets Core Gui REQUIRED)

# 查找OpenCV
find_package(OpenCV REQUIRED)

# 包含目录
include_directories(${OpenCV_INCLUDE_DIRS})
include_directories(${Qt5Widgets_INCLUDES})
include_directories(${PROJECT_SOURCE_DIR}/include)

# 源文件
set(SOURCES
    src/main.cpp
    src/mainwindow.cpp
    src/fruitrecognizer.cpp
    src/imageprocessor.cpp
    src/featureextractor.cpp
    src/classifier.cpp
)

# 头文件
set(HEADERS
    include/mainwindow.h
    include/fruitrecognizer.h
    include/imageprocessor.h
    include/featureextractor.h
    include/classifier.h
)

# 创建可执行文件
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})

# 链接库
target_link_libraries(${PROJECT_NAME}
    Qt5::Widgets
    Qt5::Core
    Qt5::Gui
    ${OpenCV_LIBS}
)

三、核心代码实现

3.1 主窗口界面 (mainwindow.h/cpp)

cpp 复制代码
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QImage>
#include <QPixmap>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>
#include <QDateTime>

#include "fruitrecognizer.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_loadImage_clicked();
    void on_pushButton_startCamera_clicked();
    void on_pushButton_stopCamera_clicked();
    void on_pushButton_recognize_clicked();
    void on_pushButton_saveResult_clicked();
    void updateCameraFrame();
    void updateRecognitionResult();

private:
    Ui::MainWindow *ui;
    FruitRecognizer *fruitRecognizer;
    QTimer *cameraTimer;
    cv::VideoCapture videoCapture;
    bool isCameraRunning;
    
    void setupUI();
    void updateStatusBar(const QString &message);
    void displayImage(const cv::Mat &image, QLabel *label);
    void saveResultToDatabase(const QString &fruitName, double confidence);
};

#endif // MAINWINDOW_H
cpp 复制代码
// mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    
    // 初始化组件
    fruitRecognizer = new FruitRecognizer();
    cameraTimer = new QTimer(this);
    isCameraRunning = false;
    
    // 连接信号槽
    connect(cameraTimer, SIGNAL(timeout()), this, SLOT(updateCameraFrame()));
    connect(ui->pushButton_loadImage, SIGNAL(clicked()), this, SLOT(on_pushButton_loadImage_clicked()));
    connect(ui->pushButton_startCamera, SIGNAL(clicked()), this, SLOT(on_pushButton_startCamera_clicked()));
    connect(ui->pushButton_stopCamera, SIGNAL(clicked()), this, SLOT(on_pushButton_stopCamera_clicked()));
    connect(ui->pushButton_recognize, SIGNAL(clicked()), this, SLOT(on_pushButton_recognize_clicked()));
    connect(ui->pushButton_saveResult, SIGNAL(clicked()), this, SLOT(on_pushButton_saveResult_clicked()));
    
    setupUI();
    updateStatusBar("系统已就绪,请选择图片或启动摄像头");
}

void MainWindow::setupUI() {
    setWindowTitle("智能水果识别分类系统 v1.0");
    resize(1200, 800);
    
    // 设置默认图片
    QPixmap defaultPixmap(640, 480);
    defaultPixmap.fill(Qt::lightGray);
    ui->label_originalImage->setPixmap(defaultPixmap);
    ui->label_processedImage->setPixmap(defaultPixmap);
    
    // 初始化结果显示
    ui->textEdit_results->setPlainText("等待识别结果...");
}

void MainWindow::on_pushButton_loadImage_clicked() {
    QString fileName = QFileDialog::getOpenFileName(this, 
                                                   "选择图片", 
                                                   "", 
                                                   "Images (*.png *.jpg *.jpeg *.bmp)");
    
    if (!fileName.isEmpty()) {
        cv::Mat image = cv::imread(fileName.toStdString());
        if (!image.empty()) {
            displayImage(image, ui->label_originalImage);
            fruitRecognizer->setOriginalImage(image);
            updateStatusBar("图片加载成功: " + fileName);
        } else {
            QMessageBox::warning(this, "错误", "无法加载图片文件!");
        }
    }
}

void MainWindow::on_pushButton_startCamera_clicked() {
    if (!isCameraRunning) {
        videoCapture.open(0); // 打开默认摄像头
        if (videoCapture.isOpened()) {
            isCameraRunning = true;
            cameraTimer->start(33); // 30 FPS
            ui->pushButton_startCamera->setEnabled(false);
            ui->pushButton_stopCamera->setEnabled(true);
            updateStatusBar("摄像头已启动");
        } else {
            QMessageBox::warning(this, "错误", "无法打开摄像头!");
        }
    }
}

void MainWindow::on_pushButton_stopCamera_clicked() {
    if (isCameraRunning) {
        cameraTimer->stop();
        videoCapture.release();
        isCameraRunning = false;
        ui->pushButton_startCamera->setEnabled(true);
        ui->pushButton_stopCamera->setEnabled(false);
        updateStatusBar("摄像头已停止");
    }
}

void MainWindow::updateCameraFrame() {
    cv::Mat frame;
    videoCapture >> frame;
    
    if (!frame.empty()) {
        displayImage(frame, ui->label_originalImage);
        fruitRecognizer->setOriginalImage(frame);
    }
}

void MainWindow::on_pushButton_recognize_clicked() {
    if (fruitRecognizer->hasImage()) {
        updateStatusBar("正在识别水果...");
        
        // 执行识别
        FruitResult result = fruitRecognizer->recognizeFruit();
        
        // 显示处理结果
        cv::Mat processedImage = fruitRecognizer->getProcessedImage();
        displayImage(processedImage, ui->label_processedImage);
        
        // 显示识别结果
        QString resultText = QString("识别结果:\n"
                                     "水果名称: %1\n"
                                     "置信度: %2%\n"
                                     "颜色特征: %3\n"
                                     "形状特征: %4\n"
                                     "识别时间: %5")
                             .arg(QString::fromStdString(result.fruitName))
                             .arg(result.confidence, 0, 'f', 2)
                             .arg(QString::fromStdString(result.colorFeature))
                             .arg(QString::fromStdString(result.shapeFeature))
                             .arg(QDateTime::currentDateTime().toString("hh:mm:ss"));
        
        ui->textEdit_results->setPlainText(resultText);
        updateStatusBar("识别完成");
    } else {
        QMessageBox::warning(this, "提示", "请先加载图片或启动摄像头!");
    }
}

void MainWindow::displayImage(const cv::Mat &image, QLabel *label) {
    if (image.empty()) return;
    
    cv::Mat rgbImage;
    if (image.channels() == 3) {
        cv::cvtColor(image, rgbImage, cv::COLOR_BGR2RGB);
    } else {
        rgbImage = image.clone();
    }
    
    QImage qImage(rgbImage.data, rgbImage.cols, rgbImage.rows, 
                  rgbImage.step, QImage::Format_RGB888);
    
    QPixmap pixmap = QPixmap::fromImage(qImage);
    label->setPixmap(pixmap.scaled(label->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation));
}

void MainWindow::updateStatusBar(const QString &message) {
    ui->statusbar->showMessage(message);
}

MainWindow::~MainWindow() {
    if (isCameraRunning) {
        videoCapture.release();
    }
    delete fruitRecognizer;
    delete cameraTimer;
    delete ui;
}

3.2 水果识别核心类 (fruitrecognizer.h/cpp)

cpp 复制代码
// fruitrecognizer.h
#ifndef FRUITRECOGNIZER_H
#define FRUITRECOGNIZER_H

#include <opencv2/opencv.hpp>
#include <vector>
#include <string>
#include <map>

// 水果识别结果结构体
struct FruitResult {
    std::string fruitName;
    double confidence;
    std::string colorFeature;
    std::string shapeFeature;
    cv::Rect boundingBox;
    std::vector<cv::Point> contour;
};

// 水果特征结构体
struct FruitFeatures {
    std::vector<float> colorHistogram;
    std::vector<float> shapeFeatures;
    std::vector<float> textureFeatures;
    cv::Rect boundingBox;
};

class FruitRecognizer {
public:
    FruitRecognizer();
    ~FruitRecognizer();
    
    // 图像设置
    void setOriginalImage(const cv::Mat &image);
    bool hasImage() const { return !originalImage.empty(); }
    
    // 识别接口
    FruitResult recognizeFruit();
    cv::Mat getProcessedImage() const { return processedImage; }
    
    // 参数设置
    void setColorThreshold(const cv::Scalar &lower, const cv::Scalar &upper);
    void setMinFruitArea(int area) { minFruitArea = area; }
    void setMaxFruitArea(int area) { maxFruitArea = area; }
    
private:
    // 图像处理
    cv::Mat preprocessImage(const cv::Mat &image);
    cv::Mat segmentFruit(const cv::Mat &image);
    FruitFeatures extractFeatures(const cv::Mat &segmentedImage, const cv::Rect &boundingBox);
    FruitResult classifyFruit(const FruitFeatures &features);
    
    // 特征提取
    std::vector<float> extractColorFeatures(const cv::Mat &hsvImage, const cv::Rect &roi);
    std::vector<float> extractShapeFeatures(const std::vector<cv::Point> &contour);
    std::vector<float> extractTextureFeatures(const cv::Mat &grayImage, const cv::Rect &roi);
    
    // 分类器
    void trainClassifier();
    double calculateSimilarity(const FruitFeatures &features1, const FruitFeatures &features2);
    
private:
    cv::Mat originalImage;
    cv::Mat processedImage;
    std::map<std::string, FruitFeatures> fruitDatabase;
    
    // 参数
    cv::Scalar colorLowerBound;
    cv::Scalar colorUpperBound;
    int minFruitArea;
    int maxFruitArea;
    
    // 水果数据库
    void initializeFruitDatabase();
};

#endif // FRUITRECOGNIZER_H
cpp 复制代码
// fruitrecognizer.cpp
#include "fruitrecognizer.h"
#include <cmath>
#include <algorithm>

FruitRecognizer::FruitRecognizer() {
    // 初始化默认参数
    colorLowerBound = cv::Scalar(0, 50, 50);
    colorUpperBound = cv::Scalar(180, 255, 255);
    minFruitArea = 1000;
    maxFruitArea = 50000;
    
    initializeFruitDatabase();
}

FruitRecognizer::~FruitRecognizer() {}

void FruitRecognizer::setOriginalImage(const cv::Mat &image) {
    originalImage = image.clone();
}

cv::Mat FruitRecognizer::preprocessImage(const cv::Mat &image) {
    cv::Mat processed;
    
    // 高斯滤波去噪
    cv::GaussianBlur(image, processed, cv::Size(5, 5), 0);
    
    // 转换到HSV色彩空间
    cv::cvtColor(processed, processed, cv::COLOR_BGR2HSV);
    
    return processed;
}

cv::Mat FruitRecognizer::segmentFruit(const cv::Mat &image) {
    cv::Mat segmented;
    
    // 颜色阈值分割
    cv::inRange(image, colorLowerBound, colorUpperBound, segmented);
    
    // 形态学操作
    cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(5, 5));
    cv::morphologyEx(segmented, segmented, cv::MORPH_CLOSE, kernel);
    cv::morphologyEx(segmented, segmented, cv::MORPH_OPEN, kernel);
    
    return segmented;
}

FruitFeatures FruitRecognizer::extractFeatures(const cv::Mat &segmentedImage, const cv::Rect &boundingBox) {
    FruitFeatures features;
    
    // 提取轮廓
    std::vector<std::vector<cv::Point>> contours;
    cv::findContours(segmentedImage, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
    
    if (contours.empty()) {
        return features;
    }
    
    // 找到最大轮廓
    auto maxContourIt = std::max_element(contours.begin(), contours.end(),
                                       const std::vector<cv::Point>& a, const std::vector<cv::Point>& b {
                                           return cv::contourArea(a) < cv::contourArea(b);
                                       });
    
    std::vector<cv::Point> maxContour = *maxContourIt;
    
    // 检查面积
    double area = cv::contourArea(maxContour);
    if (area < minFruitArea || area > maxFruitArea) {
        return features;
    }
    
    features.boundingBox = cv::boundingRect(maxContour);
    
    // 提取各种特征
    features.colorFeatures = extractColorFeatures(originalImage, features.boundingBox);
    features.shapeFeatures = extractShapeFeatures(maxContour);
    features.textureFeatures = extractTextureFeatures(originalImage, features.boundingBox);
    
    return features;
}

std::vector<float> FruitRecognizer::extractColorFeatures(const cv::Mat &hsvImage, const cv::Rect &roi) {
    std::vector<float> features;
    
    // 提取ROI区域的HSV直方图
    cv::Mat roiImage = hsvImage(roi);
    std::vector<cv::Mat> hsvChannels;
    cv::split(roiImage, hsvChannels);
    
    // 计算H通道直方图
    cv::Mat histH;
    int histSize[] = {16};
    float hranges[] = {0, 180};
    const float* ranges[] = {hranges};
    cv::calcHist(&hsvChannels[0], 1, 0, cv::Mat(), histH, 1, histSize, ranges);
    
    // 归一化
    cv::normalize(histH, histH, 0, 1, cv::NORM_L1);
    
    // 转换为vector
    features.assign((float*)histH.datastart, (float*)histH.dataend);
    
    return features;
}

std::vector<float> FruitRecognizer::extractShapeFeatures(const std::vector<cv::Point> &contour) {
    std::vector<float> features;
    
    if (contour.size() < 5) return features;
    
    // 计算轮廓面积
    double area = cv::contourArea(contour);
    features.push_back(static_cast<float>(area));
    
    // 计算周长
    double perimeter = cv::arcLength(contour, true);
    features.push_back(static_cast<float>(perimeter));
    
    // 计算圆形度
    double circularity = 4 * CV_PI * area / (perimeter * perimeter);
    features.push_back(static_cast<float>(circularity));
    
    // 计算外接矩形长宽比
    cv::RotatedRect rotatedRect = cv::minAreaRect(contour);
    float aspectRatio = rotatedRect.size.width / rotatedRect.size.height;
    features.push_back(aspectRatio);
    
    // 计算凸包
    std::vector<cv::Point> hull;
    cv::convexHull(contour, hull);
    double hullArea = cv::contourArea(hull);
    double solidity = area / hullArea;
    features.push_back(static_cast<float>(solidity));
    
    return features;
}

FruitResult FruitRecognizer::recognizeFruit() {
    FruitResult result;
    
    if (originalImage.empty()) {
        result.fruitName = "No Image";
        result.confidence = 0.0;
        return result;
    }
    
    // 预处理
    cv::Mat preprocessedImage = preprocessImage(originalImage);
    processedImage = preprocessedImage.clone();
    
    // 分割
    cv::Mat segmentedImage = segmentFruit(preprocessedImage);
    processedImage = segmentedImage.clone();
    
    // 提取特征
    FruitFeatures features = extractFeatures(segmentedImage, cv::Rect(0, 0, originalImage.cols, originalImage.rows));
    
    // 分类
    result = classifyFruit(features);
    
    // 绘制结果
    if (!result.contour.empty()) {
        cv::drawContours(processedImage, std::vector<std::vector<cv::Point>>{result.contour}, -1, cv::Scalar(0, 255, 0), 2);
        cv::rectangle(processedImage, result.boundingBox, cv::Scalar(255, 0, 0), 2);
        
        // 添加标签
        cv::putText(processedImage, result.fruitName, 
                   cv::Point(result.boundingBox.x, result.boundingBox.y - 10),
                   cv::FONT_HERSHEY_SIMPLEX, 0.7, cv::Scalar(255, 255, 255), 2);
    }
    
    return result;
}

void FruitRecognizer::initializeFruitDatabase() {
    // 苹果特征
    FruitFeatures appleFeatures;
    appleFeatures.shapeFeatures = {5000, 300, 0.8, 1.2, 0.9}; // 示例特征
    appleFeatures.colorFeatures = {0.1, 0.2, 0.3, 0.4}; // 示例颜色特征
    fruitDatabase["Apple"] = appleFeatures;
    
    // 香蕉特征
    FruitFeatures bananaFeatures;
    bananaFeatures.shapeFeatures = {3000, 400, 0.6, 3.5, 0.7}; // 细长形状
    bananaFeatures.colorFeatures = {0.2, 0.3, 0.4, 0.5}; // 黄色特征
    fruitDatabase["Banana"] = bananaFeatures;
    
    // 橙子特征
    FruitFeatures orangeFeatures;
    orangeFeatures.shapeFeatures = {4000, 250, 0.9, 1.0, 0.95}; // 圆形
    orangeFeatures.colorFeatures = {0.3, 0.4, 0.5, 0.6}; // 橙色特征
    fruitDatabase["Orange"] = orangeFeatures;
}

FruitResult FruitRecognizer::classifyFruit(const FruitFeatures &features) {
    FruitResult result;
    result.confidence = 0.0;
    result.fruitName = "Unknown";
    
    // 简单的最近邻分类
    for (const auto& fruit : fruitDatabase) {
        double similarity = calculateSimilarity(features, fruit.second);
        if (similarity > result.confidence) {
            result.confidence = similarity;
            result.fruitName = fruit.first;
        }
    }
    
    // 设置特征描述
    if (!features.shapeFeatures.empty()) {
        result.shapeFeature = "Area: " + std::to_string(features.shapeFeatures[0]);
    }
    
    if (!features.colorFeatures.empty()) {
        result.colorFeature = "Color Histogram Size: " + std::to_string(features.colorFeatures.size());
    }
    
    return result;
}

double FruitRecognizer::calculateSimilarity(const FruitFeatures &features1, const FruitFeatures &features2) {
    if (features1.shapeFeatures.empty() || features2.shapeFeatures.empty()) {
        return 0.0;
    }
    
    double distance = 0.0;
    int minSize = std::min(features1.shapeFeatures.size(), features2.shapeFeatures.size());
    
    for (int i = 0; i < minSize; ++i) {
        double diff = features1.shapeFeatures[i] - features2.shapeFeatures[i];
        distance += diff * diff;
    }
    
    return 1.0 / (1.0 + sqrt(distance));
}

3.3 图像处理器 (imageprocessor.h/cpp)

cpp 复制代码
// imageprocessor.h
#ifndef IMAGEPROCESSOR_H
#define IMAGEPROCESSOR_H

#include <opencv2/opencv.hpp>
#include <vector>

class ImageProcessor {
public:
    ImageProcessor();
    ~ImageProcessor();
    
    // 图像增强
    cv::Mat enhanceContrast(const cv::Mat &image);
    cv::Mat reduceNoise(const cv::Mat &image);
    cv::Mat correctIllumination(const cv::Mat &image);
    
    // 边缘检测
    cv::Mat detectEdges(const cv::Mat &image, int method = 0); // 0:Canny, 1:Sobel, 2:Laplacian
    std::vector<cv::Vec4i> detectLines(const cv::Mat &edgeImage);
    
    // 颜色空间转换
    cv::Mat convertToHSV(const cv::Mat &image);
    cv::Mat convertToLab(const cv::Mat &image);
    cv::Mat extractColorChannel(const cv::Mat &hsvImage, int channel);
    
    // 形态学操作
    cv::Mat morphologicalOperation(const cv::Mat &image, int operation, int kernelSize = 5);
    
private:
    cv::Mat createKernel(int size, int shape = 0); // 0:椭圆, 1:矩形, 2:十字
};

#endif // IMAGEPROCESSOR_H

3.4 Qt界面设计文件 (mainwindow.ui)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>1200</width>
    <height>800</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>智能水果识别分类系统</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <!-- 左侧图像显示区域 -->
    <widget class="QLabel" name="label_originalImage">
     <property name="minimumSize">
      <size>
       <width>400</width>
       <height>300</height>
      </size>
     </property>
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
     <property name="text">
      <string>原始图像</string>
     </property>
    </widget>
    
    <widget class="QLabel" name="label_processedImage">
     <property name="minimumSize">
      <size>
       <width>400</width>
       <height>300</height>
      </size>
     </property>
     <property name="frameShape">
      <enum>QFrame::Box</enum>
     </property>
     <property name="alignment">
      <set>Qt::AlignCenter</set>
     </property>
     <property name="text">
      <string>处理结果</string>
     </property>
    </widget>
    
    <!-- 右侧控制面板 -->
    <widget class="QGroupBox" name="groupBox_control">
     <property name="title">
      <string>控制面板</string>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout">
      <widget class="QPushButton" name="pushButton_loadImage">
       <property name="text">
        <string>加载图片</string>
       </property>
      </widget>
      <widget class="QPushButton" name="pushButton_startCamera">
       <property name="text">
        <string>启动摄像头</string>
       </property>
      </widget>
      <widget class="QPushButton" name="pushButton_stopCamera">
       <property name="text">
        <string>停止摄像头</string>
       </property>
      </widget>
      <widget class="QPushButton" name="pushButton_recognize">
       <property name="text">
        <string>开始识别</string>
       </property>
      </widget>
      <widget class="QPushButton" name="pushButton_saveResult">
       <property name="text">
        <string>保存结果</string>
       </property>
      </widget>
      <spacer name="verticalSpacer">
       <property name="orientation">
        <enum>Qt::Vertical</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>20</width>
         <height>40</height>
        </size>
       </property>
      </spacer>
     </layout>
    </widget>
    
    <!-- 结果显示区域 -->
    <widget class="QGroupBox" name="groupBox_results">
     <property name="title">
      <string>识别结果</string>
     </property>
     <layout class="QVBoxLayout" name="verticalLayout_2">
      <widget class="QTextEdit" name="textEdit_results">
       <property name="html">
        <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
&lt;/body&gt;&lt;/html&gt;</string>
       </property>
      </widget>
     </layout>
    </widget>
   </layout>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

参考代码 对不同种类水果进行识别分类 www.youwenfan.com/contentcsu/60514.html

四、项目文件结构

复制代码
FruitRecognitionSystem/
├── CMakeLists.txt
├── README.md
├── include/
│   ├── mainwindow.h
│   ├── fruitrecognizer.h
│   ├── imageprocessor.h
│   ├── featureextractor.h
│   └── classifier.h
├── src/
│   ├── main.cpp
│   ├── mainwindow.cpp
│   ├── fruitrecognizer.cpp
│   ├── imageprocessor.cpp
│   ├── featureextractor.cpp
│   └── classifier.cpp
├── ui/
│   └── mainwindow.ui
├── resources/
│   ├── images/           # 测试图片
│   └── models/           # 训练好的模型文件
└── docs/
    └── user_manual.md

五、功能扩展建议

5.1 算法优化

  1. 深度学习集成:添加 TensorFlow Lite 或 ONNX Runtime 支持
  2. 多尺度检测:实现多尺度滑动窗口检测
  3. 实时追踪:添加 KCF 或 CSRT 追踪算法

5.2 功能增强

  1. 批量处理:支持文件夹批量识别
  2. 数据标注:集成标注工具,方便训练数据准备
  3. 结果导出:支持 CSV、JSON 格式导出

5.3 用户体验

  1. 参数调节:添加实时参数调节滑块
  2. 历史记录:保存识别历史
  3. 多语言支持:中英文界面切换

六、编译和运行

bash 复制代码
# 1. 克隆项目
git clone https://github.com/yourusername/FruitRecognitionSystem.git
cd FruitRecognitionSystem

# 2. 创建构建目录
mkdir build
cd build

# 3. 配置和编译
cmake ..
make -j4

# 4. 运行程序
./FruitRecognitionSystem
相关推荐
代钦塔拉1 小时前
Qt 按钮 Lambda 信号槽重复绑定、多次触发 BUG 深度剖析与终极解决方案
c++·qt·bug
没想好取什么名2 小时前
解决vscode打开qt creator项目头文件报错的现象
ide·vscode·qt
用户8055336980319 小时前
现代Qt开发教程(新手篇)1.14——日志
c++·qt
江公望19 小时前
Qt QSharedPointer用法,10分钟讲清楚
开发语言·qt
Shadow(⊙o⊙)21 小时前
初识Qt+经典方式实现hello world!的交互
开发语言·c++·后端·qt·学习
weixin_413063211 天前
c++ opencv 复现 halcon算子 derivate_gauss
opencv·计算机视觉·derivate_gauss
道剑剑非道1 天前
FFmpeg + Qt 实现摄像头采集与 MP3 背景音乐 RTSP 推流
开发语言·qt·ffmpeg
努力努力再努力wz1 天前
【Qt入门系列】第一个 Qt Widgets 程序:项目创建、UI 文件、Hello World、对象树与 qDebug 日志
java·c语言·开发语言·数据结构·c++·qt·ui
Hua-Jay1 天前
OpenCV联合C++/Qt 学习笔记(十五)----形态学操作及应用
c++·笔记·qt·opencv·学习·计算机视觉