Raw File
OxSignCalculatorShmolli.h
/*!
 * \file OxSignCalculatorShmolli.h
 * \author Konrad Werys
 * \date 2018/08/01
 */

#ifndef Tomato_OXSIGNCALCULATORSHMOLLI_H
#define Tomato_OXSIGNCALCULATORSHMOLLI_H

#include "OxSignCalculator.h"
#include "KWUtil.h"

namespace Ox {

    /**
     * \class SignCalculatorShmolli
     * \brief
     * \details
     * @tparam MeasureType
     */
    template< typename MeasureType >
    class SignCalculatorShmolli : public SignCalculator<MeasureType> {

    public:
        virtual int calculateSign(){
            int IsPhaseOK = SKPPhase2Signs(
                    this->getNSamples(),
                    this->getInvTimes(),
                    this->getSigMag(),
                    this->getSigPha(),
                    this->getSignal(),
                    this->getSigns(),
                    this->getPhaMin(),
                    this->getPhaMax());
            for (int i = 0; i < this->getNSamples(); ++i) {
                if (this->getSigns()[i] != 0) {
                    this->getSignal()[i] = this->getSigns()[i] * (this->getSigMag()[i]);
                }
            }
            return IsPhaseOK;
        };

        static int SKPPhase2Signs(
                int nSamples,
                const MeasureType* invTimes,
                const MeasureType* sigMag,
                const MeasureType* sigPha,
                MeasureType* signal,
                MeasureType* signs,
                MeasureType phaMin,
                MeasureType phaMax
              );

        virtual int getPhaMin() { return _phaMin; }
        virtual int getPhaMax() { return _phaMax; }

        virtual void setPhaMin(int _phaMin) { SignCalculatorShmolli::_phaMin = _phaMin; }
        virtual void setPhaMax(int _phaMax) { SignCalculatorShmolli::_phaMax = _phaMax; }

        /**
         * \brief constructor
         */
        SignCalculatorShmolli(){
            this->setAllPointersToNull();
            this->_nSamples = 0;
            this->_phaMin = -4096;
            this->_phaMax = 4096;
        };

        /**
         * \brief copy constructor
         */
        SignCalculatorShmolli(const SignCalculatorShmolli &old){
            this->setAllPointersToNull();
            this->_nSamples = old._nSamples;
            this->_phaMin = old._phaMin;
            this->_phaMax = old._phaMax;
        };

        /**
         * cloning
         * @return
         */
        virtual SignCalculator<MeasureType> *newByCloning() { return new SignCalculatorShmolli<MeasureType>(*this); }


    protected:
        double _phaMin;
        double _phaMax;
        const static int MAX_MOLLI_TI_SAMPLES = 128;
        const static int MAX_T1_TRESHOLD = 4000;

//        /**
//         * \brief do not forget about the virtual destructor, see
//         * https://stackoverflow.com/questions/461203/when-to-use-virtual-destructors
//         */
//        virtual ~SignCalculatorShmolli(){};
    };

    template<typename MeasureType>
    int
    SignCalculatorShmolli<MeasureType>
    ::SKPPhase2Signs(
            int nSamples,
            const MeasureType* invTimes,
            const MeasureType* sigMag,
            const MeasureType* sigPha,
            MeasureType* signal,
            MeasureType* signs,
            MeasureType phaMin,
            MeasureType phaMax) {
        // takes all samples sorted from shortest to longest TI, returns 0 if clear problems identified to allow rejection of data based on incosistent signs.
        // note: logic for correction of signs works for ShMOLLI, should work for most basic T1_MOLLI variants too. but not fully tested.
        //std::cout << "a" << std::endl;

        KWUtil::copyArrayToArray(nSamples, signal, sigMag);

        double MaxTIForSignInvert = (MAX_T1_TRESHOLD * 0.67);

        //double _phaMin = -4096;
        //double _phaMax = 4096;
        double zeroRangeFraction = 0.3; // marks size of uncertaintey of corossover between signs. The signs in this zone will be decided by brute force. 0.1 (few doubts) 1 - all phase info doubtful ,0.3 is about right.
        double PIrange = (phaMax - phaMin) / 2; // scale to make one pi=~3.14 phase rotation // KW on dicoms *2
        double PI05 = PIrange * 0.5;
        double PIx2 = PIrange * 2;
        int IsPhaseErrorFound = 0;

        double upmap[MAX_MOLLI_TI_SAMPLES]; // phase normalised
        double PhaseAway[MAX_MOLLI_TI_SAMPLES]; // phase normalised
        //int asign[MAX_MOLLI_TI_SAMPLES] ; // signs estimate
        int nsignspositive = 0;
        int nsignsnegative = 0;

        for (int i = 0; i < nSamples; i++) {
            upmap[i] = fmod(PIx2 + sigPha[i] - sigPha[0], PIx2); // make sure within 2*pi
            PhaseAway[i] = fabs(upmap[i] - PIrange);  // away from positive signs  of first smaple+PI
            PhaseAway[i] = ((PI05 - PhaseAway[i]) /
                            (zeroRangeFraction * PI05)); // =How many times outsede midzone grey area around PI/2
            if (PhaseAway[i] < -1) {
                signs[i] = -1;
                nsignsnegative++;
            }
            else if (PhaseAway[i] > 1) {
                signs[i] = 1;
                nsignspositive++;
            }
            else signs[i] = 0;
            if ((signs[i] < 0) && (invTimes[i] > MaxTIForSignInvert)) // detect obvious pahse problems
            {
                IsPhaseErrorFound = 1;// wrong- found clear engative sign for for long TI
                signs[i] = 1; // corect problem in case recon is atempted, // depends on IsAbortreconOnPhaseErrors
            }
        }
        if ((nsignspositive == 0) || (nsignsnegative == 0)) // suspicious, no positive signs were found
        {
            // basic solution  for (i = 0;i < n;i++)signs[i]=0; // dont bother guessing, leave to brute force...
            /// more complicated.
            // sceanrios
            ////(1) longest TI is longer than MaxTIForSignInvert
            /////////// ==> ALL signs must be POSITIVE.
            ////(2) longest TI is SHORTER than MaxTIForSignInvert
            /////////// (2a) 1st and last 4 samples show NEGATIVE trend  (R(1&3456)=>> All signs NEGATIVE
            /////////// (2b) 1st 4 samples show positive trend, which is greater than last 4 samples  and greater than R(1&3456)=>> definite very short  T1, All samaples POSITIVE
            //////////   (else) set all positive + indicate pahse errors.

            ////(1) longest TI is longer than MaxTIForSignInvert
            /////////// ==> ALL signs must be POSITIVE.
            if (invTimes[nSamples - 1] > MaxTIForSignInvert) {
                for (int i = 0; i < nSamples; i++) signs[i] = 1;
                return (1); // loks like conssitent all positive signs
            }
            ////(2) longest TI is SHORTER than MaxTIForSignInvert
            /////////// (2a) 1st and last 4 samples show NEGATIVE trend  (R(1&3456)=>> All signs NEGATIVE
            double y1andlast4[MAX_MOLLI_TI_SAMPLES];
            double x1andlast4[MAX_MOLLI_TI_SAMPLES];
            double R1andlast4 = 0;
            double dummySl, dummyOff;
            y1andlast4[0] = sigMag[0];
            x1andlast4[0] = invTimes[0];
            for (int i = 0; i < 4; i++)
                y1andlast4[i + 1] = sigMag[nSamples - 1 - i]; // order reversed, does not amtter for regression
            for (int i = 0; i < 4; i++)
                x1andlast4[i + 1] = invTimes[nSamples - 1 - i]; // order reversed, does not matter for regression
            //double MOLLI_LinReg(double *x,double *y, int n, double &rslope, double &roffset)
            R1andlast4 = KWUtil::SKPLinReg(x1andlast4, y1andlast4, 5, dummySl, dummyOff);
            if (R1andlast4 <
                (-0.2)) // !!!note arbitrary threshold below zero toa void noise but not too far to account for curvature. !!!!Not validated but this is digging into noise/saving few pixels, should carry no impact.
            {
                for (int i = 0; i < nSamples; i++)signs[i] = -1;
                return (1); // loks like conssitent all negative signs, with high heart rate
            }
            /////////// (2b) 1st 4 samples show positive trend, which is greater than last 4 samples  and greater than R(1&3456)=>> definite very short  T1, All samaples POSITIVE
            double Rfirst4 = KWUtil::SKPLinReg(invTimes, sigMag, 4, dummySl, dummyOff);
            double Rlast4 = KWUtil::SKPLinReg(invTimes + nSamples - 4,
                                              sigMag + nSamples - 4, 4, dummySl, dummyOff);
            if ((Rfirst4 > Rlast4) && (Rfirst4 > R1andlast4)) {
                for (int i = 0; i < nSamples; i++) signs[i] = 1;
                return (1); // loks like conssitent all positive signs, with high heart rate
            }

            //////////   (else) set all positive + indicate pahse errors.
            for (int i = 0; i < nSamples; i++) signs[i] = 1;
            return (0);
        }
        return (!IsPhaseErrorFound);
    }

} //namespace Ox

#endif //Tomato_OXSIGNCALCULATOR_H
back to top