一、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ 水果智能识别分类系统 │
├─────────────────────────────────────────────────────────────┤
│ 用户界面层 │ 业务逻辑层 │ 图像处理层 │ 算法核心层 │
│ │ │ │ │
│ • 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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
p, li { white-space: pre-wrap; }
</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p>
</body></html></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 算法优化
- 深度学习集成:添加 TensorFlow Lite 或 ONNX Runtime 支持
- 多尺度检测:实现多尺度滑动窗口检测
- 实时追踪:添加 KCF 或 CSRT 追踪算法
5.2 功能增强
- 批量处理:支持文件夹批量识别
- 数据标注:集成标注工具,方便训练数据准备
- 结果导出:支持 CSV、JSON 格式导出
5.3 用户体验
- 参数调节:添加实时参数调节滑块
- 历史记录:保存识别历史
- 多语言支持:中英文界面切换
六、编译和运行
bash
# 1. 克隆项目
git clone https://github.com/yourusername/FruitRecognitionSystem.git
cd FruitRecognitionSystem
# 2. 创建构建目录
mkdir build
cd build
# 3. 配置和编译
cmake ..
make -j4
# 4. 运行程序
./FruitRecognitionSystem