1、基于新框架实现点击显示光谱;
2、影像拉伸显示优化;
3、右键菜单新增:移除所有栅格图层;
This commit is contained in:
tangchao0503
2026-03-06 17:33:30 +08:00
parent 4ad5c8b91e
commit 09095592af
10 changed files with 310 additions and 104 deletions

View File

@ -6,7 +6,6 @@
#include <QFileInfo> // 新增,用于解析路径
#include "HPPA.h"
#include "ImageReaderWriter.h"
#include "RasterLayer.h"
#include "LayerTreeLayerNode.h"
@ -17,6 +16,11 @@ HPPA* HPPA::instance()
return s_instance;
}
LayerTreeNode* HPPA::rasterGroupNode() const
{
return m_RasterGroup;
}
HPPA::HPPA(QWidget* parent)
: QMainWindow(parent)
{
@ -720,7 +724,7 @@ void HPPA::initControlTabwidget()
//电源控制
m_pc = new PowerControl();
m_pc->setWindowFlags(Qt::Widget);
ui.controlTabWidget->addTab(m_pc, QString::fromLocal8Bit("电源控制"));
ui.controlTabWidget->addTab(m_pc, QString::fromLocal8Bit("电源控制"));
//机械臂控制
m_rac = new RobotArmControl();
@ -912,6 +916,61 @@ void HPPA::removeLayerByTreeIndex()
}
}
void HPPA::removeAllLayersInRasterGroup()
{
if (!m_LayerTreeModel || !m_RasterGroup) return;
QVector<LayerTreeNode*> pending;
for (int i = 0; i < m_RasterGroup->childCount(); ++i)
{
pending.push_back(m_RasterGroup->childAt(i));
}
while (!pending.isEmpty())
{
LayerTreeNode* node = pending.back();
pending.pop_back();
if (!node) continue;
for (int i = 0; i < node->childCount(); ++i)
{
pending.push_back(node->childAt(i));
}
if (node->type() != LayerTreeNode::Type::Layer) continue;
auto* layerNode = static_cast<LayerTreeLayerNode*>(node);
MapLayer* mapLayer = layerNode->mapLayer();
QWidget* layerWidget = nullptr;
if (mapLayer && m_MapLayerStore)
{
layerWidget = m_MapLayerStore->widgetForLayer(mapLayer);
m_MapLayerStore->removeLayer(mapLayer);
}
if (layerWidget && m_imageViewerTabWidget)
{
const int tabIndex = m_imageViewerTabWidget->indexOf(layerWidget);
if (tabIndex >= 0)
{
m_imageViewerTabWidget->removeTab(tabIndex);
}
layerWidget->deleteLater();
}
}
while (m_RasterGroup->childCount() > 0)
{
const int row = m_RasterGroup->childCount() - 1;
LayerTreeNode* removed = m_LayerTreeModel->removeNode(m_RasterGroup, row);
if (removed)
{
delete removed;
}
}
}
void HPPA::initPanelToolbar()
{
mPanelMenu = new QMenu(QString::fromLocal8Bit("面板"), this);//create panel menu
@ -1084,7 +1143,7 @@ void HPPA::onStartRecordStep1()
if (m_RecordState % 2 == 1)
{
m_imageViewerTabWidget->clear();
//m_imageViewerTabWidget->clear();
m_Imager->setFileName2Save(imgPath);
m_Imager->setFrameNumber(this->frame_number->text().toInt());
@ -1099,7 +1158,7 @@ void HPPA::onStartRecordStep1()
m_RecordState -= 1;
ui.action_start_recording->setText(QString::fromLocal8Bit("采集"));
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(0,255,0);}");
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(0,255,0);}");
}
return;
}
@ -1109,10 +1168,10 @@ void HPPA::onStartRecordStep1()
if (m_RecordState % 2 == 1)
{
m_imageViewerTabWidget->clear();
//m_imageViewerTabWidget->clear();
ui.action_start_recording->setText(QString::fromLocal8Bit("停止采集"));
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(255,0,0);}");
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(255,0,0);}");
//应该先控制马达运动,当马达运动后,再开始光谱仪采集(发射开始采集信号)
m_Imager->setFileName2Save(imgPath);
@ -1136,7 +1195,7 @@ void HPPA::onStartRecordStep1()
if (m_RecordState % 2 == 1)
{
m_imageViewerTabWidget->clear();
//m_imageViewerTabWidget->clear();
m_Imager->setFileName2Save(imgPath);
m_Imager->setFrameNumber(this->frame_number->text().toInt());
@ -1145,7 +1204,7 @@ void HPPA::onStartRecordStep1()
m_tmc->run();
ui.action_start_recording->setText(QString::fromLocal8Bit("停止采集"));
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(255,0,0);}");
ui.mainToolBar->widgetForAction(ui.action_start_recording)->setStyleSheet("QWidget{background-color:rgb(255,0,0);}");
}
else
{
@ -1201,8 +1260,8 @@ void HPPA::onTabWidgetCurrentChanged(int index)//代码新建一个tab会调
QList<Mapcavas*> currentImageViewer = currentWidget->findChildren<Mapcavas*>();
//先disconnect然后再connect否则每次切换一次都会connect一次会累积connect很多次
disconnect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
connect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int, int)), this, SLOT(onLeftMouseButtonPressed(int, int)));
disconnect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int,int,QVector<double>,QVector<double>)), this, SLOT(onLeftMouseButtonPressed(int,int,QVector<double>,QVector<double>)));
connect(currentImageViewer[0], SIGNAL(leftMouseButtonPressed(int,int,QVector<double>,QVector<double>)), this, SLOT(onLeftMouseButtonPressed(int,int,QVector<double>,QVector<double>)));
}
void HPPA::onActionOpenDirectory()
@ -1259,8 +1318,11 @@ void HPPA::OnGainSliderChanged(double Gain)
ui.gain_lineEdit->setText(QString::number(Gain));
}
void HPPA::onLeftMouseButtonPressed(int x, int y)
void HPPA::onLeftMouseButtonPressed(int x, int y, QVector<double> wavelengths, QVector<double> spectrum)
{
Q_UNUSED(x);
Q_UNUSED(y);
try
{
//正在采集时,不能显示光谱曲线
@ -1269,62 +1331,29 @@ void HPPA::onLeftMouseButtonPressed(int x, int y)
return;
}
FileOperation* fileOperation = new FileOperation();
string directory = fileOperation->getDirectoryFromString();
string imgPath = directory + "\\" + m_FilenameLineEdit->text().toStdString() + "_" + std::to_string(m_TabWidgetCurrentIndex) + ".bil";
ImageReaderWriter* ImageReader = new ImageReaderWriter(imgPath.c_str());
if (x < 0 || x>ImageReader->getXCount() || y<0 || y>ImageReader->getyCount() - 1)
if (spectrum.isEmpty())
{
return;
}
float* data = ImageReader->ReadImage(x, y, 1, 1);
QLineSeries* series = new QLineSeries();
//series->clear();//////////////////////////////
QString imagerSelected = mImagerGroup->checkedAction()->objectName();
if (imagerSelected == "mActionPica_L" || imagerSelected == "mActionCorning_410" || imagerSelected == "mActionPika_XC2")
const int count = qMin(wavelengths.size(), spectrum.size());
if (count > 0)
{
int start = m_Imager->getStartBand();
for (size_t i = 0; i < m_Imager->getBandCount() - 1; i++)
for (int i = 0; i < count; ++i)
{
//malloc申请的内存用法1可以当做数组用
series->append(m_Imager->getWavelengthAtBand(i + start), data[i]);
////malloc申请的内存用法2指针存取
//series->append(m_Imager->getWavelengthAtBand(i), *data);
//data++;
}
}
else if (imagerSelected == "mActionPica_NIR")
{
int start = 0;
for (size_t i = 0; i < m_Imager->getBandCount() - 1; i++)
{
//malloc申请的内存用法1可以当做数组用
series->append(m_Imager->getWavelengthAtBand(i + start), data[i]);
////malloc申请的内存用法2指针存取
//series->append(m_Imager->getWavelengthAtBand(i), *data);
//data++;
series->append(wavelengths[i], spectrum[i]);
}
}
else
{
QMessageBox msgBox;
msgBox.setText(QString::fromLocal8Bit("请选择相机!"));
msgBox.exec();
return;
for (int i = 0; i < spectrum.size(); ++i)
{
series->append(i, spectrum[i]);
}
}
series->setPen(QPen(QColor("#FF928A"), 2));
m_chart->removeAllSeries();
@ -1335,11 +1364,6 @@ void HPPA::onLeftMouseButtonPressed(int x, int y)
QValueAxis* axisY = qobject_cast<QValueAxis*>(m_chart->axisY());
setAxis(axisX, axisY);
/*std::cout << "x坐标:" << x << std::endl;
std::cout << "y坐标:" << y << std::endl;
std::cout << "文件名:" << imgPath << std::endl;*/
}
catch (const std::exception&)
{

View File

@ -18,6 +18,7 @@
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QVector>
#include "ui_HPPA.h"
#include "resononImager.h"
@ -174,6 +175,7 @@ public:
~HPPA();
static HPPA* instance();
LayerTreeNode* rasterGroupNode() const;
void CalculateIntegratioinTimeRange();//通过帧率计算积分时间范围设置slider最大值
@ -294,41 +296,42 @@ public Q_SLOTS:
void onActionOpenDirectory();
void OnFramerateLineeditEditingFinished();//
void OnFramerateSliderChanged(double framerate);//
void OnFramerateSliderChanged(double framerate);//
void OnIntegratioinTimeEditingFinished();//
void OnIntegratioinTimeSliderChanged(double IntegratioinTime);//
void OnGainEditingFinished();//
void OnGainSliderChanged(double Gain);//
void OnIntegratioinTimeEditingFinished();//
void OnIntegratioinTimeSliderChanged(double IntegratioinTime);//
void OnGainEditingFinished();//
void OnGainSliderChanged(double Gain);//
void onLeftMouseButtonPressed(int x, int y);//点击影像像元显示光谱
void setAxis(QValueAxis* axisX, QValueAxis* axisY);
void onLeftMouseButtonPressed(int x, int y, QVector<double> wavelengths, QVector<double> spectrum);//点击影像像元显示光谱
void setAxis(QValueAxis* axisX, QValueAxis* axisY);
void timerEvent(QTimerEvent *event);
//
void onimagerSimulatorMove(int x, int y);
void OnSendLogToCallClass(QString str);
void timerEvent(QTimerEvent *event);
//
void onimagerSimulatorMove(int x, int y);
void OnSendLogToCallClass(QString str);
void onPlotRgbImage();
void onCloseRgbCamera();
void onClearLabel();
void onPlotRgbImage();
void onCloseRgbCamera();
void onClearLabel();
void onCopyFinished();
void onCopyFinished();
void requestFinished(QNetworkReply* reply);
void requestFinished(QNetworkReply* reply);
void recordFromRobotArm(int fileCounter);
void recordFromRobotArm(int fileCounter);
void createOneMotorScenario();
void createPlantPhenotypeScenario();
void onCreated3DModelPlantPhenotype();
void onCreated3DModelOneMotor();
void createOneMotorScenario();
void createPlantPhenotypeScenario();
void onCreated3DModelPlantPhenotype();
void onCreated3DModelOneMotor();
void onImageFileSaved(QString path, int fileIndex);
void onImageFileSaved(QString path, int fileIndex);
void onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex);
void removeLayerByTreeIndex();
void onLayerCreatedFromFile(const QString& baseName, const QString& filePath, int fileIndex);
void removeLayerByTreeIndex();
void removeAllLayersInRasterGroup();
signals:
void StartFocusSignal();

View File

@ -1,5 +1,6 @@
#include "stdafx.h"
#include <iostream>
#include <cmath>
#include <QWheelEvent>
#include <QPoint>
@ -130,7 +131,19 @@ void Mapcavas::mousePressEvent(QMouseEvent *event)
m_bMouseTranslate = true;
m_lastMousePos = event->pos();
emit leftMouseButtonPressed(mapToScene(m_lastMousePos).x(), mapToScene(m_lastMousePos).y());
const QPointF scenePt = mapToScene(m_lastMousePos);
const int x = static_cast<int>(std::floor(scenePt.x()));
const int y = static_cast<int>(std::floor(scenePt.y()));
if (m_rasterLayer && m_rasterLayer->isValidPixel(x, y))
{
QVector<double> wavelengths;
QVector<double> spectrum;
if (m_rasterLayer->readPixelSpectrum(x, y, wavelengths, spectrum))
{
emit leftMouseButtonPressed(x, y, wavelengths, spectrum);
}
}
}
QGraphicsView::mousePressEvent(event);
}

View File

@ -3,6 +3,7 @@
#include "QGraphicsView"
#include "qlabel.h"
#include <QVector>
class RasterLayer; // forward
@ -62,6 +63,6 @@ private:
signals:
void leftMouseButtonPressed(int, int);
void leftMouseButtonPressed(int, int, QVector<double>, QVector<double>);
};
#endif

View File

@ -24,7 +24,9 @@ void LayerTreeView::contextMenuEvent(QContextMenuEvent* event)
return;
const QModelIndex idx = indexAt(event->pos());
if (!idx.isValid())
if (idx.isValid())
setCurrentIndex(idx);
else
setCurrentIndex(QModelIndex());
QMenu* menu = m_menuProvider->createContextMenu();

View File

@ -17,20 +17,37 @@ QMenu* LayerTreeViewMenuProvider::createContextMenu()
QMenu* menu = new QMenu();
// Always allow remove when index is valid and points to a Layer
if (m_contextIndex.isValid())
if (!m_contextIndex.isValid())
{
// determine node type by looking at model's node
const LayerTreeModel* model = static_cast<const LayerTreeModel*>(m_contextIndex.model());
if (model)
return menu;
}
const LayerTreeModel* model = static_cast<const LayerTreeModel*>(m_contextIndex.model());
if (!model)
{
return menu;
}
LayerTreeNode* node = static_cast<LayerTreeNode*>(m_contextIndex.internalPointer());
if (!node)
{
return menu;
}
if (node->type() == LayerTreeNode::Type::Layer)
{
QAction* removeAction = new QAction(QStringLiteral("<EFBFBD>Ƴ<EFBFBD>ͼ<EFBFBD><EFBFBD>"), menu);
connect(removeAction, &QAction::triggered, HPPA::instance(), &HPPA::removeLayerByTreeIndex);
menu->addAction(removeAction);
}
else if (node->type() == LayerTreeNode::Type::Group)
{
HPPA* app = HPPA::instance();
if (app && node == app->rasterGroupNode())
{
LayerTreeNode* node = static_cast<LayerTreeNode*>(m_contextIndex.internalPointer());
if (node && node->type() == LayerTreeNode::Type::Layer)
{
QAction* removeAction = new QAction(QStringLiteral("<EFBFBD>Ƴ<EFBFBD>ͼ<EFBFBD><EFBFBD>"), menu);
connect(removeAction, &QAction::triggered, HPPA::instance(), &HPPA::removeLayerByTreeIndex);
menu->addAction(removeAction);
}
QAction* removeAllAction = new QAction(QStringLiteral("<EFBFBD>Ƴ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͼ<EFBFBD><EFBFBD>"), menu);
connect(removeAllAction, &QAction::triggered, app, &HPPA::removeAllLayersInRasterGroup);
menu->addAction(removeAllAction);
}
}

View File

@ -1,6 +1,9 @@
#include "RasterDataProvider.h"
#include <QString>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QRegularExpression>
#if HPPA_HAVE_GDAL
#include <gdal_priv.h>
@ -79,27 +82,126 @@ int RasterDataProvider::height() const
#endif
}
bool RasterDataProvider::isValidPixel(int x, int y) const
{
const int w = width();
const int h = height();
return x >= 0 && y >= 0 && x < w && y < h;
}
std::vector<double> RasterDataProvider::parseEnviHdrWavelengths() const
{
std::vector<double> res;
QFileInfo fi(m_uri);
QString hdrPath = fi.path() + "/" + fi.completeBaseName() + ".hdr";
QFile hdr(hdrPath);
if (!hdr.open(QIODevice::ReadOnly | QIODevice::Text)) {
return res;
}
QString text = QString::fromLocal8Bit(hdr.readAll());
hdr.close();
QRegularExpression rx("wavelength\\s*=\\s*\\{([^}]*)\\}", QRegularExpression::CaseInsensitiveOption | QRegularExpression::DotMatchesEverythingOption);
QRegularExpressionMatch m = rx.match(text);
if (!m.hasMatch()) {
return res;
}
const QString body = m.captured(1);
const QStringList parts = body.split(',', QString::SkipEmptyParts);
res.reserve(parts.size());
for (const QString& p : parts) {
bool ok = false;
double v = p.trimmed().toDouble(&ok);
if (ok) {
res.push_back(v);
}
}
return res;
}
std::vector<double> RasterDataProvider::bandWavelengths() const
{
std::vector<double> res;
#if HPPA_HAVE_GDAL
if (!m_dataset) return res;
// 1) Try ENVI dataset-level metadata first: wavelength = { ... }
const char* dsWave = m_dataset->GetMetadataItem("wavelength", "ENVI");
if (!dsWave) dsWave = m_dataset->GetMetadataItem("Wavelength", "ENVI");
if (dsWave) {
QString dsWaveStr = QString::fromLocal8Bit(dsWave);
dsWaveStr.remove('{').remove('}');
const QStringList parts = dsWaveStr.split(',', QString::SkipEmptyParts);
res.reserve(parts.size());
for (const QString& p : parts) {
bool ok = false;
double v = p.trimmed().toDouble(&ok);
if (ok) res.push_back(v);
}
if (!res.empty()) return res;
}
// 2) Try per-band metadata
for (int i = 1; i <= m_dataset->GetRasterCount(); ++i) {
GDALRasterBand* band = m_dataset->GetRasterBand(i);
if (!band) continue;
// Attempt to read wavelength from metadata (commonly "Wavelength" or "wavelength")
const char* val = band->GetMetadataItem("Wavelength");
if (!val) val = band->GetMetadataItem("wavelength");
if (val) {
double v = atof(val);
res.push_back(v);
bool ok = false;
double v = QString::fromLocal8Bit(val).trimmed().toDouble(&ok);
res.push_back(ok ? v : -1.0);
} else {
// push a negative to indicate unknown
res.push_back(-1.0);
}
}
if (!res.empty()) return res;
#endif
// 3) Fallback: parse ENVI .hdr directly
return parseEnviHdrWavelengths();
}
bool RasterDataProvider::readPixelSpectrum(int x, int y, std::vector<double>& outSpectrum) const
{
#if HPPA_HAVE_GDAL
outSpectrum.clear();
if (!m_dataset) return false;
if (!isValidPixel(x, y)) return false;
const int bands = m_dataset->GetRasterCount();
if (bands <= 0) return false;
outSpectrum.resize(bands);
for (int i = 0; i < bands; ++i) {
GDALRasterBand* band = m_dataset->GetRasterBand(i + 1);
if (!band) return false;
float value = 0.0f;
CPLErr err = band->RasterIO(
GF_Read,
x, y,
1, 1,
&value,
1, 1,
GDT_Float32,
0, 0);
if (err != CE_None) return false;
outSpectrum[i] = static_cast<double>(value);
}
return true;
#else
Q_UNUSED(x);
Q_UNUSED(y);
Q_UNUSED(outSpectrum);
return false;
#endif
return res;
}
bool RasterDataProvider::readBandAsFloat(int bandIndex, std::vector<float>& outBuffer) const

View File

@ -24,9 +24,14 @@ public:
int width() const;
int height() const;
bool isValidPixel(int x, int y) const;
// Returns per-band wavelength metadata if available. If not available, returns empty vector.
std::vector<double> bandWavelengths() const;
// Read spectrum of one pixel (x,y) across all bands.
bool readPixelSpectrum(int x, int y, std::vector<double>& outSpectrum) const;
// Read a single band (0-based index) into a float buffer of size width()*height().
// Returns true on success.
bool readBandAsFloat(int bandIndex, std::vector<float>& outBuffer) const;
@ -35,6 +40,7 @@ public:
private:
QString m_uri;
std::vector<double> parseEnviHdrWavelengths() const;
#if HPPA_HAVE_GDAL
GDALDataset* m_dataset = nullptr;
#else

View File

@ -36,6 +36,40 @@ bool RasterLayer::openDataProvider()
return ok;
}
bool RasterLayer::isValidPixel(int x, int y)
{
if (!m_provider) {
if (!openDataProvider()) return false;
}
return m_provider->isValidPixel(x, y);
}
bool RasterLayer::readPixelSpectrum(int x, int y, QVector<double>& wavelengths, QVector<double>& spectrum)
{
if (!m_provider) {
if (!openDataProvider()) return false;
}
std::vector<double> wl;
std::vector<double> sp;
if (!m_provider->readPixelSpectrum(x, y, sp)) return false;
wl = m_provider->bandWavelengths();
wavelengths = QVector<double>::fromStdVector(wl);
spectrum = QVector<double>::fromStdVector(sp);
if (wavelengths.size() != spectrum.size()) {
wavelengths.resize(spectrum.size());
for (int i = 0; i < wavelengths.size(); ++i) {
wavelengths[i] = i;
}
}
return true;
}
QImage RasterLayer::render(const RenderParams& params)
{
if (!m_provider) {

View File

@ -3,6 +3,7 @@
#include "MapLayer.h"
#include <memory>
#include <QImage>
#include <QVector>
class RasterDataProvider;
class RasterRenderer;
@ -23,12 +24,15 @@ public:
// Create or open provider based on this layer's uri
bool openDataProvider();
bool isValidPixel(int x, int y);
bool readPixelSpectrum(int x, int y, QVector<double>& wavelengths, QVector<double>& spectrum);
struct RenderParams {
double rWave = 665.0; // default wavelengths (nm)
double gWave = 560.0;
double bWave = 490.0;
double minValue = 0.0; // optional stretch
double maxValue = 255.0;
double maxValue = 4095.0;
};
// Render the raster using current provider and renderer. Returns an empty QImage on failure.