患者方位計算

PatientOrientation Q2DViewer::getCurrentDisplayedImagePatientOrientation() const
{
    if (!getMainInput())
    {
        return PatientOrientation();
    }
    
    // Si no estem a la vista axial (adquisició original) obtindrem 
    // la orientació a través de la primera imatge
    int index = (getCurrentViewPlane() == OrthogonalPlane::XYPlane) ? getCurrentSlice() : 0;

    PatientOrientation originalOrientation;
    Image *image = getMainInput()->getImage(index);
    if (image)
    {
        originalOrientation = image->getPatientOrientation();
        if (originalOrientation.getDICOMFormattedPatientOrientation().isEmpty())
        {
            return PatientOrientation();
        }
    }
    else
    {
        DEBUG_LOG("L'índex d'imatge actual és incorrecte o no hi ha imatges al volum. Això no hauria de passar en aquest mètode.");
        return PatientOrientation();
    }
    
    // Escollim les etiquetes que hem agafar com a referència segons la vista actual
    QString baseRowLabel;
    QString baseColumnLabel;
    switch (getCurrentViewPlane())
    {
        case OrthogonalPlane::XYPlane:
            baseRowLabel = originalOrientation.getRowDirectionLabel();
            baseColumnLabel = originalOrientation.getColumnDirectionLabel();
            break;

        case OrthogonalPlane::YZPlane:
            baseRowLabel = originalOrientation.getColumnDirectionLabel();
             // TODO Tenim la normal "al revés", en realitat hauria de ser el contrari
            baseColumnLabel = PatientOrientation::getOppositeOrientationLabel(originalOrientation.getNormalDirectionLabel());
            break;

        case OrthogonalPlane::XZPlane:
            baseRowLabel = originalOrientation.getRowDirectionLabel();
            // TODO Tenim la normal "al revés", en realitat hauria de ser el contrari
            baseColumnLabel = PatientOrientation::getOppositeOrientationLabel(originalOrientation.getNormalDirectionLabel());
            break;
    }

    // Ara caldrà escollir les etiquetes corresponents en funció de les rotacions i flips
    QString rowLabel;
    QString columnLabel;
    int absoluteRotateFactor = (4 + m_rotateFactor) % 4;
    if (getCurrentViewPlane() == OrthogonalPlane::YZPlane || getCurrentViewPlane() == OrthogonalPlane::XZPlane)
    {
        // HACK FLIP De moment necessitem fer aquest truc. Durant el refactoring caldria
        // veure si es pot fer d'una manera millor
        if (m_isImageFlipped)
        {
            absoluteRotateFactor = (absoluteRotateFactor + 2) % 4;
        }
    }
    switch (absoluteRotateFactor)
    {
        case 0:
            rowLabel = baseRowLabel;
            columnLabel = baseColumnLabel;
            break;

        case 1:
            rowLabel = PatientOrientation::getOppositeOrientationLabel(baseColumnLabel);
            columnLabel = baseRowLabel;
            break;

        case 2:
            rowLabel = PatientOrientation::getOppositeOrientationLabel(baseRowLabel);
            columnLabel = PatientOrientation::getOppositeOrientationLabel(baseColumnLabel);
            break;

        case 3:
            rowLabel = baseColumnLabel;
            columnLabel = PatientOrientation::getOppositeOrientationLabel(baseRowLabel);
            break;
    }

    if (m_isImageFlipped)
    {
        rowLabel = PatientOrientation::getOppositeOrientationLabel(rowLabel);
    }
    
    PatientOrientation patientOrientation;
    patientOrientation.setLabels(rowLabel, columnLabel);

    return patientOrientation;
}
/*************************************************************************************
  Copyright (C) 2014 Laboratori de Gràfics i Imatge, Universitat de Girona &
  Institut de Diagnòstic per la Imatge.
  Girona 2014. All rights reserved.
  http://starviewer.udg.edu

  This file is part of the Starviewer (Medical Imaging Software) open source project.
  It is subject to the license terms in the LICENSE file found in the top-level
  directory of this distribution and at http://starviewer.udg.edu/license. No part of
  the Starviewer (Medical Imaging Software) open source project, including this file,
  may be copied, modified, propagated, or distributed except according to the
  terms contained in the LICENSE file.
 *************************************************************************************/

#include "patientorientation.h"

#include <QRegExp>
#include <QStringList>
#include <cmath>

#include "dicomvaluerepresentationconverter.h"
#include "imageorientation.h"
#include "logging.h"

namespace udg {

const QString PatientOrientation::LeftLabel("L");
const QString PatientOrientation::RightLabel("R");
const QString PatientOrientation::PosteriorLabel("P");
const QString PatientOrientation::AnteriorLabel("A");
const QString PatientOrientation::HeadLabel("H");
const QString PatientOrientation::FeetLabel("F");

bool PatientOrientation::setLabels(const QString &rowDirectionLabel, const QString &columnDirectionLabel, const QString &normalDirectionLabel)
{
    QString dicomFormattedString = rowDirectionLabel + DICOMValueRepresentationConverter::ValuesSeparator + columnDirectionLabel;

    if (!normalDirectionLabel.isEmpty())
    {
        dicomFormattedString += DICOMValueRepresentationConverter::ValuesSeparator + normalDirectionLabel;
    }
    
    return setDICOMFormattedPatientOrientation(dicomFormattedString);
}

bool PatientOrientation::setDICOMFormattedPatientOrientation(const QString &patientOrientation)
{
    if (validateDICOMFormattedPatientOrientationString(patientOrientation))
    {
        m_patientOrientationString = patientOrientation;
        return true;
    }
    else
    {
        m_patientOrientationString = "";
        return false;
    }
}

QString PatientOrientation::getDICOMFormattedPatientOrientation() const
{
    return m_patientOrientationString;
}

void PatientOrientation::setPatientOrientationFromImageOrientation(const ImageOrientation &imageOrientation)
{
    QString rowLabel = this->getOrientationLabelFromDirectionVector(imageOrientation.getRowVector());
    QString columnLabel = this->getOrientationLabelFromDirectionVector(imageOrientation.getColumnVector());
    QString normalLabel = this->getOrientationLabelFromDirectionVector(imageOrientation.getNormalVector());

    if (!setLabels(rowLabel, columnLabel, normalLabel))
    {
        DEBUG_LOG("makePatientOrientationFromImageOrientationPatient() ha generat una cadena d'orientació de pacient invàlida");
    }
}

QString PatientOrientation::getRowDirectionLabel() const
{
    return getNthDirectionLabel(0);
}

QString PatientOrientation::getColumnDirectionLabel() const
{
    return getNthDirectionLabel(1);
}

QString PatientOrientation::getNormalDirectionLabel() const
{
    return getNthDirectionLabel(2);
}

QString PatientOrientation::getNthDirectionLabel(int i) const
{
    QString label;
    
    if (!m_patientOrientationString.isEmpty())
    {
        QStringList labelList = m_patientOrientationString.split(DICOMValueRepresentationConverter::ValuesSeparator);
        if (labelList.size() >= i + 1 && i >= 0)
        {
            label = labelList.at(i);
        }
    }

    return label;
}

QString PatientOrientation::getOppositeOrientationLabel(const QString &label)
{
    int i = 0;
    QString oppositeLabel;
    while (i < label.size())
    {
        if (QString(label.at(i)) == PatientOrientation::LeftLabel)
        {
            oppositeLabel += PatientOrientation::RightLabel;
        }
        else if (QString(label.at(i)) == PatientOrientation::RightLabel)
        {
            oppositeLabel += PatientOrientation::LeftLabel;
        }
        else if (QString(label.at(i)) == PatientOrientation::AnteriorLabel)
        {
            oppositeLabel += PatientOrientation::PosteriorLabel;
        }
        else if (QString(label.at(i)) == PatientOrientation::PosteriorLabel)
        {
            oppositeLabel += PatientOrientation::AnteriorLabel;
        }
        else if (QString(label.at(i)) == PatientOrientation::HeadLabel)
        {
            oppositeLabel += PatientOrientation::FeetLabel;
        }
        else if (QString(label.at(i)) == PatientOrientation::FeetLabel)
        {
            oppositeLabel += PatientOrientation::HeadLabel;
        }
        else
        {
            oppositeLabel += "?";
        }
        i++;
    }
    return oppositeLabel;
}

bool PatientOrientation::operator==(const PatientOrientation &orientation) const
{
    return m_patientOrientationString == orientation.m_patientOrientationString;
}

bool PatientOrientation::validateDICOMFormattedPatientOrientationString(const QString &string)
{
    // Construim l'expressió regular que ens comprova que la cadena està en el format correcte: Cadena buida, o amb 2 o 3 elements.
    
    // Etiquetes vàlides: RLAPHF
    QString validOrientationLabels = RightLabel + LeftLabel + AnteriorLabel + PosteriorLabel + HeadLabel + FeetLabel;
    
    // Expressió per les etiquetes [RLAPHF]+
    QString validLabelsExpression = "[" + validOrientationLabels + "]+";
    
    // Expressió pels separadors 
    // Hem de posar dos cops seguits DICOMValueRepresentationConverter::ValuesSeparator, ja que tal com diu la documentació de QRegExp:
    //
    // Note: The C++ compiler transforms backslashes in strings. To include a \ in a regexp, enter it twice, i.e. \\. 
    // To match the backslash character itself, enter it four times, i.e. \\\\.
    //
    // Llavors cal posar \\\\ perquè l'expressió sigui correcta
    QString separatorExpression = DICOMValueRepresentationConverter::ValuesSeparator + DICOMValueRepresentationConverter::ValuesSeparator;
    
    // L'expressió final: ([RLAPHF]+\\\\[RLAPHF]+(\\\\[RLAPHF]+)?)*
    // És a dir, 2 o 3 ítems amb etiquetes vàlides separats per \\.
    QString regExpString = "(" + validLabelsExpression + separatorExpression + validLabelsExpression +
        "(" + separatorExpression + validLabelsExpression + ")?)*";

    QRegExp validStringExpression(regExpString);
    return validStringExpression.exactMatch(string);
}

QString PatientOrientation::getOrientationLabelFromDirectionVector(const QVector3D &vector)
{
    QString orientation;

    QString orientationX = vector.x() < 0 ? RightLabel : LeftLabel;
    QString orientationY = vector.y() < 0 ? AnteriorLabel : PosteriorLabel;
    QString orientationZ = vector.z() < 0 ? FeetLabel : HeadLabel;

    double absX = fabs(vector.x());
    double absY = fabs(vector.y());
    double absZ = fabs(vector.z());

    for (int i = 0; i < 3; ++i)
    {
        if (absX > .0001 && absX > absY && absX > absZ)
        {
            orientation += orientationX;
            absX = 0;
        }
        else if (absY > .0001 && absY > absX && absY > absZ)
        {
            orientation += orientationY;
            absY = 0;
        }
        else if (absZ > .0001 && absZ > absX && absZ > absY)
        {
            orientation += orientationZ;
            absZ = 0;
        }
        else
        {
            break;
        }
    }
    
    return orientation;
}

} // End namespace udg

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章