commit 67366dffd177545e1fc6501f01cc4c32f6e27a1b Author: kaiza_hikaru Date: Wed May 28 10:47:46 2025 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0fbc88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vs/ +x64/ +QtWidget.DFE02648/ \ No newline at end of file diff --git a/M_camera.cpp b/M_camera.cpp new file mode 100644 index 0000000..34f80cf --- /dev/null +++ b/M_camera.cpp @@ -0,0 +1,29 @@ +#include "M_camera.h" + +M_camera::M_camera(QObject* parent) +{ + + +} + +M_camera::~M_camera() +{ + +} + +//死循环,一直获取图像 +void M_camera::do_start() { + + system = System::GetInstance();//新建相机系统 + camList = system->GetCameras();//获取相机列表 + const unsigned int numCameras = camList.GetSize();//获取相机数目 + cout << "Number of cameras detected: " << numCameras << endl << endl; + pCam = camList.GetByIndex(0);//相机实例化,由于只有一个相机,因此相机index为0 + pCam->Init();//相机初始化 + pCam->BeginAcquisition();//开始采集图像 + while (1) { + pResultImage = pCam->GetNextImage(); + pResultImage = processor.Convert(pResultImage, PixelFormat_RGB8); + emit img_redy(pResultImage); + } +} \ No newline at end of file diff --git a/M_camera.h b/M_camera.h new file mode 100644 index 0000000..42ee2f5 --- /dev/null +++ b/M_camera.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include +#include +#include + + +using namespace Spinnaker; +using namespace Spinnaker::GenApi; +using namespace Spinnaker::GenICam; +using namespace std; +using namespace cv; + +class M_camera : public QObject { + Q_OBJECT +public: + + M_camera(QObject* parent); + ~M_camera(); + + SystemPtr system; + CameraList camList; + CameraPtr pCam = nullptr; + Mat frame; + ImageProcessor processor; + ImagePtr pResultImage; + +public slots: + void do_start(); + +signals: + + void img_redy(ImagePtr pResultImage); + +private: + + + + +}; + diff --git a/Mycontrol.cpp b/Mycontrol.cpp new file mode 100644 index 0000000..c6941b2 --- /dev/null +++ b/Mycontrol.cpp @@ -0,0 +1,199 @@ +#include "Mycontrol.h" + + +Mycontrol::Mycontrol() +{ +} + +// 获取单例实例(C++11 线程安全) +Mycontrol& Mycontrol::getInstance() { + static Mycontrol instance; + return instance; +} + +void Mycontrol::init() { + volatile int result = 0; + // Init vars + int ioin_count = 0, motor_count = 0, connect_rebuild = 1; + + // Load config file + wchar_t path[] = L"D:\\Program Files\\demo\\glass.csv"; + result = XT_Controler_Extend::Profile_Load(path); + if (result == 1) { + qDebug() << "XTSystem - Init: Profile_Load succeed!"; + } + else { + qDebug() << "XTSystem - Init: Profile_Load failed!"; + return; + } + + // Init controller + //XT_Controler_Extend::Profile_DeInit_Controller(); + //qDebug() << "Do deinit controller"; + result = XT_Controler_Extend::Profile_Init_Controller(1); + qDebug() << "XTSystem - Init: Do init controller"; + if (result == 1) { + qDebug() << "XTSystem - Init: Profile_Init_Controller succeed!"; + + ioin_count = XT_Controler_Extend::Profile_Get_IoIn_Count(); + motor_count = XT_Controler_Extend::Profile_Get_Axis_Count(); + connect_rebuild = 0; + + qDebug() << "XTSystem - Init:ioin_count: " << ioin_count << ", " + << "motor_count: " << motor_count << ", " + << "connect_rebuild: " << connect_rebuild; + } + else { + qDebug() << "XTSystem - Init: Profile_Init_Controller failed!"; + return; + } + + // Init 3 axis motors + VCM_Resource_struct xtMotorsResources[3]; + xtMotorsResources[0].CanID = 1; + xtMotorsResources[0].iAxis = motor_count + 1; + xtMotorsResources[0].Connet_Rebuild = connect_rebuild; + xtMotorsResources[0].IO_ID = ioin_count + 3 * 0; + xtMotorsResources[0].iThread = 2; + xtMotorsResources[0].iThread_Curve = 3; + xtMotorsResources[0].Z_Index_ID = -1; + xtMotorsResources[0].IO_CAN_ID = 4; + xtMotorsResources[0].Extren_IO_Index = 0; + + xtMotorsResources[1].CanID = 2; + xtMotorsResources[1].iAxis = motor_count + 2; + xtMotorsResources[1].Connet_Rebuild = connect_rebuild; + xtMotorsResources[1].IO_ID = ioin_count + 3 * 1; + xtMotorsResources[1].iThread = 5; + xtMotorsResources[1].iThread_Curve = 6; + xtMotorsResources[1].Z_Index_ID = -1; + xtMotorsResources[1].IO_CAN_ID = 4; + xtMotorsResources[1].Extren_IO_Index = 1; + + xtMotorsResources[2].CanID = 3; + xtMotorsResources[2].iAxis = motor_count + 3; + xtMotorsResources[2].Connet_Rebuild = connect_rebuild; + xtMotorsResources[2].IO_ID = ioin_count + 3 * 2; + xtMotorsResources[2].iThread = 8; + xtMotorsResources[2].iThread_Curve = 9; + xtMotorsResources[2].Z_Index_ID = -1; + xtMotorsResources[2].IO_CAN_ID = 4; + xtMotorsResources[2].Extren_IO_Index = 2; + + VCMT_resource_alloc(xtMotorsResources, 3); + + // 初始化音圈控制端 + result = Soft_landing_dll_init(3); + if (result == 1) { + qDebug() << "XTSystem - Init:Soft_landing_dll_init succeed!"; + } + else { + qDebug() << "XTSystem - Init:Soft_landing_dll_init failed!"; + } + + // 获取电机状态 + result = Get_Init_Ready(); + if (result == -1) { + qDebug() << "XTSystem - Init:Motors not exist!"; + } + else if (result == 0) { + qDebug() << "XTSystem - Init:Motors busy!"; + } + else { + qDebug() << "XTSystem - Init:Motors init succeed!"; + } + + // 电机使能 + SetServoOnOff(1, true); + SetServoOnOff(2, true); + SetServoOnOff(3, true); + QThread::msleep(1000); + qDebug() << "XTSystem - Init:Servos enable succeed!"; + + // 配置三轴参数,motor_id =3, 1, 2 对应 z, y, x + SetPosModejerk(3, 500000); + SetPosModeAcc(3, 50); + SetPosModeSpeed(3, 15); + SetGoZeroSpeed(3, 10); + SetGoZeroDistance(3, 20); + SetRunDirect(3, 0, 20000); + + if (Init_Go_Zero(3, 1)) { + qDebug() << "XTsystem - init: Z axis go zero succeed!"; + } + + SetPosModejerk(1, 500000); + SetPosModeAcc(1, 50); + SetPosModeSpeed(1, 15); + SetGoZeroSpeed(1, 10); + SetGoZeroDistance(1, 500); + SetRunDirect(1, 0, 10000); + + if (Init_Go_Zero(1, 1)) { + qDebug() << "XTsystem - init: Y axis go zero succeed!"; + } + + SetPosModejerk(2, 500000); + SetPosModeAcc(2, 5); + SetPosModeSpeed(2, 15); + SetGoZeroSpeed(2, 10); + SetGoZeroDistance(2, 500); + SetRunDirect(2, 1, 10000); + + if (Init_Go_Zero(2, 1)) { + qDebug() << "XTsystem - init: x axis go zero succeed!"; + } + + + qDebug() << QString("XTSystem: init() called"); + +} + +void Mycontrol::moveBy(int motorID, double distance, int block) { + qDebug() << QString("Mycontrol: moveBy(motorID=%1, distance=%2, block=%3)") + .arg(motorID).arg(distance).arg(block); + double nowPosition = 0.0; + GetNowPos(motorID, nowPosition); + SetPosModePos(motorID, nowPosition + distance); + TillPosModePos(motorID, block); +} + +void Mycontrol::moveTo(int motorID, double targetPosition, int block) { + qDebug() << QString("Mycontrol: moveTo(motorID=%1, targetPosition=%2, block=%3)") + .arg(motorID).arg(targetPosition).arg(block); + SetPosModePos(motorID, targetPosition); + TillPosModePos(motorID, block); +} + +void Mycontrol::moveByX(double xDis) { + moveBy(2, xDis); +} + +void Mycontrol::moveByY(double yDis) { + moveBy(1, yDis); +} + +void Mycontrol::moveByZ(double zDis) { + moveBy(3, zDis); +} + + + + +double Mycontrol::getXPos() const { + double xPos; + GetNowPos(2, xPos); + return xPos; +} + +double Mycontrol::getYPos() const { + double yPos; + GetNowPos(1, yPos); + return yPos; +} + +double Mycontrol::getZPos() const { + double zPos; + GetNowPos(3, zPos); + return zPos; +} \ No newline at end of file diff --git a/Mycontrol.h b/Mycontrol.h new file mode 100644 index 0000000..a25698b --- /dev/null +++ b/Mycontrol.h @@ -0,0 +1,40 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + + +class Mycontrol : public QObject { + Q_OBJECT + + + +private: + Mycontrol(); // 私有构造函数 + Mycontrol(const Mycontrol&) = delete; + Mycontrol& operator=(const Mycontrol&) = delete; + +public: + static Mycontrol& getInstance(); // 获取单例实例 + void init(); // 初始化 XT 控制系统 + void moveBy(int motorID, double distance, int block = 0); // 按距离移动,distance 参数单位为 um + void moveTo(int motorID, double targetPosition, int block = 0); // 按坐标移动,targetPosition 参数单位为 mm + + // 分轴移动接口 + void moveByX(double xDis); // 按距离移动 X 轴,左正右负 + void moveByY(double yDis); // 按距离移动 Y 轴,前正后负 + void moveByZ(double zDis); // 按距离移动 Z 轴,下正上负 + double getXPos() const; // 返回 X 轴当前位置(单位:mm) + double getYPos() const; // 返回 Y 轴当前位置(单位:mm) + double getZPos() const; // 返回 Z 轴当前位置(单位:mm) +public slots: + +}; \ No newline at end of file diff --git a/QtWidgetsApplication.cpp b/QtWidgetsApplication.cpp new file mode 100644 index 0000000..fd9b1f5 --- /dev/null +++ b/QtWidgetsApplication.cpp @@ -0,0 +1,419 @@ +#include "QtWidgetsApplication.h" + +QtWidgetsApplication::QtWidgetsApplication(QWidget *parent) + : QMainWindow(parent) +{ + + ui.setupUi(this); + // 获取 控制系统 单例并初始化 + Mycontrol::getInstance().init(); + + //相机需要的显示控件 + m_scene = new QGraphicsScene(this); + ui.imgView->setScene(m_scene); + + //保存路径 + ui.edtSaveDir->setText("D:/"); + //TCT检测显示需要 + tct_pixmapItem = new QGraphicsPixmapItem(tct_pixmap); + tct_scene = new QGraphicsScene(this); + tct_scene->addItem(tct_pixmapItem); + + //TCT模型处理的线程 + my_TCT = new TCT(this); + m_infQthread = new QThread(nullptr); + my_TCT->moveToThread(m_infQthread); + + //TCT模型处理需要的信号与槽 + connect(this, &QtWidgetsApplication::emit_img, my_TCT, &TCT::main_module); + connect(my_TCT, &TCT::em_img, this, &QtWidgetsApplication::input_img); + m_infQthread->start(); + + //定时器定时获取当前位置 + QTimer* timer = new QTimer(this); + timer->setInterval(50); // 设置定时器间隔为 50 毫秒 + timer->setSingleShot(false); // 非单次触发,持续运行 + connect(timer, &QTimer::timeout, [this]() { + Mycontrol& Mycontrol = Mycontrol::getInstance(); + ui.m_xpos->setValue(Mycontrol::getInstance().getXPos()); + ui.m_ypos->setValue(Mycontrol::getInstance().getYPos()); + ui.m_zpos->setValue(Mycontrol::getInstance().getZPos()); + }); + timer->start(); +} + +QtWidgetsApplication::~QtWidgetsApplication() +{ + +} + +//选择相机 +void QtWidgetsApplication::on_pb_camera_clicked() { + + QString currentText = ui.CameraBox->currentText(); // 获取文本 + if (currentText == "haikang camera") + { + // 创建相机线程,和信号与槽 + cam_thread = new QThread(this); + Hacm_worker = new haikang(this); + Hacm_worker->moveToThread(cam_thread); + connect(cam_thread, &QThread::started, Hacm_worker, &haikang::GrabThreadProcess); + //connect(Hacm_worker, &haikang::img_redy, this, &QtWidgetsApplication::do_hai_img_redy); //弃用 + m_hwndDisplay = (HWND)ui.imgView->winId();//弃用 + connect(cam_thread, &QThread::finished, Hacm_worker, &haikang::finish); + connect(Hacm_worker, &haikang::img_to, this, &QtWidgetsApplication::hai_img_redy); + cam_thread->start(); + setSlideSpinboxCheckboxSync(); + } + if (currentText == "flir camera") + { + + qRegisterMetaType("ImagePtr"); + // 创建相机线程,和信号与槽 + cam_thread = new QThread(this); + cam_worker = new M_camera(nullptr); + cam_worker->moveToThread(cam_thread); + connect(cam_thread, &QThread::started, cam_worker, &M_camera::do_start);//线程开始,让相机初始化 + connect(cam_worker, &M_camera::img_redy, this, &QtWidgetsApplication::do_img_redy); + cam_thread->start(); + //让曝光,增益的Slide和Spinbox同步 + setSlideSpinboxCheckboxSync(); + } + +} + +// 将海康相机传回来的图片进行显示 +void QtWidgetsApplication::hai_img_redy(QImage image, void* handle) +{ + hikimage = image; //往tct里面送的时候用 + HK_handle = handle; //相机句柄 + m_scene->clear(); // 清除旧图像 + QPixmap pixmap = QPixmap::fromImage(image); + pixmapItem = m_scene->addPixmap(pixmap); + + ui.imgView->fitInView(pixmapItem, Qt::KeepAspectRatio); + ui.imgView->setScene(m_scene); + ui.imgView->show(); +} + +//将海康相机传回来的图片进行显示 //SB处理办法 +void QtWidgetsApplication::do_hai_img_redy(MV_FRAME_OUT stImageInfo, void *handle) +{ + //HK_handle = handle; + //MV_DISPLAY_FRAME_INFO stDisplayInfo = {0}; + //stDisplayInfo.hWnd = m_hwndDisplay; //显示的句柄 这种显示方式相当于绑定句柄 + //stDisplayInfo.pData = stImageInfo.pBufAddr; + //stDisplayInfo.nDataLen = stImageInfo.stFrameInfo.nFrameLen; + //stDisplayInfo.nWidth = stImageInfo.stFrameInfo.nWidth; + //stDisplayInfo.nHeight = stImageInfo.stFrameInfo.nHeight; + //stDisplayInfo.enPixelType = stImageInfo.stFrameInfo.enPixelType; + //MV_CC_DisplayOneFrame(handle, &stDisplayInfo); + //MV_CC_FreeImageBuffer(handle, &stImageInfo); +} + + + +//将flir相机传回来的图片进行显示 +void QtWidgetsApplication::do_img_redy(ImagePtr pResultImage) +{ + const size_t width = pResultImage->GetWidth(); + const size_t height = pResultImage->GetHeight(); + const unsigned char* pData = (const unsigned char*)pResultImage->GetData(); + // 根据像素格式创建 QImage + QImage qImage(pData, static_cast(width), static_cast(height), + static_cast(width * pResultImage->GetBitsPerPixel() / 8), + QImage::Format_RGB888); // 根据实际情况调整格式 + + m_scene->clear(); // 清除旧图像 + QPixmap pixmap = QPixmap::fromImage(qImage); + pixmapItem = m_scene->addPixmap(pixmap); + + ui.imgView->fitInView(pixmapItem, Qt::KeepAspectRatio); + ui.imgView->setScene(m_scene); + ui.imgView->show(); +} + + +int QtWidgetsApplication::resetExposure() { + + Spinnaker::GenApi::INodeMap& nodeMap = cam_worker->pCam->GetNodeMap(); + + CEnumerationPtr exposureAuto = nodeMap.GetNode("ExposureAuto"); + exposureAuto->SetIntValue(exposureAuto->GetEntryByName("Continuous")->GetValue()); + + return 0; +} + + +int QtWidgetsApplication::configureExposure(double exposure) { + + Spinnaker::GenApi::INodeMap& nodeMap = cam_worker->pCam->GetNodeMap(); + + CEnumerationPtr exposureAuto = nodeMap.GetNode("ExposureAuto"); + exposureAuto->SetIntValue(exposureAuto->GetEntryByName("Off")->GetValue()); + + CEnumerationPtr exposureMode = nodeMap.GetNode("ExposureMode"); + exposureMode->SetIntValue(exposureMode->GetEntryByName("Timed")->GetValue()); + + CFloatPtr exposureTime = nodeMap.GetNode("ExposureTime"); + exposureTime->SetValue(exposure); + + return 0; +} + + +void QtWidgetsApplication::setSlideSpinboxCheckboxSync() { + QString currentText = ui.CameraBox->currentText(); + if (currentText == "flir camera") { + // Exposure + connect(ui.sldExposure, &QSlider::valueChanged, ui.spinExposure, [this](int sliderValue) { + ui.spinExposure->setValue(static_cast(sliderValue)); + }); + connect(ui.spinExposure, QOverload::of(&QDoubleSpinBox::valueChanged), ui.sldExposure, [this](double spinValue) { + ui.sldExposure->setValue(static_cast(spinValue)); + configureExposure(spinValue); + }); + connect(ui.chkExposure, &QCheckBox::toggled, [this](bool checked) { + bool isEnabled = !checked; + ui.spinExposure->setEnabled(isEnabled); + ui.sldExposure->setEnabled(isEnabled); + resetExposure(); + }); + + // Gain + connect(ui.sldGain, &QSlider::valueChanged, ui.spinGain, [this](int sliderValue) { + ui.spinGain->setValue(static_cast(sliderValue) / 100.0); + }); + connect(ui.spinGain, QOverload::of(&QDoubleSpinBox::valueChanged), ui.sldGain, [this](double spinValue) { + ui.sldGain->setValue(static_cast(spinValue * 100)); + configureGain(spinValue); + }); + connect(ui.chkGain, &QCheckBox::toggled, [this](bool checked) { + bool isEnabled = !checked; + ui.spinGain->setEnabled(isEnabled); + ui.sldGain->setEnabled(isEnabled); + resetGain(); + }); + } + if (currentText =="haikang camera") { + // Exposure + connect(ui.sldExposure, &QSlider::valueChanged, ui.spinExposure, [this](int sliderValue) { + ui.spinExposure->setValue(static_cast(sliderValue)); + }); + connect(ui.spinExposure, QOverload::of(&QDoubleSpinBox::valueChanged), ui.sldExposure, [this](double spinValue) { + ui.sldExposure->setValue(static_cast(spinValue)); + HK_configureExposure(spinValue); + }); + connect(ui.chkExposure, &QCheckBox::toggled, [this](bool checked) { + bool isEnabled = !checked; + ui.spinExposure->setEnabled(isEnabled); + ui.sldExposure->setEnabled(isEnabled); + HK_resetExposure(); + }); + + // Gain + connect(ui.sldGain, &QSlider::valueChanged, ui.spinGain, [this](int sliderValue) { + ui.spinGain->setValue(static_cast(sliderValue) / 100.0); + }); + connect(ui.spinGain, QOverload::of(&QDoubleSpinBox::valueChanged), ui.sldGain, [this](double spinValue) { + ui.sldGain->setValue(static_cast(spinValue * 100)); + HK_configureGain(spinValue); + }); + connect(ui.chkGain, &QCheckBox::toggled, [this](bool checked) { + bool isEnabled = !checked; + ui.spinGain->setEnabled(isEnabled); + ui.sldGain->setEnabled(isEnabled); + HK_resetGain(); + }); + } + +} + +void QtWidgetsApplication::HK_resetGain() { + + + MV_CC_SetExposureAutoMode(HK_handle, 2); + +} + +void QtWidgetsApplication::HK_resetExposure() +{ + MV_CC_SetGain(HK_handle, 2); +} + +void QtWidgetsApplication::HK_configureExposure(double exposure) +{ + MV_CC_SetFloatValue(HK_handle, "ExposureTime", exposure); +} + +void QtWidgetsApplication::HK_configureGain(double gain) +{ + MV_CC_SetFloatValue(HK_handle, "Gain", gain); +} + +int QtWidgetsApplication::configureGain(double gain) { + Spinnaker::GenApi::INodeMap& nodeMap = cam_worker->pCam->GetNodeMap(); + + CEnumerationPtr gainAuto = nodeMap.GetNode("GainAuto"); + gainAuto->SetIntValue(gainAuto->GetEntryByName("Off")->GetValue()); + + CFloatPtr gainValue = nodeMap.GetNode("Gain"); + gainValue->SetValue(gain); + + return 0; +} + +int QtWidgetsApplication::resetGain() { + Spinnaker::GenApi::INodeMap& nodeMap = cam_worker->pCam->GetNodeMap(); + + CEnumerationPtr gainAuto = nodeMap.GetNode("GainAuto"); + gainAuto->SetIntValue(gainAuto->GetEntryByName("Continuous")->GetValue()); + + return 0; +} + +//单张图片的保存 +void QtWidgetsApplication::on_btnSingleImageSave_clicked() { + + QString currentText = ui.CameraBox->currentText(); + if (currentText == "flir camera") { + qDebug() << "Single image save clicked"; + QString fileName = QDateTime::currentDateTime().toString("yyyy-MM-dd-hh-mm-ss-zzz"); + QString absPath = ui.edtSaveDir->text() + "/" + fileName + ".tif"; + qDebug() << "Save path: " << absPath << ""; + + ImagePtr pImage = cam_worker->pCam->GetNextImage(1000); + if (pImage->IsIncomplete()) { + qDebug() << "Image incomplete in 1s: " << Image::GetImageStatusDescription(pImage->GetImageStatus()) << "..."; + } + ImagePtr pConvertedImage = processor.Convert(pImage, PixelFormat_RGB8); + pConvertedImage->Save(absPath.toStdString().c_str()); + } + if (currentText == "haikang camera") { + + + + + } + +} + +//选择保存路径 +void QtWidgetsApplication::on_btnSaveDir_clicked() { + qDebug() << "Choose save dir clicked"; + QString dirPath = QFileDialog::getExistingDirectory( + this, + QString::fromUtf8(u8"请选择图像保存路径") + ); + qDebug() << "Choose save dir: " << dirPath << ""; + ui.edtSaveDir->setText(dirPath); +} + + +// 将TCT检测的图片进行显示 +void QtWidgetsApplication::input_img(QImage image) +{ + in_image = image; + // 显示经过网络检测后的图片 + this->ui.TCT_View->setScene(tct_scene); + tct_pixmap = QPixmap::fromImage(in_image); + tct_pixmapItem = tct_scene->addPixmap(tct_pixmap); + // 自适应显示图片 + ui.TCT_View->fitInView(tct_pixmapItem, Qt::KeepAspectRatio); + ui.TCT_View->setRenderHint(QPainter::Antialiasing); // 抗锯齿 + ui.TCT_View->setDragMode(QGraphicsView::ScrollHandDrag); // 启动手动拖动模式 + ui.TCT_View->show(); +} + +// 将ImagePtr转换成QImage +QImage convertToQImage(const ImagePtr& pImage) +{ + // 获取图像尺寸 + int width = pImage->GetWidth(); + int height = pImage->GetHeight(); + + // 获取像素数据指针(假设为 uchar* 类型) + const uchar* pData = reinterpret_cast(pImage->GetData()); + + // 获取每行的字节数(步长) + int stride = width * 3; // 假设存在此方法,若不存在可设为 width * 3 + + // 构造 QImage + QImage qImage(pData, width, height, stride, QImage::Format_RGB888); + + // 返回深拷贝,避免原始图像数据被释放导致 QImage 无效 + return qImage.copy(); +} + +// 将相机图片输入到网络的线程中 +void QtWidgetsApplication::on_pb_model_clicked() +{ + /*QString fileName = QFileDialog::getOpenFileName(this, "选择图片", "D:/TCT_down/dataset/test/images", "Images (*.png *.jpg)"); + QImage image(fileName);*/ + QString currentText = ui.CameraBox->currentText(); + if (currentText == "flir camera") { + ImagePtr pImage = cam_worker->pCam->GetNextImage(1000); + if (pImage->IsIncomplete()) { + qDebug() << "Image incomplete in 1s: " << Image::GetImageStatusDescription(pImage->GetImageStatus()) << "..."; + } + ImagePtr pConvertedImage = processor.Convert(pImage, PixelFormat_RGB8); + QImage image = convertToQImage(pConvertedImage); + emit emit_img(image); + } + if (currentText == "haikang camera") { + + emit emit_img(hikimage); + } + +} + +// 运动控制 +void QtWidgetsApplication::on_btnUp_clicked() { + double zdis = ui.spinZDis->value(); + qDebug() << "up clicked, move up: " << zdis << "um"; + Mycontrol::getInstance().moveBy(3, zdis * -1e-3); +} + +void QtWidgetsApplication::on_btnDown_clicked() { + double zdis = ui.spinZDis->value(); + qDebug() << "up clicked, move up: " << zdis << "um"; + Mycontrol::getInstance().moveBy(3, -zdis * -1e-3); +} + +void QtWidgetsApplication::on_btnLeft_clicked() { + double xdis = ui.spinXDis->value(); + qDebug() << "up clicked, move up: " << xdis << "um"; + Mycontrol::getInstance().moveBy(2, -xdis * -1e-3); +} + +void QtWidgetsApplication::on_btnRight_clicked() { + double xdis = ui.spinXDis->value(); + qDebug() << "up clicked, move up: " << xdis << "um"; + Mycontrol::getInstance().moveBy(2, xdis * -1e-3); +} + +void QtWidgetsApplication::on_btnForward_clicked() { + double ydis = ui.spinYDis->value(); + qDebug() << "up clicked, move up: " << ydis << "um"; + Mycontrol::getInstance().moveBy(1, -ydis * -1e-3); +} + +void QtWidgetsApplication::on_btnBackward_clicked() { + double ydis = ui.spinYDis->value(); + qDebug() << "up clicked, move up: " << ydis << "um"; + Mycontrol::getInstance().moveBy(1, ydis * -1e-3); +} + + +double sharpnessSobel(cv::Mat image) { + cv::Mat roi = image(cv::Rect(968, 768, 512, 512)); + cv::Mat gray; + cv::cvtColor(roi, gray, cv::COLOR_BGR2GRAY); + cv::GaussianBlur(gray, gray, cv::Size(5, 5), 0); + cv::Mat sobelx, sobely; + cv::Sobel(gray, sobelx, CV_64F, 1, 0, 3); + cv::Sobel(gray, sobely, CV_64F, 0, 1, 3); + cv::Mat magnitude; + cv::magnitude(sobelx, sobely, magnitude); + return cv::mean(magnitude)[0]; +} \ No newline at end of file diff --git a/QtWidgetsApplication.h b/QtWidgetsApplication.h new file mode 100644 index 0000000..72827bc --- /dev/null +++ b/QtWidgetsApplication.h @@ -0,0 +1,98 @@ +#pragma once +#include +#include +#include "ui_QtWidgetsApplication.h" +#include +#include +#include +#include +#include +#include "M_camera.h" +#include "TCT.h" +#include +#include +#include +#include +#include "haikang.h" +#include "Mycontrol.h" + + +class QtWidgetsApplication : public QMainWindow +{ + Q_OBJECT + +public: + QtWidgetsApplication(QWidget *parent = nullptr); + + ~QtWidgetsApplication(); + + int resetExposure(); // filr自动曝光 + + int configureExposure(double exposure); //设置曝光 + + void setSlideSpinboxCheckboxSync(); //让曝光,增益的Slide和Spinbox同步 + + void HK_resetGain(); // HK设置增益 + + void HK_resetExposure(); // HK自动曝光 + + void HK_configureExposure(double exposure); + + void HK_configureGain(double exposure); //HK调节增益 + + int configureGain(double gain); //设置增益 + + int resetGain(); //自动增益 + + QPixmap tct_pixmap; + QGraphicsPixmapItem* tct_pixmapItem; + QGraphicsScene* tct_scene; //TCT检测显示需要的 + + TCT* my_TCT; //创建模型处理的指针 + + QThread* m_infQthread; //模型处理的线程 + + QImage in_image; //经onnx模型检测后传回来的图片 + + QImage hikimage; //海康需要传入TCT时用 + +public slots: + + void do_img_redy(ImagePtr pResultImage); //显示flir相机传回来的图像 + void on_btnSingleImageSave_clicked(); //保存单张图片 + void on_btnSaveDir_clicked(); //选择存储路径 + void input_img(QImage image); //接收经过网络后的图片 + void on_pb_model_clicked(); //TCT自动检测按钮 + + void do_hai_img_redy(MV_FRAME_OUT stImageInfo, void* handle); //将海康相机传回来的图片进行显示 //SB处理办法 弃用 + void on_pb_camera_clicked(); //选择相机 + void on_btnUp_clicked(); //Z向上 + void on_btnDown_clicked(); //Z向下 + void on_btnLeft_clicked(); //X向左 + void on_btnRight_clicked(); //X向右 + void on_btnForward_clicked(); //Y向前 + void on_btnBackward_clicked(); //Y向后 + void hai_img_redy(QImage image, void* handle); //将海康相机传回来的图片进行显示 +signals: + void emit_img(QImage image); //将需要传入网络的图片传出去 + + +private: + Ui::QtWidgetsApplicationClass ui; + HWND m_hwndDisplay; //显示控件的句柄 + QPixmap pixmap; + QGraphicsPixmapItem* pixmapItem; + QGraphicsScene* m_scene; //相机显示 + void* HK_handle = NULL; //海康相机的句柄 + ImageProcessor processor; + + ImagePtr pResultImage; + + QThread* cam_thread; // 相机的线程 + M_camera* cam_worker; // filr相机 + haikang* Hacm_worker; // haikang相机 + +}; + +// 计算图像清晰度 +double sharpnessSobel(cv::Mat image); diff --git a/QtWidgetsApplication.qrc b/QtWidgetsApplication.qrc new file mode 100644 index 0000000..7f460d3 --- /dev/null +++ b/QtWidgetsApplication.qrc @@ -0,0 +1,4 @@ + + + + diff --git a/QtWidgetsApplication.sln b/QtWidgetsApplication.sln new file mode 100644 index 0000000..1dd20d1 --- /dev/null +++ b/QtWidgetsApplication.sln @@ -0,0 +1,25 @@ +锘 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.13.35919.96 d17.13 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QtWidgetsApplication", "QtWidgetsApplication.vcxproj", "{DFE02648-E0B0-477C-9592-2968ADF2C70B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DFE02648-E0B0-477C-9592-2968ADF2C70B}.Debug|x64.ActiveCfg = Debug|x64 + {DFE02648-E0B0-477C-9592-2968ADF2C70B}.Debug|x64.Build.0 = Debug|x64 + {DFE02648-E0B0-477C-9592-2968ADF2C70B}.Release|x64.ActiveCfg = Release|x64 + {DFE02648-E0B0-477C-9592-2968ADF2C70B}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {0BFAF1E1-EF2D-43DF-B91A-6C7644D4B72D} + EndGlobalSection +EndGlobal diff --git a/QtWidgetsApplication.ui b/QtWidgetsApplication.ui new file mode 100644 index 0000000..a2ee2fc --- /dev/null +++ b/QtWidgetsApplication.ui @@ -0,0 +1,693 @@ + + + QtWidgetsApplicationClass + + + + 0 + 0 + 1439 + 1169 + + + + QtWidgetsApplication + + + + + + 10 + 650 + 1531 + 471 + + + + 0 + + + + 鐢ㄦ埛妯″紡 + + + + + 800 + 0 + 551 + 41 + + + + + + + X: + + + + + + + + 0 + 0 + + + + um + + + 9999.989999999999782 + + + 500.000000000000000 + + + + + + + Qt::Vertical + + + + 40 + 20 + + + + + + + + Y: + + + + + + + + 0 + 0 + + + + um + + + 9999.989999999999782 + + + 500.000000000000000 + + + + + + + Qt::Vertical + + + + 40 + 20 + + + + + + + + Z: + + + + + + + + 0 + 0 + + + + um + + + 9999.989999999999782 + + + 0.500000000000000 + + + + + + + + + 800 + 40 + 551 + 41 + + + + + + + X: + + + + + + + + 0 + 0 + + + + + + + 9999.989999999999782 + + + 500.000000000000000 + + + + + + + Qt::Vertical + + + + 40 + 20 + + + + + + + + Y: + + + + + + + + 0 + 0 + + + + + + + 9999.989999999999782 + + + 500.000000000000000 + + + + + + + Qt::Vertical + + + + 40 + 20 + + + + + + + + Z: + + + + + + + + 0 + 0 + + + + + + + 9999.989999999999782 + + + 0.500000000000000 + + + + + + + + + 380 + 0 + 372 + 311 + + + + + + + + + 澧 鐩婏細 + + + + + + + 鏇 鍏夛細 + + + + + + + + + + + 4799 + + + + + + + 6 + + + 15000 + + + + + + + + + + + dB + + + 47.990000000000002 + + + + + + + us + + + 6.000000000000000 + + + 15000.000000000000000 + + + 6.000000000000000 + + + + + + + + + + + 鑷姩 + + + + + + + 鑷姩 + + + + + + + + + + + 0 + 0 + 376 + 219 + + + + + + + + + + + + + 璇烽夋嫨鐩告満 + + + + + + + + flir camera + + + + + haikang camera + + + + + + + + + + 纭畾 + + + + + + + + + + + + + 淇濆瓨璺緞锛 + + + + + + + true + + + + + + + 鎵撳紑... + + + + + + + + + + 0 + 0 + + + + 鍗曞紶鍥惧儚淇濆瓨 + + + + + + + + + + + + + true + + + + 120 + 120 + + + + + 120 + 120 + + + + 鑷姩瀵圭劍 + + + + + + + true + + + + 120 + 120 + + + + + 120 + 120 + + + + 鑷姩妫娴 + + + + + + + true + + + + 120 + 120 + + + + + 120 + 120 + + + + 澶氳閲庨噰闆嗚嚜鍔ㄥ鐒 + + + + + + + + + + + 990 + 80 + 81 + 71 + + + + + 0 + 0 + + + + + + + + + + 800 + 90 + 71 + 61 + + + + + 0 + 0 + + + + + + + + + + 900 + 130 + 71 + 71 + + + + + 0 + 0 + + + + + + + + + + 1090 + 130 + 71 + 71 + + + + + 0 + 0 + + + + + + + + + + 990 + 180 + 81 + 71 + + + + + 0 + 0 + + + + + + + + + + 800 + 170 + 71 + 61 + + + + + 0 + 0 + + + + + + + + + + 楂樼骇妯″紡 + + + + + + + 1 + 11 + 1431 + 641 + + + + + + + + + + + + + + + + 0 + 0 + 1439 + 23 + + + + + + TopToolBarArea + + + false + + + + + + + + + + diff --git a/QtWidgetsApplication.vcxproj b/QtWidgetsApplication.vcxproj new file mode 100644 index 0000000..beaa44c --- /dev/null +++ b/QtWidgetsApplication.vcxproj @@ -0,0 +1,145 @@ +锘 + + + + Debug + x64 + + + Release + x64 + + + + {DFE02648-E0B0-477C-9592-2968ADF2C70B} + QtVS_v304 + 10.0 + 10.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + Application + v143 + true + Unicode + + + Application + v143 + false + true + Unicode + + + + + + + 5.12.3_msvc2017_64 + core;gui;widgets + debug + + + 5.12.3_msvc2017_64 + core;gui;widgets + release + + + + + + + + + + + + + + + + + + + + + + D:\MVS\MVS\Development\Includes;D:\FUXIAOXI\MicroscopeController\3rdparty\opencv\build\include;D:\Spinnaker\include;D:\FUXIAOXI\MicroscopeController\3rdparty\XTController\include;%(AdditionalIncludeDirectories) + + + D:\MVS\MVS\Development\Libraries\win64;D:\Spinnaker\lib64\vs2015;D:\FUXIAOXI\MicroscopeController\3rdparty\opencv\build\x64\vc16\lib;D:\FUXIAOXI\MicroscopeController\3rdparty\XTController\lib;%(AdditionalLibraryDirectories) + opencv_world4100.lib;Spinnaker_v140.lib;SpinUpdate_v140.lib;SpinVideo_v140.lib;MvCameraControl.lib;MotionControlExtendDll.lib;voice_motor_dll.lib;GKZTMotion.lib;MotionControlDll.lib;%(AdditionalDependencies) + + + + + D:\MVS\MVS\Development\Includes;D:\FUXIAOXI\MicroscopeController\3rdparty\XTController\include;D:\Spinnaker\include;D:\FUXIAOXI\MicroscopeController\3rdparty\opencv\build\include;%(AdditionalIncludeDirectories) + + + D:\MVS\MVS\Development\Libraries\win64;D:\FUXIAOXI\MicroscopeController\3rdparty\opencv\build\x64\vc16\lib;D:\FUXIAOXI\MicroscopeController\3rdparty\XTController\lib;%(AdditionalLibraryDirectories) + opencv_world4100d.lib;Spinnakerd_v140.lib;SpinUpdate_v140.lib;SpinVideod_v140.lib;MvCameraControl.lib;%(AdditionalDependencies) + + + + + true + Level3 + true + true + + + Console + true + + + + + true + Level3 + true + true + true + true + + + Console + false + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QtWidgetsApplication.vcxproj.filters b/QtWidgetsApplication.vcxproj.filters new file mode 100644 index 0000000..513f5d7 --- /dev/null +++ b/QtWidgetsApplication.vcxproj.filters @@ -0,0 +1,76 @@ +锘 + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + + + {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C} + ts + + + + + Resource Files + + + Form Files + + + Header Files + + + Source Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/QtWidgetsApplication.vcxproj.user b/QtWidgetsApplication.vcxproj.user new file mode 100644 index 0000000..2788bd3 --- /dev/null +++ b/QtWidgetsApplication.vcxproj.user @@ -0,0 +1,14 @@ +锘 + + + + + + 2025-05-26T06:52:07.5163119Z + + + + + 2025-05-26T07:02:27.1347138Z + + \ No newline at end of file diff --git a/TCT.cpp b/TCT.cpp new file mode 100644 index 0000000..fed0dde --- /dev/null +++ b/TCT.cpp @@ -0,0 +1,291 @@ +#include "TCT.h" + +TCT::TCT(QObject* parent) +{ + + +} + +TCT::~TCT() +{ + +} + +TCT::TCT(const std::string& onnxModelPath, const cv::Size& modelInputShape, const std::string& classesTxtFile, const bool& runWithCuda) +{ + modelPath = onnxModelPath; + modelShape = modelInputShape; + classesPath = classesTxtFile; + cudaEnabled = runWithCuda; + + loadOnnxNetwork(); + //loadClassesFromFile(); //The classes are hard-coded for this example + +} + +std::vector TCT::runInference(const cv::Mat& input) +{ + cv::Mat modelInput = input; + if (letterBoxForSquare && modelShape.width == modelShape.height) + modelInput = formatToSquare(modelInput); + + cv::Mat blob; + cv::dnn::blobFromImage(modelInput, blob, 1.0 / 255.0, modelShape, cv::Scalar(), true, false); + net.setInput(blob); + + std::vector outputs; + net.forward(outputs, net.getUnconnectedOutLayersNames()); + + int rows = outputs[0].size[1]; + int dimensions = outputs[0].size[2]; + + bool yolov8 = false; + // yolov5 has an output of shape (batchSize, 25200, 85) (Num classes + box[x,y,w,h] + confidence[c]) + // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h]) + if (dimensions > rows) // Check if the shape[2] is more than shape[1] (yolov8) + { + yolov8 = true; + rows = outputs[0].size[2]; + dimensions = outputs[0].size[1]; + + outputs[0] = outputs[0].reshape(1, dimensions); + cv::transpose(outputs[0], outputs[0]); + } + float* data = (float*)outputs[0].data; + + float x_factor = modelInput.cols / modelShape.width; + float y_factor = modelInput.rows / modelShape.height; + + std::vector class_ids; + std::vector confidences; + std::vector boxes; + + for (int i = 0; i < rows; ++i) + { + if (yolov8) + { + float* classes_scores = data + 4; + + cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores); + cv::Point class_id; + double maxClassScore; + + minMaxLoc(scores, 0, &maxClassScore, 0, &class_id); + + if (maxClassScore > modelScoreThreshold) + { + confidences.push_back(maxClassScore); + class_ids.push_back(class_id.x); + + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = int((x - 0.5 * w) * x_factor); + int top = int((y - 0.5 * h) * y_factor); + + int width = int(w * x_factor); + int height = int(h * y_factor); + + boxes.push_back(cv::Rect(left, top, width, height)); + } + } + else // yolov5 + { + float confidence = data[4]; + + if (confidence >= modelConfidenceThreshold) + { + float* classes_scores = data + 5; + + cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores); + cv::Point class_id; + double max_class_score; + + minMaxLoc(scores, 0, &max_class_score, 0, &class_id); + + if (max_class_score > modelScoreThreshold) + { + confidences.push_back(confidence); + class_ids.push_back(class_id.x); + + float x = data[0]; + float y = data[1]; + float w = data[2]; + float h = data[3]; + + int left = int((x - 0.5 * w) * x_factor); + int top = int((y - 0.5 * h) * y_factor); + + int width = int(w * x_factor); + int height = int(h * y_factor); + + boxes.push_back(cv::Rect(left, top, width, height)); + } + } + } + + data += dimensions; + } + + std::vector nms_result; + cv::dnn::NMSBoxes(boxes, confidences, modelScoreThreshold, modelNMSThreshold, nms_result); + + std::vector detections{}; + for (unsigned long i = 0; i < nms_result.size(); ++i) + { + int idx = nms_result[i]; + + Detection result; + result.class_id = class_ids[idx]; + result.confidence = confidences[idx]; + + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution dis(100, 255); + result.color = cv::Scalar(dis(gen), + dis(gen), + dis(gen)); + + result.className = classes[result.class_id]; + result.box = boxes[idx]; + + detections.push_back(result); + } + + return detections; +} + +void TCT::loadClassesFromFile() +{ + std::ifstream inputFile(classesPath); + if (inputFile.is_open()) + { + std::string classLine; + while (std::getline(inputFile, classLine)) + classes.push_back(classLine); + inputFile.close(); + } +} + +void TCT::loadOnnxNetwork() +{ + net = cv::dnn::readNetFromONNX(modelPath); + cudaEnabled = 1; + if (cudaEnabled) + { + std::cout << "\nRunning on CUDA" << std::endl; + net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA); + net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA); + + } + else + { + std::cout << "\nRunning on CPU" << std::endl; + net.setPreferableBackend(cv::dnn::DNN_BACKEND_OPENCV); + net.setPreferableTarget(cv::dnn::DNN_TARGET_CPU); + } +} + +cv::Mat TCT::formatToSquare(const cv::Mat& source) +{ + int col = source.cols; + int row = source.rows; + int _max = MAX(col, row); + cv::Mat result = cv::Mat::zeros(_max, _max, CV_8UC3); + source.copyTo(result(cv::Rect(0, 0, col, row))); + return result; +} + +//QImageToCvMat函数 +cv::Mat convertQImageToCvMat(const QImage& qImage) { + // 转换为RGB888格式,确保三通道 + QImage swappedImage = qImage.convertToFormat(QImage::Format_RGB888); + + // 创建临时cv::Mat,共享数据 + cv::Mat tmp(swappedImage.height(), swappedImage.width(), CV_8UC3, + const_cast(swappedImage.bits()), + static_cast(swappedImage.bytesPerLine())); + + // 将RGB转为OpenCV默认的BGR顺序 + cv::Mat cvMat; + cv::cvtColor(tmp, cvMat, cv::COLOR_RGB2BGR); + + // 克隆数据以确保独立于QImage的生命周期 + return cvMat.clone(); +} + +// CvMatToQImage函数 +QImage CvMatToQImage(const cv::Mat& mat) { + if (mat.empty()) + return QImage(); + + // 处理单通道和三通道情况 + cv::Mat rgbMat; + switch (mat.channels()) { + case 1: + cv::cvtColor(mat, rgbMat, cv::COLOR_GRAY2RGB); + break; + case 3: + cv::cvtColor(mat, rgbMat, cv::COLOR_BGR2RGB); + break; + default: + throw std::runtime_error("Unsupported channel count"); + } + + // 构造QImage并拷贝数据(避免野指针) + return QImage( + rgbMat.data, + rgbMat.cols, + rgbMat.rows, + static_cast(rgbMat.step), + QImage::Format_RGB888 + ).copy(); // 必须.copy()保证独立内存 +} + +// 总体处理函数 +void TCT::main_module(QImage image) +{ + bool runOnGPU = false; + TCT inf("E:/wjy/QtWidgetsApplication-master/best.onnx", cv::Size(640, 640), "classes.txt", runOnGPU); //处理模型 + + //std::vector imageNames; + //imageNames.push_back("D:/4.png"); //读取图片 + + cv::Mat frame = convertQImageToCvMat(image); + + std::vector output = inf.runInference(frame); + + int detections = output.size(); + std::cout << "Number of detections:" << detections << std::endl; + + for (int i = 0; i < detections; ++i) + { + Detection detection = output[i]; + + cv::Rect box = detection.box; + cv::Scalar color = detection.color; + + // Detection box + cv::rectangle(frame, box, color, 2); + + // Detection box text + std::string classString = detection.className + ' ' + std::to_string(detection.confidence).substr(0, 4); + cv::Size textSize = cv::getTextSize(classString, cv::FONT_HERSHEY_DUPLEX, 1, 2, 0); + cv::Rect textBox(box.x, box.y - 40, textSize.width + 10, textSize.height + 20); + + cv::rectangle(frame, textBox, color, cv::FILLED); + cv::putText(frame, classString, cv::Point(box.x + 5, box.y - 10), cv::FONT_HERSHEY_DUPLEX, 1, cv::Scalar(0, 0, 0), 2, 0); + } + // Inference ends here... + +// This is only for preview purposes + float scale = 0.8; + QImage img_i = CvMatToQImage(frame); + cv::Mat myframe = convertQImageToCvMat(img_i); + emit em_img(img_i); + +} + + diff --git a/TCT.h b/TCT.h new file mode 100644 index 0000000..31274a7 --- /dev/null +++ b/TCT.h @@ -0,0 +1,87 @@ +#pragma once +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +// Cpp native +#include +#include +#include +#include + +// OpenCV / DNN / Inference +#include +#include +#include + + + +struct Detection +{ + int class_id{ 0 }; + std::string className{}; + float confidence{ 0.0 }; + cv::Scalar color{}; + cv::Rect box{}; +}; + +class TCT : public QObject +{ + Q_OBJECT + +public: + TCT(QObject* parent = nullptr); + ~TCT(); + + // 处理onnx模型(传入onnx模型地址,网络所需图片size, classes, 是否使用GPU) + TCT(const std::string& onnxModelPath, const cv::Size& modelInputShape = { 640, 640 }, const std::string& classesTxtFile = "", const bool& runWithCuda = false); + std::vector runInference(const cv::Mat& input); + + QImage img_i; + +public slots: + + //总体处理函数 + void main_module(QImage image); + +signals: + + //将经过网络的图片发给主线程 + void em_img(QImage img); + +private: + + void loadClassesFromFile(); + + //读取模型 + void loadOnnxNetwork(); + + cv::Mat formatToSquare(const cv::Mat& source); + + std::string modelPath{}; + + std::string classesPath{}; + + bool cudaEnabled{}; + + + /*"bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light", "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow", "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch", "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear", "hair drier", "toothbrush" };*/ + std::vector classes{ "cancer" }; //类别数及其名称 + cv::Size2f modelShape{}; + + float modelConfidenceThreshold{ 0.25 }; //置信度 + + float modelScoreThreshold{ 0.15 }; //得分门槛 + + float modelNMSThreshold{ 0.50 }; //最大值抑制 + + bool letterBoxForSquare = true; + + cv::dnn::Net net; //定义的网络 + +}; \ No newline at end of file diff --git a/best.onnx b/best.onnx new file mode 100644 index 0000000..0db8bbe Binary files /dev/null and b/best.onnx differ diff --git a/focus.cpp b/focus.cpp new file mode 100644 index 0000000..bf9b53a --- /dev/null +++ b/focus.cpp @@ -0,0 +1,12 @@ +#include "focus.h" + + +focus::focus(QObject* parent) +{ + +} + +focus::~focus() +{ + +} \ No newline at end of file diff --git a/focus.h b/focus.h new file mode 100644 index 0000000..bbfaae1 --- /dev/null +++ b/focus.h @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "opencv2/opencv.hpp" +#include +#include +#include +#include + + + + + +class focus : public QObject +{ + Q_OBJECT + + + +public: + + focus(QObject* parent); + ~focus(); + + + + + +}; \ No newline at end of file diff --git a/haikang.cpp b/haikang.cpp new file mode 100644 index 0000000..1e7d096 --- /dev/null +++ b/haikang.cpp @@ -0,0 +1,164 @@ +#pragma once +#pragma execution_character_set("UTF-8") +#include +#include "haikang.h" +#include + + + +haikang::haikang(QWidget* parent) +{ + nRet = MV_CC_Initialize(); + // ch:枚举设备 | en:Enum device + MV_CC_DEVICE_INFO_LIST stDeviceList; + memset(&stDeviceList, 0, sizeof(MV_CC_DEVICE_INFO_LIST)); + nRet = MV_CC_EnumDevices(MV_GIGE_DEVICE | MV_USB_DEVICE | MV_GENTL_CAMERALINK_DEVICE | MV_GENTL_CXP_DEVICE | MV_GENTL_XOF_DEVICE, &stDeviceList); + if (MV_OK != nRet) + { + printf("Enum Devices fail! nRet [0x%x]\n", nRet); + + } + if (stDeviceList.nDeviceNum > 0) + { + for (unsigned int i = 0; i < stDeviceList.nDeviceNum; i++) + { + printf("[device %d]:\n", i); + MV_CC_DEVICE_INFO* pDeviceInfo = stDeviceList.pDeviceInfo[i]; + if (NULL == pDeviceInfo) + { + break; + } + PrintDeviceInfo(pDeviceInfo); + } + } + else + { + printf("Find No Devices!\n"); + + } + printf("Please Input camera index(0-%d):", stDeviceList.nDeviceNum - 1); + unsigned int nIndex = 0; + + // ch:选择设备并创建句柄 | en:Select device and create handle + nRet = MV_CC_CreateHandle(&handle, stDeviceList.pDeviceInfo[nIndex]); + if (MV_OK != nRet) + { + printf("Create Handle fail! nRet [0x%x]\n", nRet); + } + + // ch:打开设备 | en:Open device + nRet = MV_CC_OpenDevice(handle); + if (MV_OK != nRet) + { + printf("Open Device fail! nRet [0x%x]\n", nRet); + } + else + { + printf("Open Device success! nRet [0x%x]\n", nRet); + } + + // ch:开始取流 | en:Start grab image + nRet = MV_CC_StartGrabbing(handle); + +} + +haikang::~haikang() +{ + finish(); + +} + +bool haikang::PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo) +{ + if (NULL == pstMVDevInfo) + { + printf("The Pointer of pstMVDevInfo is NULL!\n"); + return false; + } + + else if (pstMVDevInfo->nTLayerType == MV_USB_DEVICE) + { + printf("UserDefinedName: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chUserDefinedName); + printf("Serial Number: %s\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.chSerialNumber); + printf("Device Number: %d\n\n", pstMVDevInfo->SpecialInfo.stUsb3VInfo.nDeviceNumber); + } + return true; +} + + + +void haikang::GrabThreadProcess() { + MV_CC_SetEnumValue(handle, "TriggerMode", MV_TRIGGER_MODE_OFF); + while (1) { + MV_CC_GetImageBuffer(handle, &stImageInfo, 1000); + // 检查像素格式是否为Bayer RG8 + if (stImageInfo.stFrameInfo.enPixelType == PixelType_Gvsp_BayerRG8) { + // 使用OpenCV处理Bayer数据 + cv::Mat bayerMat( + stImageInfo.stFrameInfo.nHeight, + stImageInfo.stFrameInfo.nWidth, + CV_8UC1, + stImageInfo.pBufAddr + ); + cv::Mat rgbMat; + cv::cvtColor(bayerMat, rgbMat, cv::COLOR_BayerRG2RGB); + // 转换为QImage(RGB888) + QImage tempImage( + rgbMat.data, + rgbMat.cols, + rgbMat.rows, + rgbMat.step, + QImage::Format_RGB888 + ); + // 深拷贝数据确保安全 + QImage safeImage = tempImage.copy(); + // 发送信号到主线程显示 + emit img_to(safeImage, handle); + } + MV_CC_FreeImageBuffer(handle, &stImageInfo); + } +} + +//memcpy(&m_stImageInfo, &(stImageInfo.stFrameInfo), sizeof(MV_FRAME_OUT_INFO_EX)); //复制一份出来,用于保存图片 + /*QImage image(stImageInfo.pBufAddr, + stImageInfo.stFrameInfo.nWidth, + stImageInfo.stFrameInfo.nHeight, + QImage::Format_Indexed8); + + int bytesPerLine = stImageInfo.stFrameInfo.enPixelType; + qDebug() << "is: " << stImageInfo.stFrameInfo.enPixelType << ""; + printf("bytesPerLine"); + emit img_to(image, handle); + MV_CC_FreeImageBuffer(handle, &stImageInfo);*/ + + //MV_FRAME_OUT stOutFrame = stImageInfo; + //emit img_redy(stImageInfo, handle); + + +void haikang::finish() { + + nRet = MV_CC_StopGrabbing(handle); + nRet = MV_CC_RegisterImageCallBackEx(handle, NULL, NULL); + nRet = MV_CC_CloseDevice(handle); + if (MV_OK != nRet) + { + printf("ClosDevice fail! nRet [0x%x]\n", nRet); + } + nRet = MV_CC_DestroyHandle(handle); + if (MV_OK != nRet) + { + printf("Destroy Handle fail! nRet [0x%x]\n", nRet); + } + handle = NULL; + if (handle != NULL) + { + MV_CC_DestroyHandle(handle); + handle = NULL; + } + + // ch:反初始化SDK | en:Finalize SDK + MV_CC_Finalize(); + printf("Destroy Handle success! nRet [0x%x]\n", nRet); + +} + diff --git a/haikang.h b/haikang.h new file mode 100644 index 0000000..112de2d --- /dev/null +++ b/haikang.h @@ -0,0 +1,45 @@ +#pragma once +#include "MvCameraControl.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "opencv2/opencv.hpp" +#include +#include +#include +#include + +class haikang : public QObject +{ + Q_OBJECT + +public: + haikang(QWidget* parent = nullptr); + ~haikang(); + + unsigned char* m_pSaveImageBuf; // 保存图像缓冲区指针 + HWND m_hwndDisplay; + QMutex m_hSaveImageMux; + int nRet = MV_OK; + void* handle = NULL; + bool PrintDeviceInfo(MV_CC_DEVICE_INFO* pstMVDevInfo); + + MV_FRAME_OUT_INFO_EX m_stImageInfo; // 图像信息结构体变量 + MV_FRAME_OUT stImageInfo = {0}; + + +public slots: + void GrabThreadProcess(); + void finish(); +signals: + void img_redy(MV_FRAME_OUT stImageInfo, void* handle); + + void img_to(QImage img, void* handle); + + +}; \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..2a09251 --- /dev/null +++ b/main.cpp @@ -0,0 +1,12 @@ +#include "QtWidgetsApplication.h" +#include +#include + +int main(int argc, char *argv[]) +{ + cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR);//抑制日志输出 + QApplication a(argc, argv); + QtWidgetsApplication w; + w.show(); + return a.exec(); +}