diff --git a/camCalib/camCalib.vcxproj b/camCalib/camCalib.vcxproj
index 913e852..4e65280 100644
--- a/camCalib/camCalib.vcxproj
+++ b/camCalib/camCalib.vcxproj
@@ -147,11 +147,13 @@
+
+
diff --git a/camCalib/camCalib.vcxproj.filters b/camCalib/camCalib.vcxproj.filters
index 6a84349..8908570 100644
--- a/camCalib/camCalib.vcxproj.filters
+++ b/camCalib/camCalib.vcxproj.filters
@@ -27,6 +27,9 @@
源文件
+
+ 源文件
+
@@ -38,5 +41,8 @@
头文件
+
+ 头文件
+
\ No newline at end of file
diff --git a/camCalib/detectCB.onnx b/camCalib/detectCB.onnx
new file mode 100644
index 0000000..3f04036
Binary files /dev/null and b/camCalib/detectCB.onnx differ
diff --git a/camCalib/onnxDetector.cpp b/camCalib/onnxDetector.cpp
new file mode 100644
index 0000000..9903c49
--- /dev/null
+++ b/camCalib/onnxDetector.cpp
@@ -0,0 +1,302 @@
+#include "onnxDetector.h"
+#include
+
+using namespace std;
+using namespace cv;
+using namespace cv::dnn;
+
+class OnnxDetectorImpl :
+ public OnnxDetector
+{
+public:
+ OnnxDetectorImpl() :
+ classNum_(1), imgStride_(32), confidence_(0.25f),
+ scoreThreshold_(0.45f), nmsThreshold_(0.3f){};
+ ~OnnxDetectorImpl() {};
+
+public:
+ virtual int loadOnnxModel(const char* filename, cv::Size2f inferSize) override;
+ virtual std::vector detect(cv::Mat image) override;
+
+private:
+ vector preProcess(Mat& input_image);
+ vector postProcess(Mat& input_image, vector& outputs);
+
+private:
+ int classNum_;
+ int imgStride_;
+ float confidence_;
+ float scoreThreshold_;
+ float nmsThreshold_;
+ cv::Size2f size_;
+ cv::dnn::Net network_;
+};
+
+void letterbox(const cv::Mat& image, cv::Mat& outImage,
+ const cv::Size& newShape = cv::Size(640, 640),
+ const cv::Scalar& color = cv::Scalar(114, 114, 114),
+ bool auto_ = true,
+ bool scaleFill = false,
+ bool scaleUp = true,
+ int stride = 32)
+{
+ cv::Size shape = image.size();
+ float r = std::min((float)newShape.height / (float)shape.height,
+ (float)newShape.width / (float)shape.width);
+ if (!scaleUp)
+ r = std::min(r, 1.0f);
+
+ float ratio[2]{ r, r };
+ int newUnpad[2]{ (int)std::round((float)shape.width * r),
+ (int)std::round((float)shape.height * r) };
+
+ auto dw = (float)(newShape.width - newUnpad[0]);
+ auto dh = (float)(newShape.height - newUnpad[1]);
+
+ if (auto_)
+ {
+ dw = (float)((int)dw % stride);
+ dh = (float)((int)dh % stride);
+ }
+ else if (scaleFill)
+ {
+ dw = 0.0f;
+ dh = 0.0f;
+ newUnpad[0] = newShape.width;
+ newUnpad[1] = newShape.height;
+ ratio[0] = (float)newShape.width / (float)shape.width;
+ ratio[1] = (float)newShape.height / (float)shape.height;
+ }
+
+ dw /= 2.0f;
+ dh /= 2.0f;
+
+ if (shape.width != newUnpad[0] && shape.height != newUnpad[1])
+ {
+ cv::resize(image, outImage, cv::Size(newUnpad[0], newUnpad[1]));
+ }
+
+ int top = int(std::round(dh - 0.1f));
+ int bottom = int(std::round(dh + 0.1f));
+ int left = int(std::round(dw - 0.1f));
+ int right = int(std::round(dw + 0.1f));
+ cv::copyMakeBorder(outImage, outImage, top, bottom, left, right, cv::BORDER_CONSTANT, color);
+}
+
+// Ԥ
+vector OnnxDetectorImpl::preProcess(Mat& input_image)
+{
+ Mat blob;
+ blobFromImage(input_image, blob, 1. / 255., Size(size_), Scalar(), false, false);
+
+ network_.setInput(blob);
+
+ vector names = network_.getUnconnectedOutLayersNames();
+
+ vector outputs;
+ network_.forward(outputs, names);
+
+ return outputs;
+}
+
+//
+#if 1
+vector OnnxDetectorImpl::postProcess(Mat& input_image, vector& outputs)
+{
+ vector class_ids;
+ vector confidences;
+ vector boxes;
+
+ float x_factor = input_image.cols / size_.width;
+ float y_factor = input_image.rows / size_.height;
+
+ float* data = (float*)outputs[0].data;
+
+ const int dimensions = 5 + classNum_;
+ //const int rows = 25200;
+ const int rows =(1 + 4 + 16) * (size_.width / imgStride_) * (size_.height / imgStride_);
+ for (int i = 0; i < rows; ++i)
+ {
+#if 1
+ float confidence = data[rows * 4 + i];
+ if (confidence > confidence_)
+ {
+ int centerX = (int)(data[rows * 0 + i] * x_factor);
+ int centerY = (int)(data[rows * 1 + i] * y_factor);
+ int width = (int)(data[rows * 2 + i] * x_factor);
+ int height = (int)(data[rows * 3 + i] * y_factor);
+ int left = centerX - width / 2;
+ int top = centerY - height / 2;
+
+ boxes.emplace_back(left, top, width, height);
+ confidences.emplace_back(confidence);
+ class_ids.emplace_back(0);
+ }
+#else
+ float confidence = data[4];
+ if (confidence >= confidence_)
+ {
+ float* classes_scores = data + 5;
+ Mat scores(1, classNum_, CV_32FC1, classes_scores);
+ Point class_id;
+ double max_class_score;
+ minMaxLoc(scores, 0, &max_class_score, 0, &class_id);
+ if (max_class_score > scoreThreshold_)
+ {
+ confidences.push_back(confidence);
+ class_ids.push_back(class_id.x);
+
+ float cx = data[0];
+ float cy = data[1];
+ float w = data[2];
+ float h = data[3];
+ int left = int((cx - 0.5 * w) * x_factor);
+ int top = int((cy - 0.5 * h) * y_factor);
+ int width = int(w * x_factor);
+ int height = int(h * y_factor);
+ boxes.push_back(Rect(left, top, width, height));
+ }
+ }
+#endif
+ //data += dimensions;
+ }
+
+ vector res2d;
+ vector indices;
+ NMSBoxes(boxes, confidences, scoreThreshold_, nmsThreshold_, indices);
+ for (int i = 0; i < indices.size(); i++)
+ {
+ int idx = indices[i];
+ Rect box = boxes[idx];
+
+ int left = box.x;
+ int top = box.y;
+ int width = box.width;
+ int height = box.height;
+ //rectangle(output_image, Point(left, top), Point(left + width, top + height), BLUE, 3 * THICKNESS);
+
+ //string label = format("%.2f", confidences[idx]);
+ //label = class_name[class_ids[idx]] + ":" + label;
+ //draw_label(output_image, label, left, top);
+
+ Detection2d d2d;
+ d2d.bbox = box;
+ d2d.classIdx = idx;
+ d2d.confidence = confidences[idx];
+
+ res2d.push_back(d2d);
+ }
+ return res2d;
+}
+#else
+inline float sigmoid(float x)
+{
+ return 1.f / (1.f + exp(-x));
+}
+
+vector OnnxDetectorImpl::postProcess(Mat& cv_src, vector& outs)
+{
+ std::vector classIds;
+ std::vector confidences;
+ std::vector boxes;
+
+ int strides[] = { 8, 16, 32 };
+ std::vector > anchors = {
+ { 10,13, 16,30, 33,23 },
+ { 30,61, 62,45, 59,119 },
+ { 116,90, 156,198, 373,326 }
+ };
+
+ for (size_t k = 0; k < outs.size(); k++) {
+
+ float* data = outs[k].ptr();
+ int stride = strides[k];
+ int num_classes = outs[k].size[4] - 5;
+ for (int i = 0; i < outs[k].size[2]; i++) {
+ for (int j = 0; j < outs[k].size[3]; j++) {
+ for (int a = 0; a < outs[k].size[1]; ++a) {
+
+ float* record = data + a * outs[k].size[2] * outs[k].size[3] * outs[k].size[4] +
+ i * outs[k].size[3] * outs[k].size[4] + j * outs[k].size[4];
+ float* cls_ptr = record + 5;
+ for (int cls = 0; cls < num_classes; cls++) {
+
+ float score = sigmoid(cls_ptr[cls]) * sigmoid(record[4]);
+ if (score > scoreThreshold_) {
+
+ float cx = (sigmoid(record[0]) * 2.f - 0.5f + (float)j) * (float)stride;
+ float cy = (sigmoid(record[1]) * 2.f - 0.5f + (float)i) * (float)stride;
+ float w = pow(sigmoid(record[2]) * 2.f, 2) * anchors[k][2 * a];
+ float h = pow(sigmoid(record[3]) * 2.f, 2) * anchors[k][2 * a + 1];
+ float x1 = std::max(0, std::min(cv_src.cols, int((cx - w / 2.f) * (float)cv_src.cols / (float)size_.width)));
+ float y1 = std::max(0, std::min(cv_src.rows, int((cy - h / 2.f) * (float)cv_src.rows / (float)size_.width)));
+ float x2 = std::max(0, std::min(cv_src.cols, int((cx + w / 2.f) * (float)cv_src.cols / (float)size_.width)));
+ float y2 = std::max(0, std::min(cv_src.rows, int((cy + h / 2.f) * (float)cv_src.rows / (float)size_.width)));
+ classIds.push_back(cls);
+ confidences.push_back(score);
+ boxes.push_back(cv::Rect(cv::Point(x1, y1), cv::Point(x2, y2)));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ vector res2d;
+ std::vector indices;
+ cv::dnn::NMSBoxes(boxes, confidences, scoreThreshold_, nmsThreshold_, indices);
+ for (size_t i = 0; i < indices.size(); i++){
+
+ int idx = indices[i];
+ cv::Rect box = boxes[idx];
+ //drawPred(classIds[idx], confidences[idx], box.x, box.y,
+ // box.x + box.width, box.y + box.height, cv_src, classes);
+
+ Detection2d d2d;
+ d2d.classIdx = classIds[idx];
+ d2d.bbox = box;
+ d2d.confidence = confidences[idx];
+ res2d.push_back(d2d);
+ }
+
+ return res2d;
+}
+#endif
+
+int OnnxDetectorImpl::loadOnnxModel(const char* filename, cv::Size2f inferSize) {
+
+ std::vector< std::pair > backends = cv::dnn::getAvailableBackends();
+ for (std::pair& det : backends) {
+ std::cout << "Detected Valid Backends: " << det.first << ", " << det.second << std::endl;
+ }
+
+ size_ = inferSize;
+ network_ = cv::dnn::readNet(filename);
+
+ //std::vector< std::pair > backends = cv::dnn::getAvailableBackends();
+ //for (std::pair& det : backends) {
+ // std::cout << "Detected Valid Backends: " << det.first << ", " << det.second << std::endl;
+ //}
+ //network_.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
+ //network_.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA);
+
+ return 0;
+}
+
+std::vector OnnxDetectorImpl::detect(cv::Mat image) {
+
+ std::vector res2d;
+ if (image.empty() || network_.empty())
+ return res2d;
+
+ const int width = std::max(image.rows, image.cols);
+ cv::Mat img = cv::Mat::zeros(image.rows, image.cols, image.type());
+ image.copyTo(img(cv::Rect(0, 0, image.cols, image.rows)));
+
+ vector detections = preProcess(img);
+ return postProcess(img, detections);
+}
+
+OnnxDetector* OnnxDetector::CreateInstance() {
+ return new OnnxDetectorImpl();
+}
\ No newline at end of file
diff --git a/camCalib/onnxDetector.h b/camCalib/onnxDetector.h
new file mode 100644
index 0000000..e36bf4f
--- /dev/null
+++ b/camCalib/onnxDetector.h
@@ -0,0 +1,21 @@
+#pragma once
+
+#include
+
+typedef struct Detection2d {
+ int classIdx;
+ float confidence;
+ cv::Rect bbox;
+}_detection2d;
+
+class OnnxDetector
+{
+public:
+ static OnnxDetector* CreateInstance();
+
+public:
+ virtual int loadOnnxModel(const char* filename, cv::Size2f inferSize) = 0;
+ virtual std::vector detect(cv::Mat image) = 0;
+
+};
+
diff --git a/camCalib/sourceCode/MonoLaserCalibrate.cpp b/camCalib/sourceCode/MonoLaserCalibrate.cpp
index 0827e0d..6545e4f 100644
--- a/camCalib/sourceCode/MonoLaserCalibrate.cpp
+++ b/camCalib/sourceCode/MonoLaserCalibrate.cpp
@@ -7,6 +7,9 @@
#include "MonoLaserCalibrate.h"
#include "../lineDetection_steger.h"
+#include "../onnxDetector.h"
+OnnxDetector* cbDetector = nullptr;
+
/*Breif:角点检测函数*/
void detectCorners(const cv::Mat& img,
const cv::Size& patternSize,
@@ -42,11 +45,116 @@ void detectCirclePoints(const cv::Mat& img,
//需要将圆点外的背景去除,否则复杂的背景会导致检测失败
//使用连通域检测
- cv::Mat grayBin;
+ cv::Mat roiGray;
+ {
+#if 1
+ if (nullptr == cbDetector) {
+ cbDetector = OnnxDetector::CreateInstance();
+ cbDetector->loadOnnxModel("detectCB.onnx", cv::Size(640, 640));
+ }
+ std::vector result2d = cbDetector->detect(img);
+ for (size_t i = 0; i < result2d.size(); i++) {
+ cv::Rect roi = result2d[i].bbox;
+ if (roi.x < 0)
+ roi.x = 0;
+ if (roi.y < 0)
+ roi.y = 0;
+ if ((roi.x + roi.width) > gray.cols)
+ roi.width = gray.cols - roi.x;
+ if ((roi.y + roi.height) > gray.rows)
+ roi.height = gray.rows - roi.y;
+
+ if (roiGray.empty()) {
+ roiGray = cv::Mat::zeros(gray.rows, gray.cols, CV_8UC1);
+ gray(roi).copyTo(roiGray(roi));
+ }
+ else {
+ gray(roi).copyTo(roiGray(roi));
+ }
+ }
+
+#ifdef _DEBUG
+ cv::Mat result = img.clone();
+ for (size_t i = 0; i < result2d.size(); i++) {
+ cv::rectangle(img, result2d[i].bbox, cv::Scalar(0, 255, 0), 2);
+ }
+ cv::imwrite("_cbResult.png", result);
+#endif
+
+#else
+ cv::Mat grayBin;
+ cv::adaptiveThreshold(gray, grayBin, 255,
+ cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, 9, 2);
+
+ cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, { 3,3 });
+ cv::morphologyEx(grayBin, grayBin, cv::MORPH_OPEN, kernel);
+ cv::morphologyEx(grayBin, grayBin, cv::MORPH_CLOSE, kernel);
+
+ //cv::connectedComponents();
+ std::vector> contours;
+ std::vector hierarchy;
+ cv::findContours(grayBin, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
+
+#ifdef _DEBUG
+ cv::Mat result = grayBin.clone();
+#endif
+
+ cv::Rect roi;
+ const int N = patternSize.area();
+ for (int i = 0; i < contours.size(); ++i) {
+ // 检查当前轮廓是否为父轮廓且子轮廓数量为N
+ int childCount = 0;
+ int childIdx = hierarchy[i][2]; // 第一个子轮廓索引
+ while (childIdx != -1) {
+ childCount++;
+ childIdx = hierarchy[childIdx][0]; // 下一个同级子轮廓
+ }
+
+ if (childCount >= N) { // 子数量匹配
+#ifdef _DEBUG
+ cv::drawContours(result, contours, i, cv::Scalar(0, 255, 0), 2);
+#endif
+
+ cv::Point lt(10000, 10000), rb(0, 0);
+ for (const cv::Point pt : contours[i]) {
+ lt.x = std::min(lt.x, pt.x);
+ rb.x = std::max(rb.x, pt.x);
+ lt.y = std::min(lt.y, pt.y);
+ rb.y = std::max(rb.y, pt.y);
+ }
+ roi = cv::Rect(lt, rb);
+ if (roiGray.empty()) {
+ roiGray = cv::Mat::zeros(gray.rows, gray.cols, CV_8UC1);
+ gray(roi).copyTo(roiGray(roi));
+ }
+ else {
+ gray(roi).copyTo(roiGray(roi));
+ }
+
+ // 标记子轮廓(可选)
+ childIdx = hierarchy[i][2];
+ while (childIdx != -1) {
+#ifdef _DEBUG
+ cv::drawContours(result, contours, childIdx, cv::Scalar(255, 0, 0), 1);
+#endif
+ childIdx = hierarchy[childIdx][0];
+ }
+ }
+ }
+
+#ifdef _DEBUG
+ cv::imwrite("_cbContour.png", result);
+#endif
+#endif
+#ifdef _DEBUG
+ if (false == roiGray.empty())
+ cv::imwrite("_roiGray.png", roiGray);
+#endif
+ }
// 检测圆形网格
- bool found = cv::findCirclesGrid(gray, patternSize, corners, cv::CALIB_CB_SYMMETRIC_GRID);
+ bool found = cv::findCirclesGrid(roiGray.empty() ? gray : roiGray, patternSize, corners, cv::CALIB_CB_SYMMETRIC_GRID);
return;
}