代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <opencv2/opencv.hpp>
#include <iostream>
#include <filesystem>

int main()
{
// 从ONNX文件中读取神经网络模型
cv::dnn::Net net = cv::dnn::readNetFromONNX("../mnist.onnx");

// 读取图像
cv::Mat image = cv::imread("../images/numbers.jpg");
cv::Mat original = image.clone();

// 将图像进行预处理
cv::cvtColor(image, image, cv::COLOR_BGR2GRAY);
cv::threshold(image, image, 128, 255, cv::THRESH_BINARY_INV); // 二值化(黑底白字)
cv::GaussianBlur(image, image, cv::Size(7, 7), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));
cv::dilate(image, image, kernel);

// 查找图像中的轮廓
std::vector<std::vector<cv::Point>> contours;
cv::findContours(image, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
std::vector<std::vector<cv::Point>> digit_contours;

// 遍历所有轮廓,筛选出面积足够大的轮廓
for (const auto &contour : contours)
{
if (cv::contourArea(contour) > 100)
{
digit_contours.push_back(contour);
}
}

for (const auto &contour : digit_contours)
{
// 获取数字轮廓的边界框
cv::Rect rect = cv::boundingRect(contour);

// 在原始图像上绘制边界框
cv::rectangle(original, rect, cv::Scalar(0, 255, 0), 2);

// 提取数字区域
cv::Mat digit = image(rect);

// 计算填充值
int padding = std::max(digit.rows, digit.cols) / 6;
int padding_top_bottom = (digit.rows >= digit.cols) ? padding : (std::max(digit.rows, digit.cols) - std::min(digit.rows, digit.cols)) / 2 + padding;
int padding_left_right = (digit.rows >= digit.cols) ? (std::max(digit.rows, digit.cols) - std::min(digit.rows, digit.cols)) / 2 + padding : padding;

// 对数字区域进行边界填充
cv::copyMakeBorder(digit,
digit,
padding_top_bottom,
padding_top_bottom,
padding_left_right,
padding_left_right,
cv::BORDER_CONSTANT,
cv::Scalar(0, 0, 0));

// 将数字区域转换为神经网络输入的blob
cv::Mat blob = cv::dnn::blobFromImage(digit,
1.0 / 255.0,
cv::Size(28, 28),
cv::Scalar(0.1307 * 255),
false, false);

// 设置神经网络的输入
net.setInput(blob);

// 进行前向推理
cv::Mat prob = net.forward();

// 对概率进行指数化和归一化
cv::exp(prob, prob);
cv::divide(prob, cv::sum(prob)[0], prob);

// 获取最大概率对应的类别和置信度
cv::Point classIdPoint;
double confidence;
cv::minMaxLoc(prob.reshape(1, 1), nullptr, &confidence, nullptr, &classIdPoint);

// 输出类别和置信度
std::cout << std::format("{}, confidence: {}\n", classIdPoint.x, confidence);

// 在原始图像上绘制预测结果
cv::putText(original, std::to_string(classIdPoint.x), rect.tl(), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(0, 0, 255), 2);

cv::imshow("digit", digit);
cv::waitKey(0);
}

cv::imshow("image", original);
cv::waitKey(0);
cv::destroyAllWindows();

return 0;
}