873 lines
27 KiB
C++
873 lines
27 KiB
C++
#include "dicomimageview.h"
|
||
|
||
#include <QMessageBox>
|
||
#include <QDebug>
|
||
#include <QCursor>
|
||
|
||
#include <vtkRenderer.h>
|
||
#include <vtkCamera.h>
|
||
#include <vtkInteractorStyleImage.h>
|
||
#include <QVTKOpenGLNativeWidget.h>
|
||
#include <vtkGenericOpenGLRenderWindow.h>
|
||
#include <vtkDiscretizableColorTransferFunction.h>
|
||
#include <vtkImageProperty.h>
|
||
|
||
#include "Interaction/ActorDraggableInteractorStyle.h"
|
||
#include "UI/Window/metaDataWindow.h"
|
||
#include "UI/Widget/Component/mytitlebar.h"
|
||
#include "UI/Widget/cine/pqVCRController.h"
|
||
#include "UI/Widget/cine/pqVCRToolbar.h"
|
||
#include "UI/Widget/Thumbnail/thumbnailImage.h"
|
||
#include "dcmtk/dcmdata/dcfilefo.h"
|
||
|
||
//-----------------------------------------------------------------------------
|
||
DicomImageView::DicomImageView(QWidget *parent)
|
||
: QFrame(parent), mGLRenWin(vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New()), mImageViewer(nullptr),
|
||
mSeries(nullptr), mScrollBar(new ClickableScrollBar(Qt::Orientation::Vertical, this)),
|
||
mTitleBar(createMyTitleBar()), mGLWidget(new QVTKOpenGLNativeWidget), mVcrToolbar(nullptr), mVcrController(nullptr),
|
||
mOverlayView(nullptr), mBaseView(nullptr), mTableName("gray"),mIsCine(false), mIsNegative(false),
|
||
mIsOverlay(false), mIsSlotInited(false),mCurrentRAngle(0)
|
||
{
|
||
//main container
|
||
QFrame *wrapper = new QFrame(this);
|
||
wrapper->setObjectName("wrapper");
|
||
|
||
QGridLayout *view_layout = new QGridLayout(this);
|
||
|
||
//add container and title bar
|
||
view_layout->addWidget(mTitleBar, 0, 0);
|
||
view_layout->addWidget(wrapper, 1, 0);
|
||
view_layout->setContentsMargins(0, 0, 0, 0);
|
||
view_layout->setSpacing(0);
|
||
this->setLayout(view_layout);
|
||
|
||
// create layout for main container
|
||
QGridLayout *controlLayout = new QGridLayout(wrapper);
|
||
mGLWidget->setParent(wrapper);
|
||
mGLWidget->setRenderWindow(mGLRenWin.Get());
|
||
controlLayout->addWidget(mGLWidget, 0, 0);
|
||
|
||
//add scrollbar to container
|
||
mScrollBar->setSingleStep(1);
|
||
mScrollBar->setFocusPolicy(Qt::StrongFocus);
|
||
mScrollBar->setVisible(false);
|
||
mScrollBar->setObjectName("scrollbar");
|
||
controlLayout->addWidget(mScrollBar, 0, 1);
|
||
connect(mScrollBar, &ClickableScrollBar::clicked, this, &DicomImageView::clicked);
|
||
|
||
//config container UI
|
||
controlLayout->setContentsMargins(0, 0, 0, 0);
|
||
controlLayout->setSpacing(0);
|
||
wrapper->setLayout(controlLayout);
|
||
setAutoFillBackground(true);
|
||
|
||
//config self
|
||
QWidget::setAcceptDrops(true);
|
||
this->setObjectName("dicomview");
|
||
}
|
||
|
||
DicomImageView::~DicomImageView() {
|
||
if (mImageViewer) {
|
||
mImageViewer->Delete();
|
||
mImageViewer = nullptr;
|
||
}
|
||
mVcrControlThread.quit();//event loop
|
||
mVcrControlThread.wait(); //wait until return,block mode
|
||
if (mVcrToolbar) {
|
||
mVcrToolbar->deleteLater();
|
||
}
|
||
}
|
||
|
||
// layout about---------------------------------------------------------------
|
||
MyTitleBar *DicomImageView::createMyTitleBar() {
|
||
MyTitleBar *titleBar = new MyTitleBar(this);
|
||
connect(titleBar, &MyTitleBar::signalButtonMaxClicked, this, &DicomImageView::doubleClicked);
|
||
connect(titleBar, &MyTitleBar::signalButtonCloseClicked, this, &DicomImageView::viewCleared);
|
||
return titleBar;
|
||
}
|
||
|
||
void DicomImageView::initScrollbar() {
|
||
//_MinSlice = mImageViewer->GetSliceMin();
|
||
//_MaxSlice = mImageViewer->GetSliceMax();
|
||
mScrollBar->setValue(mImageViewer->GetSliceMin());
|
||
mScrollBar->setMaximum(mImageViewer->GetSliceMax());
|
||
// mScrollBar->setSingleStep(1);
|
||
mScrollBar->setPageStep(1);
|
||
mScrollBar->setVisible(true);
|
||
if (!mIsSlotInited) {
|
||
connect(mScrollBar, &QScrollBar::valueChanged, this, &DicomImageView::scrollBarValueChanged);
|
||
connect(this, &DicomImageView::onTransform, this, &DicomImageView::imageTransformed);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::initImageViewer() {
|
||
if (!mImageViewer) {
|
||
mImageViewer = DICOMImageViewer::New();
|
||
mImageViewer->SetViewID(this->mViewID);
|
||
mImageViewer->SetRenderWindow(mGLRenWin);
|
||
mImageViewer->SetupInteractor(mGLRenWin->GetInteractor());
|
||
}
|
||
}
|
||
|
||
void DicomImageView::initImageViewerFirstTime()
|
||
{
|
||
if (!mImageViewer) {
|
||
mImageViewer = DICOMImageViewer::New();
|
||
mImageViewer->SetViewID(this->mViewID);
|
||
mImageViewer->SetRenderWindow(mGLRenWin);
|
||
mImageViewer->SetupInteractor(mGLRenWin->GetInteractor());
|
||
mGLRenWin->GetInteractor()->RemoveAllObservers();
|
||
mImageViewer->Delete();
|
||
mImageViewer = nullptr;
|
||
mIsSlotInited = false;
|
||
}
|
||
}
|
||
|
||
void DicomImageView::setHighlight(bool yes) {
|
||
mTitleBar->SetHighlight(yes);
|
||
}
|
||
|
||
// action----------------------------------------------------------------------
|
||
void DicomImageView::SyncScrollBar() {
|
||
mScrollBar->SetValueSilently(mImageViewer->GetSlice());
|
||
}
|
||
|
||
void DicomImageView::setViewID(uint aID)
|
||
{
|
||
mViewID = aID;
|
||
}
|
||
|
||
void DicomImageView::resetView()
|
||
{
|
||
if (hasSeries()) {
|
||
unloadFusion();
|
||
removeViewWithMeasure();
|
||
mImageViewer->GetRenderer()->RemoveAllViewProps();
|
||
this->render();
|
||
|
||
mImageViewer->Delete();
|
||
mImageViewer = nullptr;
|
||
mIsSlotInited = false;
|
||
//don't delete series!!! It's belong to data cache
|
||
mSeries = nullptr;
|
||
}
|
||
mScrollBar->setVisible(false);
|
||
if (mIsCine)
|
||
mVcrToolbar->setVisible(false);
|
||
|
||
}
|
||
|
||
bool DicomImageView::hasSeries() {
|
||
return mSeries;
|
||
}
|
||
|
||
void DicomImageView::RenderReloadMeasure()
|
||
{
|
||
if (!hasSeries()) return;
|
||
mImageViewer->RenderReloadMeasure();
|
||
}
|
||
|
||
void DicomImageView::loadSeries(SeriesImageSet *series) {
|
||
if (!series) return;
|
||
initImageViewer();
|
||
mSeries = series;
|
||
QString age = series->GetProperty()->GetPatientAge();
|
||
age = age.isEmpty()?"":QString("%1%2").arg(age.left(age.length()-1).toInt()).arg(age.back());
|
||
QString date = series->GetProperty()->GetAcquisitionDate();
|
||
date = date.length()<8?"":
|
||
QString("%1/%2/%3").arg(date.left(4), date.mid(4,2), date.right(2));
|
||
QString time = series->GetProperty()->GetAcquisitionTime();
|
||
time = time.length()<6?"": QString("%1:%2:%3").arg(time.left(2), time.mid(2,2), time.mid(4,2));
|
||
mTitleBar->setTitleContent(QString("%1 (%2) - %3 %4 - %5")
|
||
.arg(series->GetProperty()->GetPatientName())
|
||
.arg(age)
|
||
.arg(date)
|
||
.arg(time)
|
||
.arg(series->GetProperty()->GetSeriesDescription()));
|
||
mImageViewer->SetInputData(mSeries->GetData());
|
||
setColorTable("gray");
|
||
mIsFirstRenderAfterLoad = true;
|
||
if(mSeries->GetOverlayData())
|
||
{
|
||
mImageViewer->SetOverlayInputData(mSeries->GetOverlayData());
|
||
}
|
||
mImageViewer->SetCoordsTransformMatrix(series->GetProperty());
|
||
mImageViewer->InitCornerInfo(series->GetProperty());
|
||
mImageViewer->SetupImageViewer();
|
||
mImageViewer->UpdateOrientationInfo();
|
||
//以下是一些转接函数
|
||
//使用connect 替代 AddObserver,避免出现多种事件机制架构
|
||
if (!mIsSlotInited) {
|
||
connect(mImageViewer->GetSignalRaiser(), &vtkSignalRaiser::raiseEvent, this, &DicomImageView::dispatchEvent);
|
||
//目前 替换了一部分包括SlicedEvent,EndDollyEvent,EndWindowLevelEvent,EndPanEvent,主要关联到sync
|
||
|
||
ActorDraggableInteractorStyle *style = mImageViewer->GetInteractorStyle();
|
||
style->AddObserver(AfterViewerClicked, this, &DicomImageView::clicked);
|
||
style->AddObserver(vtkCommand::WindowLevelEvent, this, &DicomImageView::windowLevelHandle);
|
||
style->AddObserver(DoubleClickEvent, this, &DicomImageView::doubleClickHandle);
|
||
style->AddObserver(ScalarOpacityEvent, this, &DicomImageView::scalarEventHandle);
|
||
style->AddObserver(ScalarShiftEvent, this, &DicomImageView::scalarEventHandle);
|
||
|
||
}
|
||
initScrollbar();
|
||
mIsSlotInited = true;
|
||
}
|
||
|
||
int DicomImageView::getSeriesNumber() {
|
||
if (hasSeries()) {
|
||
return mSeries->GetSeriesNumber();
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
void DicomImageView::showMetaData() {
|
||
|
||
QString fileName = QString::fromLatin1(this->mSeries->getCurImageName(mImageViewer->GetSlice()));
|
||
if (!fileName.isEmpty()) {
|
||
DcmFileFormat dcmFile;
|
||
if (!dcmFile.loadFile(fileName.toStdString()).good()) {
|
||
QMessageBox::warning(this,
|
||
tr("Error"),
|
||
tr("Unable to read file %1").arg(fileName),
|
||
QMessageBox::Ok);
|
||
return;
|
||
}
|
||
metaDataWindow *dicomWindow = new metaDataWindow(&dcmFile);
|
||
dicomWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||
dicomWindow->setWindowTitle("DICOM Tags (" + fileName + ")");
|
||
dicomWindow->setWindowFilePath(fileName);
|
||
dicomWindow->resize(USER_CONFIG::DEFAULT_TAG_WINDOW_SIZE);
|
||
dicomWindow->show();
|
||
|
||
}
|
||
}
|
||
|
||
//SLOTS------------------------------------------------------------------------
|
||
void DicomImageView::scrollBarValueChanged(int slice) {
|
||
mImageViewer->SetSlice(slice);
|
||
}
|
||
|
||
void DicomImageView::viewCleared() {
|
||
emit beforeViewCleared(this);
|
||
resetView();
|
||
mTitleBar->setTitleContent("");
|
||
emit afterViewCleared(this);
|
||
}
|
||
|
||
void DicomImageView::doubleClicked() {
|
||
emit onViewDoubleClick(this);
|
||
}
|
||
|
||
void DicomImageView::fusionWindowChanged(double level, double width) {
|
||
if (isFusion()) {
|
||
mImageViewer->SetFusionColorLeveL(level);
|
||
mImageViewer->SetFusionColorWindow(width);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::imageTransformed() {
|
||
if (hasSeries()){
|
||
mImageViewer->UpdateOrientationInfo();
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::clicked() {
|
||
emit onViewClick(this);
|
||
}
|
||
|
||
//Widget event----------------------------------------------------------------
|
||
void DicomImageView::wheelEvent(QWheelEvent *event) {
|
||
if (hasSeries()) {
|
||
if (MeasureHelper::measuring) return;
|
||
int _Slice = mImageViewer->GetSlice();
|
||
int _MinSlice = mImageViewer->GetSliceMin();
|
||
int _MaxSlice = mImageViewer->GetSliceMax();
|
||
|
||
if (event->delta() > 0) {
|
||
if (_Slice > _MinSlice) {
|
||
_Slice -= 1;
|
||
mImageViewer->SetSlice(_Slice);
|
||
} else {
|
||
_Slice = _MinSlice;
|
||
mImageViewer->SetSlice(_Slice);
|
||
}
|
||
} else {
|
||
if (_Slice < _MaxSlice) {
|
||
_Slice += 1;
|
||
mImageViewer->SetSlice(_Slice);
|
||
} else {
|
||
_Slice = _MaxSlice;
|
||
mImageViewer->SetSlice(_Slice);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void DicomImageView::mousePressEvent(QMouseEvent *event) {
|
||
|
||
emit onViewClick(this);
|
||
}
|
||
|
||
void DicomImageView::resizeEvent(QResizeEvent *event) {
|
||
|
||
//auto size conner info
|
||
if (!mImageViewer) {
|
||
QWidget::resizeEvent(event);
|
||
if(mfirstShowFlag){
|
||
initImageViewerFirstTime();
|
||
mfirstShowFlag= false;
|
||
}
|
||
return;
|
||
}
|
||
QWidget::resizeEvent(event);
|
||
|
||
mImageViewer->ResetZoomScaleToFitWindowSize();
|
||
mImageViewer->Render();
|
||
if (mIsCine) {
|
||
int ax = (this->geometry().bottomLeft().x() + this->geometry().bottomRight().x()) / 2 +
|
||
VCRHelper::getVCRXOffset();
|
||
int ay = (this->geometry().bottomLeft().y() + this->geometry().bottomRight().y()) / 2 +
|
||
VCRHelper::getVCRYOffset();
|
||
mVcrToolbar->move(ax, ay);
|
||
}
|
||
}
|
||
|
||
// DND support-----------------------------------------------------------------
|
||
void DicomImageView::dragEnterEvent(QDragEnterEvent *e) {
|
||
if (e->mimeData()->hasFormat("text/plain")) {
|
||
e->acceptProposedAction();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::dragMoveEvent(QDragMoveEvent *e) {
|
||
if (e->mimeData()->hasFormat("text/plain")) {
|
||
e->acceptProposedAction();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::dropEvent(QDropEvent *e) {
|
||
if (e->mimeData()->hasFormat("text/plain")) {
|
||
e->acceptProposedAction();
|
||
thumbnailImage *tb = qobject_cast<thumbnailImage *>(
|
||
(QObject *) (e->mimeData()->text().toULongLong()));
|
||
if (tb) {
|
||
emit onDragDrop(this, tb->getSeriesInfo()->GetUniqueID());
|
||
}
|
||
}
|
||
}
|
||
|
||
void DicomImageView::dragLeaveEvent(QDragLeaveEvent *) {
|
||
return;
|
||
}
|
||
|
||
//Fusion about -------------------------------------------------------------
|
||
bool DicomImageView::isFusion() {
|
||
if (!mImageViewer) return false;
|
||
return mImageViewer->GetFusion();
|
||
|
||
}
|
||
|
||
void DicomImageView::setFusionInput(DicomImageView *overlay) {
|
||
mOverlayView = overlay;
|
||
|
||
vtkImageData *overlay_data = mOverlayView->getSeriesInstance()->GetData();
|
||
|
||
double window;
|
||
double level;
|
||
mOverlayView->getWindowLevel(level, window);
|
||
|
||
mImageViewer->FusionOn();
|
||
mOverlayView->OverlayOn();
|
||
mOverlayView->SetBaseView(this);
|
||
|
||
mImageViewer->SetFusionInputData(overlay_data, overlay->getSeriesInstance()->GetProperty()->GetModelToWorldMatrix());
|
||
mImageViewer->SetFusionColorLeveL(level);
|
||
mImageViewer->SetFusionColorWindow(window);
|
||
mImageViewer->SetFusionOpacity(mImageViewer->GetFusionOpacity());
|
||
|
||
// Example for vtkDiscretizableColorTransferFunction
|
||
vtkNew<vtkDiscretizableColorTransferFunction> table;
|
||
// example table1 d->r->g->w
|
||
table->AddRGBPoint(0.0, 0.0, 0.0, 0.0);
|
||
table->AddRGBPoint(0.33, 1.0, 0.0, 0.0);
|
||
table->AddRGBPoint(0.66, 1.0, 1.0, 0.0);
|
||
table->AddRGBPoint(1.0, 1.0, 1.0, 1.0);
|
||
mImageViewer->SetFusionColorTable(table);
|
||
connect(mOverlayView, &DicomImageView::onFusionWindowChange,
|
||
this, &DicomImageView::fusionWindowChanged);
|
||
}
|
||
|
||
DicomImageView * DicomImageView::getFusionInput(){
|
||
return mOverlayView;
|
||
}
|
||
|
||
void DicomImageView::setFusionOpacity(double percent) {
|
||
if (isFusion()) {
|
||
mImageViewer->IncreFusionOpacity(percent);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Remove Fusion no matter it is base or overlay
|
||
*/
|
||
void DicomImageView::unloadFusion() {
|
||
if (hasSeries()) {
|
||
if (isFusion()) {
|
||
disconnect(mOverlayView, &DicomImageView::onFusionWindowChange,
|
||
this, &DicomImageView::fusionWindowChanged);
|
||
|
||
this->clearFusionContext();
|
||
mOverlayView->SetBaseView(nullptr);
|
||
mOverlayView = nullptr;
|
||
}
|
||
|
||
if (IsOverlay()) {
|
||
disconnect(this, &DicomImageView::onFusionWindowChange,
|
||
mBaseView, &DicomImageView::fusionWindowChanged);
|
||
|
||
mBaseView->clearFusionContext();
|
||
mBaseView->SetOverlayView(nullptr);
|
||
mBaseView = nullptr;
|
||
}
|
||
}
|
||
}
|
||
|
||
void DicomImageView::clearFusionContext() {
|
||
mImageViewer->RemoveFusionData();
|
||
mImageViewer->FusionOff();
|
||
mImageViewer->Render();
|
||
mOverlayView->OverlayOff();
|
||
}
|
||
|
||
//Callbacks------------------------------------------------------------------------------------
|
||
void DicomImageView::windowLevelHandle() {
|
||
mImageViewer->UpdateCornerInfo(BOTTOM_RIGHT);
|
||
mImageViewer->Render();
|
||
emit onFusionWindowChange(mImageViewer->GetColorLevel(), mImageViewer->GetColorWindow());
|
||
}
|
||
|
||
void DicomImageView::scalarEventHandle(vtkObject *, unsigned long eventId, void *calldata) {
|
||
double *r = (double *) calldata;
|
||
switch (eventId) {
|
||
case (ScalarShiftEvent):
|
||
qDebug() << "ScalarShiftEvent";
|
||
mImageViewer->SwitchToNextPreset();
|
||
|
||
break;
|
||
case (ScalarOpacityEvent):
|
||
qDebug() << "ScalarOpacityEvent" << r[0];
|
||
setFusionOpacity(r[0]);
|
||
break;
|
||
default:
|
||
|
||
break;
|
||
}
|
||
}
|
||
|
||
void DicomImageView::doubleClickHandle() {
|
||
emit onViewDoubleClick(this);
|
||
}
|
||
|
||
void DicomImageView::dispatchEvent(vtkObject *, unsigned long eid, void *callData) {
|
||
|
||
int *r = (int *) callData;
|
||
switch (eid) {
|
||
case (vtkCommand::EventIds::EndPanEvent):{
|
||
emit onEndPan(this, callData);
|
||
break;
|
||
}
|
||
case (vtkCommand::EventIds::EndWindowLevelEvent):{
|
||
//update corner info through callback
|
||
emit onEndWindowLevel(this,
|
||
mImageViewer->GetColorLevel(),
|
||
mImageViewer->GetColorWindow());
|
||
break;
|
||
}
|
||
case (DraggableStyleEvents::EndDollyEvent):{
|
||
emit onEndZoom(this, (double *) callData);
|
||
break;
|
||
}
|
||
case (DraggableStyleEvents::SlicedEvent): {
|
||
mScrollBar->SetValueSilently(mImageViewer->GetSlice());
|
||
//invoke event
|
||
emit onSlice(this, callData);
|
||
break;
|
||
}
|
||
case (DraggableStyleEvents::StartSyncSlicePointEvent): {
|
||
this->setCursor(Qt::BlankCursor);
|
||
this->mImageViewer->Render();
|
||
//invoke event
|
||
break;
|
||
}
|
||
case (DraggableStyleEvents::SyncSlicePointEvent): {
|
||
double * p = (double*)callData;
|
||
double focalPoint[3]={0};
|
||
focalPoint[0] = p[0];
|
||
focalPoint[1] = p[1];
|
||
focalPoint[2] = p[2];
|
||
mImageViewer->TransformPointM2W(focalPoint);
|
||
//invoke event
|
||
emit onSyncPointSlice(this, focalPoint);
|
||
break;
|
||
}
|
||
case (DraggableStyleEvents::EndSyncSlicePointEvent): {
|
||
this->setCursor(Qt::ArrowCursor);
|
||
//invoke event
|
||
emit onSyncPointSlice(this, nullptr);
|
||
break;
|
||
}
|
||
default:
|
||
break;
|
||
}
|
||
}
|
||
|
||
//Image render & operation about--------------------------------------------------------------------------------------
|
||
void DicomImageView::render() {
|
||
if (hasSeries()) {
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
int DicomImageView::getReconPlane() {
|
||
return mImageViewer->GetReconPlane();
|
||
}
|
||
|
||
void DicomImageView::setReconPlane(int plane) {
|
||
if (mImageViewer->GetReconPlane() == plane) return;
|
||
unloadFusion();
|
||
|
||
mImageViewer->SetReconPlane(plane);
|
||
int max = 0;
|
||
int min = 0;
|
||
mImageViewer->GetSliceRange(min, max);
|
||
mScrollBar->setValue(min);
|
||
mScrollBar->setMaximum(max);
|
||
mScrollBar->setValue(0);
|
||
}
|
||
|
||
void DicomImageView::setSlice(int slice) {
|
||
if (mImageViewer == nullptr) {
|
||
return;
|
||
}
|
||
if (hasSeries()) {
|
||
mImageViewer->SetSlice(slice);
|
||
}
|
||
}
|
||
int DicomImageView::getSlice(){
|
||
return mImageViewer->GetSlice();
|
||
}
|
||
|
||
void DicomImageView::addSlice(int step) {
|
||
if (mImageViewer == nullptr) {
|
||
return;
|
||
}
|
||
if (hasSeries()) {
|
||
int curSlice = mImageViewer->GetSlice() + step;
|
||
mImageViewer->SetSlice(curSlice);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::setZoomScale(double scale) {
|
||
if (hasSeries()) {
|
||
mImageViewer->SetZoomScale(scale);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::setZoomFactor(double factor) {
|
||
if (hasSeries()) {
|
||
double newScale = factor* mImageViewer->GetRenderer()->GetActiveCamera()->GetParallelScale();
|
||
mImageViewer->SetZoomScale(newScale);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::applyPanOffset(double *pan) {
|
||
if (hasSeries()) {
|
||
mImageViewer->applyPanOffset(pan);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::shiftCamera(double *point) {
|
||
if (hasSeries()) {
|
||
mImageViewer->shiftCamera(point);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::resetPanZoom() {
|
||
if (hasSeries()) {
|
||
mImageViewer->GetRenderer()->ResetCamera();
|
||
mImageViewer->ResetZoomScaleToFitWindowSize();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::setWindowLevel(double level, double width) {
|
||
if (hasSeries()) {
|
||
|
||
mImageViewer->SetColorLevel(level);
|
||
mImageViewer->SetColorWindow(width);
|
||
//You have to call updateConerInfo manually
|
||
//only mouse event can rely on callback
|
||
mImageViewer->UpdateCornerInfo(BOTTOM_RIGHT);
|
||
mImageViewer->Render();
|
||
emit onFusionWindowChange(level, width);
|
||
|
||
}
|
||
}
|
||
|
||
void DicomImageView::getWindowLevel(double &level, double &width) {
|
||
if (hasSeries()) {
|
||
level = mImageViewer->GetColorLevel();
|
||
width = mImageViewer->GetColorWindow();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::negativeWindow() {
|
||
if (hasSeries()) {
|
||
if (mIsNegative) {
|
||
mImageViewer->SetNegativeMode(false);
|
||
mIsNegative = false;
|
||
} else {
|
||
mImageViewer->SetNegativeMode(true);
|
||
mIsNegative = true;
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
void DicomImageView::setColorTable(const QString &aTableName)
|
||
{
|
||
if (hasSeries()) {
|
||
mImageViewer->SetLookupTable(aTableName);
|
||
mTableName = aTableName;
|
||
}
|
||
}
|
||
|
||
QString DicomImageView::getColorTable()
|
||
{
|
||
return mTableName;
|
||
}
|
||
|
||
void DicomImageView::hFlipImage() {
|
||
if (hasSeries()) {
|
||
int slice = mImageViewer->GetSlice();
|
||
//HFlip
|
||
//mImageViewer->GetRenderer()->GetActiveCamera()->SetViewUp(0, 1, 0);
|
||
resetPanZoom();
|
||
mImageViewer->GetRenderer()->GetActiveCamera()->Azimuth(180);
|
||
FlipExportHelper::toggleFlip();
|
||
//to avoid black out problem during slicing
|
||
//slicing is related with rotation, you have to recalculate to get it right
|
||
mImageViewer->GetRenderer()->ResetCameraClippingRange();
|
||
mImageViewer->SetSlice(slice);
|
||
|
||
mImageViewer->GetRenderWindow()->Render();
|
||
emit onTransform(H_FLIP);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::vFlipImage() {
|
||
if (hasSeries()) {
|
||
//double scale = mImageViewer->GetRenderer()->GetActiveCamera()->GetParallelScale();
|
||
|
||
int slice = mImageViewer->GetSlice();
|
||
resetPanZoom();
|
||
//Method 2: Order matters
|
||
mImageViewer->GetRenderer()->GetActiveCamera()->Elevation(-180);
|
||
mImageViewer->GetRenderer()->GetActiveCamera()->Roll(180);
|
||
//mImageViewer->GetRenderer()->GetActiveCamera()->SetViewUp(0,-1,0);
|
||
FlipExportHelper::toggleFlip();
|
||
//to avoid black out problem during slicing
|
||
//slicing is related with rotation, you have to recalculate to get it right
|
||
|
||
mImageViewer->GetRenderer()->ResetCameraClippingRange();
|
||
mImageViewer->SetSlice(slice);
|
||
mImageViewer->GetRenderWindow()->Render();
|
||
emit onTransform(V_FLIP);
|
||
}
|
||
|
||
}
|
||
|
||
void DicomImageView::rotateImage(double angle, TransFormType operation) {
|
||
if (hasSeries()) {
|
||
int slice = mImageViewer->GetSlice();
|
||
resetPanZoom();
|
||
mImageViewer->GetRenderer()->GetActiveCamera()->Roll(angle);
|
||
mCurrentRAngle=(mCurrentRAngle+(int)angle)%360;
|
||
resetPanZoom();
|
||
//to avoid black out problem during slicing
|
||
//slcing is related with rotation, you have to recalculate to get it right
|
||
mImageViewer->GetRenderer()->ResetCameraClippingRange();
|
||
mImageViewer->SetSlice(slice);
|
||
// mImageViewer->GetRenderWindow()->Render();
|
||
emit onTransform(operation);
|
||
}
|
||
|
||
}
|
||
|
||
void DicomImageView::ClearTransformations() {
|
||
if (hasSeries()) {
|
||
int slice = mImageViewer->GetSlice();
|
||
//reset flip and rotation
|
||
mImageViewer->UpdateReconPlane();
|
||
resetPanZoom();
|
||
//avoid black out problem
|
||
mImageViewer->GetRenderer()->ResetCameraClippingRange();
|
||
mImageViewer->SetSlice(slice);
|
||
//Render
|
||
mImageViewer->GetRenderWindow()->Render();
|
||
emit onTransform(CLEAR);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::updateCornerInfoAll() {
|
||
if (hasSeries()) {
|
||
mImageViewer->UpdateCornerInfoAll();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::updateCornerInfoPrivacy() {
|
||
if (hasSeries()) {
|
||
mImageViewer->UpdateCornerInfo(TOP_RIGHT);
|
||
mImageViewer->Render();
|
||
}
|
||
}
|
||
|
||
//--VCR about------------------------------------------------------------------
|
||
bool DicomImageView::isVCRVisible() {
|
||
if (!mVcrToolbar) return false;
|
||
return mVcrToolbar->isVisible();
|
||
}
|
||
|
||
void DicomImageView::setVCRVisible(bool visible) {
|
||
if (!mVcrToolbar) return;
|
||
mVcrToolbar->setVisible(visible);
|
||
}
|
||
|
||
void DicomImageView::cineModeOn() {
|
||
if (!mVcrToolbar) return;
|
||
//updateVCRToolbarPos();
|
||
int ax = (this->geometry().bottomLeft().x() + this->geometry().bottomRight().x()) / 2 + VCRHelper::getVCRXOffset();
|
||
int ay = (this->geometry().bottomLeft().y() + this->geometry().bottomRight().y()) / 2 + VCRHelper::getVCRYOffset();
|
||
mVcrToolbar->move(ax, ay);
|
||
|
||
mVcrToolbar->show();
|
||
this->initCineModeThread();
|
||
mVcrToolbar->reConnectController(mVcrController);
|
||
|
||
}
|
||
|
||
void DicomImageView::initCineModeThread() {
|
||
mVcrController = new pqVCRController(nullptr, this);
|
||
mVcrController->moveToThread(&mVcrControlThread);
|
||
connect(&mVcrControlThread, &QThread::finished, mVcrController, &QObject::deleteLater);
|
||
mVcrControlThread.start();
|
||
mIsCine = true;
|
||
}
|
||
|
||
void DicomImageView::onFirstFrame() {
|
||
if (hasSeries()) {
|
||
mScrollBar->setValue(mImageViewer->GetSliceMin());
|
||
}
|
||
}
|
||
|
||
void DicomImageView::onPreviousFrame() {
|
||
if (hasSeries()) {
|
||
int slice = mImageViewer->GetSlice();
|
||
slice = --slice;
|
||
int min_slice = mImageViewer->GetSliceMin();
|
||
if (slice < min_slice) {
|
||
slice = mImageViewer->GetSliceMax();
|
||
}
|
||
mScrollBar->setValue(slice);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::onNextFrame() {
|
||
if (hasSeries()) {
|
||
int slice = mImageViewer->GetSlice();
|
||
slice = ++slice;
|
||
int max_slice = mImageViewer->GetSliceMax();
|
||
if (slice > max_slice) {
|
||
slice = mImageViewer->GetSliceMin();
|
||
}
|
||
mScrollBar->setValue(slice);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::onLastFrame() {
|
||
if (hasSeries()) {
|
||
mScrollBar->setValue(mImageViewer->GetSliceMax());
|
||
}
|
||
}
|
||
|
||
//-- Measure about--------------------------------------------------------------------------------------
|
||
void DicomImageView::activeMeasure(Measure *m) {
|
||
if (nullptr != mSeries) {
|
||
mImageViewer->ActiveMeasure(m);
|
||
}
|
||
}
|
||
|
||
void DicomImageView::unactiveMeasure() {
|
||
if (nullptr != mSeries) {
|
||
mImageViewer->UnActiveMeasure();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::deleteSelectedMeasure() {
|
||
if (nullptr != mSeries) {
|
||
mImageViewer->DeleteSelectedMeasure();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::deleteCurrentSliceMeasure() {
|
||
if (nullptr != mSeries) {
|
||
mImageViewer->DeleteCurrentSliceMeasure();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::deleteCurrentSeriesMeasure() {
|
||
if (nullptr != mSeries) {
|
||
mImageViewer->DeleteCurrentSeriesMeasure();
|
||
}
|
||
}
|
||
|
||
void DicomImageView::removeViewWithMeasure() {
|
||
mImageViewer->UnActiveMeasure();
|
||
mImageViewer->DeleteCurrentSeriesMeasure();
|
||
}
|
||
|
||
bool DicomImageView::checkFusion(DicomImageView *base, DicomImageView *overlap) {
|
||
SeriesImageSet *baseSeries = base->getSeriesInstance();
|
||
SeriesImageSet *overlapSeries = overlap->getSeriesInstance();
|
||
// no data
|
||
if (!base->hasSeries() || !overlap->hasSeries()) return false;
|
||
// diff study
|
||
if (baseSeries->getStudyUID() == nullptr ||
|
||
strcmp(baseSeries->getStudyUID(), overlapSeries->getStudyUID()) != 0)
|
||
return false;
|
||
//same series
|
||
if (baseSeries->GetSeriesNumber() == overlapSeries->GetSeriesNumber()) return false;
|
||
// not enough slice
|
||
if (baseSeries->GetProperty()->GetSliceCount() < 20 ||
|
||
overlapSeries->GetProperty()->GetSliceCount() < 20) return false;
|
||
// check intersect
|
||
if(!baseSeries->IntersectWorldBounds(overlapSeries)) return false;
|
||
// check current slice orientation
|
||
return base->CompareReconPlane(overlap);
|
||
}
|
||
|
||
bool DicomImageView::checkFusion(DicomImageView *overlap) {
|
||
return DicomImageView::checkFusion(this, overlap);
|
||
}
|
||
|
||
bool DicomImageView::checkMPRAble(DicomImageView *view) {
|
||
return (view->getSeriesInstance()->GetProperty()->GetSliceCount() > 20 );
|
||
}
|
||
|