示例#1
0
ArrayXcd MP2RAGE::signal(const double M0, const double T1, const double B1, const double eta) const {
    return One_MP2RAGE(m_flip, m_TR, m_N, m_TD, M0, T1, B1, eta);
}
示例#2
0
int main(int argc, char **argv) {
    args::ArgumentParser parser(
        "Calculates T1/B1 maps from MP2/3-RAGE data\nhttp://github.com/spinicist/QUIT");
    args::Positional<std::string> input_path(parser, "INPUT FILE", "Path to complex MP-RAGE data");
    args::HelpFlag                help(parser, "HELP", "Show this help message", {'h', "help"});
    args::Flag           verbose(parser, "VERBOSE", "Print more information", {'v', "verbose"});
    args::ValueFlag<int> threads(parser,
                                 "THREADS",
                                 "Use N threads (default=4, 0=hardware limit)",
                                 {'T', "threads"},
                                 QI::GetDefaultThreads());
    args::ValueFlag<std::string> outarg(
        parser, "OUTPREFIX", "Add a prefix to output filenames", {'o', "out"});
    args::ValueFlag<std::string> json_file(
        parser, "FILE", "Read JSON input from file instead of stdin", {"file"});
    args::ValueFlag<float> beta_arg(
        parser,
        "BETA",
        "Regularisation factor for robust contrast calculation "
        "(https://journals.plos.org/plosone/article?id=10.1371/journal.pone.0099676)",
        {'b', "beta"},
        0.0);
    args::Flag t1(parser, "T1", "Calculate T1 map via spline look-up", {'t', "t1"});
    QI::ParseArgs(parser, argc, argv, verbose, threads);

    auto inFile = QI::ReadImage<QI::SeriesXF>(QI::CheckPos(input_path), verbose);

    auto ti_1                     = itk::ExtractImageFilter<QI::SeriesXF, QI::VolumeXF>::New();
    auto ti_2                     = itk::ExtractImageFilter<QI::SeriesXF, QI::VolumeXF>::New();
    auto region                   = inFile->GetLargestPossibleRegion();
    region.GetModifiableSize()[3] = 0;
    ti_1->SetExtractionRegion(region);
    ti_1->SetDirectionCollapseToSubmatrix();
    ti_1->SetInput(inFile);
    region.GetModifiableIndex()[3] = 1;
    ti_2->SetExtractionRegion(region);
    ti_2->SetDirectionCollapseToSubmatrix();
    ti_2->SetInput(inFile);

    QI::Log(verbose, "Generating MP2 contrasts");
    using BinaryFilter = itk::BinaryGeneratorImageFilter<QI::VolumeXF, QI::VolumeXF, QI::VolumeF>;
    auto MP2Filter     = BinaryFilter::New();
    MP2Filter->SetInput1(ti_1->GetOutput());
    MP2Filter->SetInput2(ti_2->GetOutput());
    const float &beta = beta_arg.Get();
    MP2Filter->SetFunctor([&](const std::complex<float> &p1, const std::complex<float> &p2) {
        return MP2Contrast(p1, p2, beta);
    });
    MP2Filter->Update();
    const std::string out_prefix = outarg ? outarg.Get() : QI::StripExt(input_path.Get());
    QI::WriteImage(MP2Filter->GetOutput(), out_prefix + "_MP2" + QI::OutExt(), verbose);

    if (t1) {
        QI::Log(verbose, "Reading sequence information");
        rapidjson::Document input =
            json_file ? QI::ReadJSON(json_file.Get()) : QI::ReadJSON(std::cin);
        QI::MP2RAGESequence mp2rage_sequence(input["MP2RAGE"]);
        QI::Log(verbose, "Building look-up spline");
        int            num_entries = 100;
        Eigen::ArrayXd T1_values   = Eigen::ArrayXd::LinSpaced(num_entries, 0.25, 4.0);
        Eigen::ArrayXd MP2_values(num_entries);
        for (int i = 0; i < num_entries; i++) {
            const auto  sig = One_MP2RAGE(1., T1_values[i], 1., mp2rage_sequence);
            const float mp2 = MP2Contrast(sig[0], sig[1]);
            if ((i > 0) && (mp2 > MP2_values[i - 1])) {
                num_entries = i;
                break;
            } else {
                MP2_values[i] = mp2;
            }
        }
        QI::Log(verbose, "Lookup table length = {}", num_entries);
        QI::SplineInterpolator mp2_to_t1(MP2_values.head(num_entries), T1_values.head(num_entries));
        if (beta) {
            QI::Log(verbose, "Recalculating unregularised MP2 image");
            MP2Filter->SetFunctor(
                [&](const std::complex<float> &p1, const std::complex<float> &p2) {
                    return MP2Contrast(p1, p2, 0.0);
                });
            MP2Filter->Update();
        }
        using UnaryFilter   = itk::UnaryGeneratorImageFilter<QI::VolumeF, QI::VolumeF>;
        auto T1LookupFilter = UnaryFilter::New();
        T1LookupFilter->SetInput(MP2Filter->GetOutput());
        auto lookup = [&](const float &p) { return mp2_to_t1(p); };
        T1LookupFilter->SetFunctor(lookup);
        QI::Log(verbose, "Calculating T1");
        T1LookupFilter->Update();
        QI::WriteImage(T1LookupFilter->GetOutput(), out_prefix + "_MP2_T1" + QI::OutExt(), verbose);
    }
    QI::Log(verbose, "Finished.");
    return EXIT_SUCCESS;
}