-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwidget.h
More file actions
235 lines (196 loc) · 8.38 KB
/
widget.h
File metadata and controls
235 lines (196 loc) · 8.38 KB
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
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
#pragma once
#include <QWidget>
#include <QPushButton>
#include <QCheckBox>
#include <QLabel>
#include <QComboBox>
#include <QProgressBar>
#include <QCamera>
#include <QMediaDevices>
#include <QVideoSink>
#include <QMediaCaptureSession>
#include <QVideoFrame>
#include <QPainterPath>
#include <QPointF>
#include <QImage>
#include <QTimer>
#include <QElapsedTimer>
#include <opencv2/opencv.hpp>
#include <vector>
#include <deque>
#include <array>
#include <optional>
class FrameProcessor;
class QThread;
/* ─────────────────────────────────────────────────────────────────────────
Physical A4 constants (ISO 216)
───────────────────────────────────────────────────────────────────────── */
static constexpr double A4_WIDTH_MM = 210.0;
static constexpr double A4_HEIGHT_MM = 297.0;
/* Internal warp canvas → 10 px / mm → exact integer arithmetic */
static constexpr int WARP_W = 2100;
static constexpr int WARP_H = 2970;
static constexpr double PX_PER_MM = WARP_W / A4_WIDTH_MM; // 10.0
/* Temporal smoothing window */
static constexpr int SMOOTH_FRAMES = 7;
/* Checkerboard used for lens calibration
Change to match your printed pattern (inner corners, not squares) */
static constexpr int CB_COLS = 9; // inner corners per row
static constexpr int CB_ROWS = 6; // inner corners per column
static constexpr float CB_SQUARE_MM = 25.0f; // physical square size in mm
/* Minimum calibration frames before we accept the result */
static constexpr int CALIB_MIN_FRAMES = 15;
/* ─────────────────────────────────────────────────────────────────────────
Data structures
───────────────────────────────────────────────────────────────────────── */
struct ObjectMeasure {
double widthMM = 0;
double heightMM = 0;
double areaMM2 = 0;
double perimMM = 0;
double angleDeg = 0;
QPointF centerMM;
QString shapeLabel;
};
struct DetectedObject {
QPainterPath pathPx; // warp-canvas pixel coords
QPainterPath pathMM; // millimetre coords (for export)
ObjectMeasure measure;
};
/* ─────────────────────────────────────────────────────────────────────────
Widget
───────────────────────────────────────────────────────────────────────── */
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override = default;
signals: // ✅ HERE (inside class, not outside)
void sendFrameToProcessor(QImage frame);
protected:
void paintEvent(QPaintEvent *) override;
void resizeEvent(QResizeEvent *) override;
private slots:
/* camera */
void startCamera();
void selectCamera(int index);
/* main workflow */
void captureImage();
void runDetection();
void resetAll();
void togglePathOnly();
void exportJson();
/* calibration */
void startCalibration();
void collectCalibFrame();
void finishCalibration();
void clearCalibration();
/* internal */
void onFrameChanged(const QVideoFrame &frame);
void onAutoDetectTimer();
void onA4Processed(bool locked, std::vector<cv::Point2f> quad);
private:
FrameProcessor *processor = nullptr;
QThread *workerThread = nullptr;
/* ── UI ── */
void buildUI();
void applyTheme();
void updateUI();
void setStatus(const QString &msg, const QString &color = "#6e6460");
/* ── Camera ── */
void buildCameraForIndex(int index);
/* ── Calibration ── */
bool loadCalibration();
bool saveCalibration();
cv::Mat undistortFrame(const cv::Mat &src) const;
bool isCalibrated() const { return !camMatrix.empty() && !distCoeffs.empty(); }
void drawCalibOverlay(QPainter &p, const QRect &vp);
/* ── A4 detection ── */
bool detectAndWarpA4(const QImage &src);
bool tryHsvMethod (const cv::Mat &bgr, cv::Mat &warped);
bool tryCannyMethod (const cv::Mat &bgr, cv::Mat &warped);
bool tryAdaptiveMethod(const cv::Mat &bgr, cv::Mat &warped);
bool warpFromQuad (const cv::Mat &src,
std::vector<cv::Point2f> quad,
cv::Mat &warped);
std::vector<cv::Point2f> orderQuad(std::vector<cv::Point2f> pts);
/* ── Object detection ── */
void detectObjects(const QImage &warpedImg);
QString classifyShape(const std::vector<cv::Point> &approx,
double circularity);
void pushAndSmooth();
/* ── Drawing ── */
void drawLiveView (QPainter &p, const QRect &vp);
void drawDetectedView(QPainter &p, const QRect &vp);
void drawPathOnlyView(QPainter &p, const QRect &vp);
void drawCalibView (QPainter &p, const QRect &vp);
/* ── Export ── */
void exportPathsToJson();
/* ── State machine ── */
enum AppState {
LiveView, CapturedView, DetectedView, PathOnlyView,
Calibrating // checkerboard collection mode
};
AppState currentState = LiveView;
/* ── Camera ── */
QCamera *camera = nullptr;
QVideoSink *videoSink = nullptr;
QMediaCaptureSession *captureSession = nullptr;
QList<QCameraDevice> availCameras;
/* ── Images ── */
QImage liveFrame;
QImage capturedFrame;
QImage warpedA4;
/* ── Lens calibration data ── */
cv::Mat camMatrix; // 3×3 intrinsic matrix
cv::Mat distCoeffs; // distortion coefficients (k1,k2,p1,p2,k3)
cv::Mat optimalMatrix; // getOptimalNewCameraMatrix result
cv::Rect validROI; // valid pixel region after undistort
double calibRmsError = 0.0; // reprojection error (pixels) – lower = better
/* Collected calibration samples */
std::vector<std::vector<cv::Point2f>> calibImagePts; // detected corners
std::vector<std::vector<cv::Point3f>> calibObjPts; // 3-D world pts
cv::Size calibImageSize;
QImage calibPreviewFrame; // last frame with corners drawn on it
/* ── Detection results ── */
std::vector<DetectedObject> objects;
std::deque<std::vector<ObjectMeasure>> history;
/* ── A4 lock state ── */
std::vector<cv::Point2f> lastQuad;
int quadLockCount = 0;
bool a4Locked = false;
double pixPerMM = PX_PER_MM;
/* ── UI widgets ── */
QPushButton *btnStart = nullptr;
QPushButton *btnCapture = nullptr;
QPushButton *btnDetect = nullptr;
QPushButton *btnReset = nullptr;
QPushButton *btnPathOnly = nullptr;
QPushButton *btnExport = nullptr;
QPushButton *btnCalib = nullptr; // "Calibrate Lens"
QPushButton *btnCollect = nullptr; // "Collect Frame" (calib mode)
QPushButton *btnFinishCalib = nullptr; // "Finish & Apply" (calib mode)
QPushButton *btnClearCalib = nullptr; // "Clear Calibration"
QComboBox *cameraCombo = nullptr;
QCheckBox *chkOverlay = nullptr;
QCheckBox *chkAutoDetect = nullptr;
QLabel *lblStatus = nullptr;
QLabel *lblA4Lock = nullptr;
QLabel *lblObjects = nullptr;
QLabel *lblCalibStatus = nullptr; // shows "Calibrated ✓" or "Not calibrated"
/* ── Timers ── */
QTimer *autoTimer = nullptr;
int selectedObject = -1;
};
class FrameProcessor : public QObject
{
Q_OBJECT
public:
bool a4Locked = false;
int lockCount = 0;
public slots:
void processFrame(QImage frame);
signals:
void resultReady(bool locked, std::vector<cv::Point2f> quad);
};