PosCalibrate/mainwindow.cpp
2025-10-15 14:50:31 +08:00

639 lines
19 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "calPoint.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
pos = new PosDevice(this);
cam = new Camera(this);
rs = new RotationStage(this);
tz = new QTimer(this);
tz_ = new QTimer(this);
tu = new QTimer(this);
tu_ = new QTimer(this);
topLeft=0;
topRight=0;
selectWidth=0;
selectHeight=0;
isFirstFrame=false;
loopState = false;
ui->lineEdit->setMaxLength(500);
ui->lineEdit_2->setMaxLength(500);
ui->lineEdit_3->setMaxLength(500);
ui->lineEdit_9->setMaxLength(500);
ui->lineEdit_10->setMaxLength(500);
ui->lineEdit_11->setMaxLength(500);
tz->setInterval(10);
tz_->setInterval(10);
tu->setInterval(10);
tu_->setInterval(10);
connect(pos,&PosDevice::sendPosData,this,&MainWindow::revPosData);
connect(cam,&Camera::sendPicData,this,&MainWindow::revPicData);
connect(rs,&RotationStage::sendRotateData,this,&MainWindow::revRotateData);
connect(tz,&QTimer::timeout,this,&MainWindow::zmove);
connect(tz_,&QTimer::timeout,this,&MainWindow::zmove_);
connect(tu,&QTimer::timeout,this,&MainWindow::umove);
connect(tu_,&QTimer::timeout,this,&MainWindow::umove_);
connect(ui->graphicsView, &ImageView::roiSelected, this, &MainWindow::onRoiSelected);
}
MainWindow::~MainWindow()
{
delete ui;
}
cv::Rect MainWindow::trackRoiWithCorrelation(const cv::Mat &frame)
{
if(roiTemplate.empty()) {
qDebug() << "模板为空";
return cv::Rect();
}
// 定义搜索区域在上一帧ROI周围扩大区域
int searchMargin = 30; // 搜索边界,可根据需要调整
cv::Rect searchArea;
searchArea.x = std::max(0, lastRoi.x - searchMargin);
searchArea.y = std::max(0, lastRoi.y - searchMargin);
searchArea.width = std::min(frame.cols - searchArea.x,
lastRoi.width + 2 * searchMargin);
searchArea.height = std::min(frame.rows - searchArea.y,
lastRoi.height + 2 * searchMargin);
// 检查搜索区域有效性
if(searchArea.width <= 0 || searchArea.height <= 0) {
qDebug() << "搜索区域无效";
return lastRoi;
}
// 提取搜索区域
cv::Mat searchRegion = frame(searchArea);
// 检查模板和搜索区域大小
if(searchRegion.rows < roiTemplate.rows || searchRegion.cols < roiTemplate.cols) {
// 搜索区域比模板小,调整模板大小
double scaleX = static_cast<double>(searchRegion.cols) / roiTemplate.cols;
double scaleY = static_cast<double>(searchRegion.rows) / roiTemplate.rows;
double scale = std::min(scaleX, scaleY) * 0.9; // 稍微缩小一点确保安全
cv::Mat resizedTemplate;
cv::resize(roiTemplate, resizedTemplate, cv::Size(), scale, scale, cv::INTER_AREA);
// 模板匹配
cv::Mat result;
cv::matchTemplate(searchRegion, resizedTemplate, result, cv::TM_CCOEFF_NORMED);
// 找到最佳匹配位置
double maxVal;
cv::Point maxLoc;
cv::minMaxLoc(result, nullptr, &maxVal, nullptr, &maxLoc);
// 设置匹配阈值
double correlationThreshold = 0.5;
if(maxVal < correlationThreshold) {
qDebug() << "匹配度低:" << maxVal;
return lastRoi;
}
// 计算新的ROI位置
cv::Rect newRoi;
newRoi.x = searchArea.x + maxLoc.x;
newRoi.y = searchArea.y + maxLoc.y;
newRoi.width = resizedTemplate.cols;
newRoi.height = resizedTemplate.rows;
qDebug() << "模板匹配成功, 相似度:" << maxVal << "位置:" << newRoi.x << newRoi.y;
lastRoi = newRoi;
return newRoi;
} else {
// 正常情况下的模板匹配
cv::Mat result;
cv::matchTemplate(searchRegion, roiTemplate, result, cv::TM_CCOEFF_NORMED);
// 找到最佳匹配位置
double maxVal;
cv::Point maxLoc;
cv::minMaxLoc(result, nullptr, &maxVal, nullptr, &maxLoc);
// 设置匹配阈值
double correlationThreshold = 0.6;
if(maxVal < correlationThreshold) {
qDebug() << "匹配度低:" << maxVal;
return lastRoi;
}
// 计算新的ROI位置
cv::Rect newRoi;
newRoi.x = searchArea.x + maxLoc.x;
newRoi.y = searchArea.y + maxLoc.y;
newRoi.width = roiTemplate.cols;
newRoi.height = roiTemplate.rows;
qDebug() << "模板匹配成功, 相似度:" << maxVal << "位置:" << newRoi.x << newRoi.y;
lastRoi = newRoi;
return newRoi;
}
}
void MainWindow::updateTemplate(const cv::Mat &frame, const cv::Rect &roi)
{
static int updateCounter = 0;
updateCounter++;
// 每N帧更新一次模板
if(updateCounter % 15 == 0) { // 每15帧更新一次
if(roi.x >= 0 && roi.y >= 0 &&
roi.x + roi.width <= frame.cols &&
roi.y + roi.height <= frame.rows) {
cv::Mat newTemplate = frame(roi).clone();
// 使用加权平均更新模板
double alpha = 0.1; // 学习率,较小的值使模板更新更慢
if(roiTemplate.size() == newTemplate.size()) {
cv::addWeighted(roiTemplate, 1.0 - alpha, newTemplate, alpha, 0, roiTemplate);
qDebug() << "模板已更新";
}
}
updateCounter = 0;
}
}
QImage MainWindow::cvMatToQImage(const cv::Mat &mat)
{
switch(mat.type()) {
case CV_8UC1: { // 灰度图
QImage img(mat.data, mat.cols, mat.rows,
static_cast<int>(mat.step), QImage::Format_Grayscale8);
return img.copy(); // 创建副本以避免内存问题
}
case CV_8UC3: { // 彩色图
QImage img(mat.data, mat.cols, mat.rows,
static_cast<int>(mat.step), QImage::Format_RGB888);
return img.rgbSwapped().copy();
}
case CV_8UC4: { // 带透明度的彩色图
QImage img(mat.data, mat.cols, mat.rows,
static_cast<int>(mat.step), QImage::Format_ARGB32);
return img.copy();
}
default:
qWarning() << "不支持的图像格式:" << mat.type();
return QImage();
}
}
cv::Point2f MainWindow::calculateCentroid(const cv::Mat &image, const cv::Rect &roi)
{
// 提取ROI区域
cv::Mat roiRegion = image(roi);
// 使用灰度图像直接计算矩
cv::Moments m = cv::moments(roiRegion, true);
if(m.m00 == 0) {
qDebug() << "无法计算质心:区域可能为全黑";
return cv::Point2f(-1, -1);
}
// 计算质心坐标
float centroidX = static_cast<float>(m.m10 / m.m00);
float centroidY = static_cast<float>(m.m01 / m.m00);
// 转换为图像坐标系
centroidX += roi.x;
centroidY += roi.y;
return cv::Point2f(centroidX, centroidY);
}
void MainWindow::closeloop()
{
// if(ui->lineEdit_4->text().isEmpty()||ui->lineEdit_5->text().isEmpty()||ui->lineEdit_6->text().isEmpty()){
// qDebug()<<"Error --> B Longtitude/Lantitude/Altitude is empty.";
// return ;
// }
while(loopState){
double ALon =ui->lineEdit_9->text().toDouble();
double ALan = ui->lineEdit_10->text().toDouble();
double AAlt = ui->lineEdit_11->text().toDouble();
double AHeading = ui->lineEdit->text().toDouble();
double APitch = ui->lineEdit_2->text().toDouble();
double ARoll = ui->lineEdit_3->text().toDouble();
// ALon = 120.30071541667; // A机经度deg
// ALan = 30.2623007866115; // A机纬度deg
// AAlt = 60.0/1000; // A机高度km
double BLon = ui->lineEdit_4->text().toDouble();
double BLan = ui->lineEdit_5->text().toDouble();
double BAlt= ui->lineEdit_6->text().toDouble();
// qDebug()<<"A Longtitude is "<<ALon<<"°, Lantitude is "<<ALan<<"°,Altitude is "<<AAlt<<"m.";
// qDebug()<<"B Longtitude is "<<BLon<<"°, Lantitude is "<<BLan<<"°,Altitude is "<<BAlt<<"m.";
// qDebug()<<"A Heading is "<<AHeading<<"°, Pitch is "<<APitch<<"°,Roll is "<<ARoll<<"°.";
QVector<double> res = calRes(ALon,ALan,AAlt,BLon,BLan,BAlt,AHeading,APitch,ARoll); //返回指向的方位角和俯仰角度数 E-->Z, N-->U,DEGEE
rs->move(0,2,res.at(0)*67108864/360);
rs->move(0,3,res.at(1)*67108864/360);
std::this_thread::sleep_for(std::chrono::milliseconds((int)(50)));
}
}
void MainWindow::on_pushButton_clicked()
{
pos->open();
}
void MainWindow::revPosData(QVector<QString> posData)
{
ui->lineEdit->setText(posData.at(0)); //Heading
ui->lineEdit_2->setText(posData.at(1)); //Pitch
ui->lineEdit_3->setText(posData.at(2)); //Roll
ui->lineEdit_10->setText(posData.at(3)); //Lantitude
ui->lineEdit_9->setText(posData.at(4)); //Longtitude
ui->lineEdit_11->setText(QString::number(posData.at(5).toDouble()/1000.0)); //Altitude
}
void MainWindow::on_pushButton_3_clicked()
{
cam->open();
}
void MainWindow::on_pushButton_5_clicked()
{
pos->close();
}
void MainWindow::on_pushButton_4_clicked()
{
cam->close();
}
void MainWindow::revPicData(QImage img){
QPixmap tmppximg = QPixmap::fromImage(img);
if(!ui->graphicsView->state){
ui->graphicsView->pximg->setPixmap(tmppximg);
}else{
cv::Mat cvImg; //origin pic
cvImg = cv::Mat(img.height(), img.width(), CV_8UC1,
(void*)img.constBits(), img.bytesPerLine());
// 定义ROI区域
cv::Rect currentRoi;
if(isFirstFrame) {
// 第一帧使用初始ROI
currentRoi = cv::Rect(topLeft, topRight,
selectWidth, selectHeight);
// 保存模板
if(currentRoi.x >= 0 && currentRoi.y >= 0 &&
currentRoi.x + currentRoi.width <= cvImg.cols &&
currentRoi.y + currentRoi.height <= cvImg.rows) {
roiTemplate = cvImg(currentRoi).clone();
lastRoi = currentRoi;
isFirstFrame = false;
qDebug() << "初始ROI设置: " << currentRoi.x << currentRoi.y
<< currentRoi.width << "x" << currentRoi.height;
centroidInit = calculateCentroid(cvImg, currentRoi);
qDebug() << "初始ROI质心坐标: (" << centroidInit.x << "," << centroidInit.y << ")";
}
} else {
// 后续帧使用模板匹配
currentRoi = trackRoiWithCorrelation(cvImg);
}
// 在图像上绘制ROI矩形
if(currentRoi.width > 0 && currentRoi.height > 0) {
// 为了在灰度图上显示彩色矩形先转换为BGR
cv::Mat colorImg;
cv::cvtColor(cvImg, colorImg, cv::COLOR_GRAY2BGR);
cv::rectangle(colorImg, currentRoi, cv::Scalar(255, 0, 0), 1);
centroid = calculateCentroid(cvImg, currentRoi);
if(centroid.x >= 0 && centroid.y >= 0) {
// 在质心位置绘制红色圆点
cv::circle(colorImg, centroid, 5, cv::Scalar(255, 0, 0), -1); // 红色实心圆
// 绘制十字线
cv::line(colorImg,
cv::Point(centroid.x - 10, centroid.y),
cv::Point(centroid.x + 10, centroid.y),
cv::Scalar(0, 0, 255), 2); // 水平线
cv::line(colorImg,
cv::Point(centroid.x, centroid.y - 10),
cv::Point(centroid.x, centroid.y + 10),
cv::Scalar(0, 0, 255), 2); // 垂直线
// 显示质心坐标
std::string centroidText = "Centroid: (" +
std::to_string(centroid.x).substr(0, 5) + ", " +
std::to_string(centroid.y).substr(0, 5) + ")";
cv::putText(colorImg, centroidText,
cv::Point(currentRoi.x, currentRoi.y - 20),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1);
std::string xTBL = "xTBL: " + std::to_string((centroid.x-centroidInit.x)*0.003953).substr(0, 5)+" degree";
std::string yTBL = "yTBL: " + std::to_string((centroid.y-centroidInit.y)*0.003953).substr(0, 5)+" degree";
cv::putText(colorImg, xTBL,
cv::Point(20, 20),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1);
cv::putText(colorImg, yTBL,
cv::Point(20, 40),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 255), 1);
// qDebug() << "ROI质心坐标: (" << centroid.x << "," << centroid.y << ")";
qDebug()<<"X 脱靶量(像素)"<< centroid.x-centroidInit.x;
qDebug()<<"Y 脱靶量(像素)"<< centroid.y-centroidInit.y;
// 更新模板
updateTemplate(cvImg, currentRoi);
// 转换回QImage显示
QImage resultImg = cvMatToQImage(colorImg);
ui->graphicsView->pximg->setPixmap(QPixmap::fromImage(resultImg));
} else {
// 如果没有有效ROI直接显示原图
ui->graphicsView->pximg->setPixmap(tmppximg);
}
}
}
}
void MainWindow::revRotateData(QVector<double> rData)
{
ui->lineEdit_7->setText(QString::number(rData[0],'f',3));
ui->lineEdit_8->setText(QString::number(rData[1],'f',3));
}
void MainWindow::on_pushButton_10_clicked()
{
rs->open();
}
void MainWindow::on_pushButton_6_pressed()
{
tz->start();
}
void MainWindow::on_pushButton_6_released()
{
tz->stop();
}
void MainWindow::zmove(){
rs->move(0,2,1000);
}
void MainWindow::zmove_()
{
rs->move(0,2,-1000);
}
void MainWindow::umove()
{
rs->move(0,3,1000);
}
void MainWindow::umove_()
{
rs->move(0,3,-1000);
}
void MainWindow::on_pushButton_7_pressed()
{
if(rs->state){
tz_->start();
}
}
void MainWindow::on_pushButton_7_released()
{
if(rs->state){
tz_->stop();
}
}
void MainWindow::on_pushButton_8_pressed()
{
if(rs->state){
tu->start();
}
}
void MainWindow::on_pushButton_8_released()
{
if(rs->state){
tu->stop();
}
}
void MainWindow::on_pushButton_9_pressed()
{
if(rs->state){
tu_->start();
}
}
void MainWindow::on_pushButton_9_released()
{
if(rs->state){
tu_->stop();
}
}
void MainWindow::on_pushButton_11_clicked()
{
rs->close();
}
void MainWindow::onRoiSelected(const QRectF &roi)
{
qDebug() << "ROI Selected - Position:" << roi.topLeft()
<< "Size:" << roi.size();
topLeft = roi.topLeft().x();
topRight = roi.topRight().y();
selectWidth=roi.width();
selectHeight=roi.height();
isFirstFrame=true;
ui->graphicsView->state=true;
}
void MainWindow::on_pushButton_2_clicked()
{
double ALon =ui->lineEdit_9->text().toDouble();
double ALan = ui->lineEdit_10->text().toDouble();
double AAlt =ui->lineEdit_11->text().toDouble();
double AHeading = ui->lineEdit->text().toDouble();
double APitch = ui->lineEdit_2->text().toDouble();
double ARoll = ui->lineEdit_3->text().toDouble();
// if(ui->lineEdit_4->text().isEmpty()||ui->lineEdit_5->text().isEmpty()||ui->lineEdit_6->text().isEmpty()){
// QMessageBox::critical(this,"Error","B Longtitude/Lantitude/Altitude is empty.");
// return ;
// }
double BLon = ui->lineEdit_4->text().toDouble();
double BLan = ui->lineEdit_5->text().toDouble();
double BAlt= ui->lineEdit_6->text().toDouble();
// gcal->setPositionA(ALon,ALan,AAlt);
// gcal->setPositionB(BLon,BLan,BAlt);
// gcal->setRealTimeAttitude(AHeading,APitch,ARoll);
// // // A机位置
// ALon = 120.30071541667; // A机经度deg
// ALan = 30.2623007866115; // A机纬度deg
// AAlt = 60.0/1000; // A机高度km
// // // B机位置
// BLon = 120.300913900136; // B机经度deg
// BLan = 30.2622503984337; // B机纬度deg
// BAlt = 60.0/1000 ;
// // // 根据POS设备实时姿态角解算B机在A机载体坐标系中的位置
// AHeading = 65.516;
// APitch = -0.112;
// ARoll = 1.648;
qDebug()<<"A Longtitude is "<<ALon<<"°, Lantitude is "<<ALan<<"°,Altitude is "<<AAlt<<"km.";
qDebug()<<"B Longtitude is "<<BLon<<"°, Lantitude is "<<BLan<<"°,Altitude is "<<BAlt<<"km.";
qDebug()<<"A Heading is "<<AHeading<<"°, Pitch is "<<APitch<<"°,Roll is "<<ARoll<<"°.";
QVector<double> res = calRes(ALon,ALan,AAlt,BLon,BLan,BAlt,AHeading,APitch,ARoll); //返回指向的方位角和俯仰角度数 E-->Z, N-->U,DEGEE
rs->move(0,2,res.at(0)*67108864/360);
rs->move(0,3,res.at(1)*67108864/360);
qDebug() <<"Single Close Loop track done";
}
void MainWindow::on_pushButton_12_clicked()
{
rs->stop();
loopState= false;
// task->join();
qDebug() <<"Open Loop track";
}
void MainWindow::on_pushButton_13_clicked()
{
double zDegree;
if(ui->lineEdit_12->text().isEmpty()){
zDegree = 0.0;
}else{
if(ui->lineEdit_12->text().toDouble()<0 ||ui->lineEdit_12->text().toDouble()>360 ){
QMessageBox::critical(this,"Error","Z Degree < 0 or Z Degree > 360");
return;
}
zDegree = ui->lineEdit_12->text().toDouble();
}
double uDegree;
if(ui->lineEdit_13->text().isEmpty()){
uDegree = 0.0;
}else{
if(ui->lineEdit_13->text().toDouble()<0 ||ui->lineEdit_13->text().toDouble()>360 ){
QMessageBox::critical(this,"Error","U Degree < 0 or U Degree > 360");
return ;
}
uDegree = ui->lineEdit_13->text().toDouble();
}
rs->move(1,2,zDegree*67108864/360);
rs->move(1,3,uDegree*67108864/360);
}
void MainWindow::on_pushButton_14_clicked()
{
loopState =true ;
qDebug() <<"Start RT Close Loop track";
// task = std::thread(&MainWindow::closeloop,this);
task = new std::thread(&MainWindow::closeloop,this);
}