Skip to content
Snippets Groups Projects
Commit df55aff5 authored by Taylor Hanayik's avatar Taylor Hanayik
Browse files

add json lib; update unit tests; add lfs

parent 08e56c46
No related branches found
No related tags found
1 merge request!22add --json option and associated logic
Pipeline #19003 skipped
*.nii.gz filter=lfs diff=lfs merge=lfs -text
...@@ -8,21 +8,27 @@ ...@@ -8,21 +8,27 @@
#include <limits> #include <limits>
#include <string> #include <string>
#include <typeinfo>
#include "utils/fsl_isfinite.h" #include "utils/fsl_isfinite.h"
#include "armawrap/newmat.h" #include "armawrap/newmat.h"
#include "miscmaths/miscmaths.h" #include "miscmaths/miscmaths.h"
#include "newimage/newimageall.h" #include "newimage/newimageall.h"
#include "newimage/costfns.h" #include "newimage/costfns.h"
#include "nlohmann/json.hpp"
using namespace std; using namespace std;
using namespace NEWMAT; using namespace NEWMAT;
using namespace NEWIMAGE; using namespace NEWIMAGE;
using json = nlohmann::json;
int print_usage(const string &progname) int print_usage(const string &progname)
{ {
cout << "Usage: fslstats [preoptions] <input> [options]" << endl cout << "Usage: fslstats [preoptions] <input> [options]" << endl
<< endl; << endl;
// add --json output format option
// keys will correspond to the options above
cout << "preoption -json: output in JSON format to standard out" << endl;
cout << "preoption -t will give a separate output line for each 3D volume of a 4D timeseries" << endl; cout << "preoption -t will give a separate output line for each 3D volume of a 4D timeseries" << endl;
cout << "preoption -K < indexMask > will generate seperate n submasks from indexMask, for indexvalues 1..n where n is the maximum index value in indexMask, and generate statistics for each submask" << endl; cout << "preoption -K < indexMask > will generate seperate n submasks from indexMask, for indexvalues 1..n where n is the maximum index value in indexMask, and generate statistics for each submask" << endl;
cout << "Note - options are applied in order, e.g. -M -l 10 -M will report the non-zero mean, apply a threshold and then report the new nonzero mean" << endl cout << "Note - options are applied in order, e.g. -M -l 10 -M will report the non-zero mean, apply a threshold and then report the new nonzero mean" << endl
...@@ -51,11 +57,7 @@ int print_usage(const string &progname) ...@@ -51,11 +57,7 @@ int print_usage(const string &progname)
cout << "-k <mask> : use the specified image (filename) for masking - overrides lower and upper thresholds" << endl; cout << "-k <mask> : use the specified image (filename) for masking - overrides lower and upper thresholds" << endl;
cout << "-d <image> : take the difference between the base image and the image specified here" << endl; cout << "-d <image> : take the difference between the base image and the image specified here" << endl;
cout << "-h <nbins> : output a histogram (for the thresholded/masked voxels only) with nbins" << endl; cout << "-h <nbins> : output a histogram (for the thresholded/masked voxels only) with nbins" << endl;
cout << "-H <nbins> <min> <max> : output a histogram (for the thresholded/masked voxels only) with nbins and histogram limits of min and max" << endl; cout << "-H <nbins> <min> <max> : output a histogram (for the thresholded/masked voxels only) with nbins and histogram limits of min and max" << endl;
// add --json output format option
// keys will correspond to the options above
cout << "--json : output in JSON format" << endl
<< endl;
cout << "Note - thresholds are not inclusive ie lthresh<allowed<uthresh" << endl; cout << "Note - thresholds are not inclusive ie lthresh<allowed<uthresh" << endl;
return 1; return 1;
} }
...@@ -120,38 +122,35 @@ int generate_masks(volume4D<float> &mask, volume4D<float> &masknz, const volume4 ...@@ -120,38 +122,35 @@ int generate_masks(volume4D<float> &mask, volume4D<float> &masknz, const volume4
return generateNonZeroMask(mask, masknz, input); return generateNonZeroMask(mask, masknz, input);
} }
// print function that can handle outputting in JSON format or not // print function that can handle outputting in JSON format or not. Use a template to allow for different types
void print_value(const string &key, const string &value, string &json, const bool jsonOutput) template <typename T>
void update_json(string key, T &value, json &j)
{ {
// append the key and value to the json string j[key] = value;
if (jsonOutput)
{
json.append("\"" + key + "\": \"" + value + "\", ");
}
else
{
cout << value << " ";
}
} }
template <typename T>
void update_json(string key, initializer_list<T> &value, json &j)
{
j[key] = value;
}
int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const string &indexMaskName) template <typename T>
void update_json(string key, vector<T> &value, json &j)
{ {
// determine if the user wants to output in JSON format j[key] = value;
bool jsonOutput = false; }
for (int i = 1; i < argc; i++)
{ void update_json(string key, ColumnVector &value, json &j)
if (strcmp(argv[i], "--json") == 0) {
{ j[key] = value;
jsonOutput = true; }
break;
} int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const string &indexMaskName, const bool jsonMode = false)
} {
// if JSON output is requested, then we need to store the output as a string
// and then output it at the end via cout json js;
// don't forget to close the json at the end with a "}"
string json = "";
cout.setf(ios::dec); cout.setf(ios::dec);
cout.setf(ios::fixed, ios::floatfield); cout.setf(ios::fixed, ios::floatfield);
cout.setf(ios::left, ios::adjustfield); cout.setf(ios::left, ios::adjustfield);
...@@ -167,27 +166,16 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -167,27 +166,16 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
read_volume4D(indexMask, indexMaskName); read_volume4D(indexMask, indexMaskName);
int nTimepoints((timeseriesMode) ? inputMaster.tsize() : 1), nIndices((indexMaskName != "") ? indexMask.max() : 1); int nTimepoints((timeseriesMode) ? inputMaster.tsize() : 1), nIndices((indexMaskName != "") ? indexMask.max() : 1);
// initialise the volumes array in the json object. This will get appended to later
js["volumes"] = json::array();
for (int timepoint = 0; timepoint < nTimepoints; timepoint++) for (int timepoint = 0; timepoint < nTimepoints; timepoint++)
{ {
if (jsonOutput) json tp = json::object();
{ tp["indices"] = json::array();
if (timepoint == 0){
json.append("{\"volumes\": [");
} else {
json.append(", ");
}
}
for (int index = 1; index <= nIndices; index++) for (int index = 1; index <= nIndices; index++)
{ {
// if json mode is enabled json idx = json::object();
if (jsonOutput)
{
if (index == 1){
json.append("{\"indices\": [{");
} else {
json.append(", {");
}
}
if (timeseriesMode) if (timeseriesMode)
vol = inputMaster[timepoint]; vol = inputMaster[timepoint];
volume<float> mask, masknz; volume<float> mask, masknz;
...@@ -229,23 +217,41 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -229,23 +217,41 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
else if (sarg == "-m") else if (sarg == "-m")
{ {
if (mask.nvoxels() > 0){ if (mask.nvoxels() > 0){
string val = to_string(vol.mean(mask)); auto val = vol.mean(mask);
print_value(sarg, val, json, jsonOutput); if (jsonMode){
update_json(sarg, val, idx);
} else {
cout << val << " ";
}
} else { } else {
string val = to_string(vol.mean()); // print_value(sarg, val, json, jsonMode);
print_value(sarg, val, json, jsonOutput); auto val = vol.mean();
if (jsonMode){
update_json(sarg, val, idx);
} else {
cout << val << " ";
}
} }
} }
else if (sarg == "-M") else if (sarg == "-M")
{ {
if (masknz.nvoxels() > 0) if (masknz.nvoxels() > 0){
print_value(sarg, to_string(vol.mean(masknz)), json, jsonOutput); auto val = vol.mean(masknz);
if (jsonMode){
update_json(sarg, val, idx);
} else {
cout << val << " ";
}
}
else else
{ {
double nzmean = 0; double nzmean = 0;
nzmean = nonzeromean(vol); nzmean = nonzeromean(vol);
string val = to_string(nzmean); if (jsonMode){
print_value(sarg, val, json, jsonOutput); update_json(sarg, nzmean, idx);
} else {
cout << nzmean << " ";
}
} }
} }
else if (sarg == "-X") else if (sarg == "-X")
...@@ -259,17 +265,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -259,17 +265,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
auto coord1 = MISCMATHS::round(coord(1)); auto coord1 = MISCMATHS::round(coord(1));
auto coord2 = MISCMATHS::round(coord(2)); auto coord2 = MISCMATHS::round(coord(2));
auto coord3 = MISCMATHS::round(coord(3)); auto coord3 = MISCMATHS::round(coord(3));
string coordString = ""; auto coordList = {coord1, coord2, coord3};
coordString.append(to_string(coord1)); if (jsonMode){
coordString.append(" "); update_json(sarg, coordList, idx);
coordString.append(to_string(coord2)); } else {
coordString.append(" "); cout << coord1 << " " << coord2 << " " << coord3 << " ";
coordString.append(to_string(coord3)); }
print_value(
sarg,
coordString,
json,
jsonOutput);
} }
else if (sarg == "-x") else if (sarg == "-x")
...@@ -283,17 +284,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -283,17 +284,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
auto coord1 = MISCMATHS::round(coord(1)); auto coord1 = MISCMATHS::round(coord(1));
auto coord2 = MISCMATHS::round(coord(2)); auto coord2 = MISCMATHS::round(coord(2));
auto coord3 = MISCMATHS::round(coord(3)); auto coord3 = MISCMATHS::round(coord(3));
string coordString = ""; auto coordList = {coord1, coord2, coord3};
coordString.append(to_string(coord1)); if (jsonMode){
coordString.append(" "); update_json(sarg, coordList, idx);
coordString.append(to_string(coord2)); } else {
coordString.append(" "); cout << coord1 << " " << coord2 << " " << coord3 << " ";
coordString.append(to_string(coord3)); }
print_value(
sarg,
coordString,
json,
jsonOutput);
} }
else if (sarg == "-w") else if (sarg == "-w")
{ {
...@@ -357,28 +353,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -357,28 +353,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
zmax = zmin; zmax = zmin;
zmin = tmp; zmin = tmp;
} }
// now output nifti coords vector<int> coords = {xmin, 1 + xmax - xmin, ymin, 1 + ymax - ymin, zmin, 1 + zmax - zmin, tmin, 1 + tmax - tmin};
string coords = ""; if (jsonMode){
coords.append(to_string(xmin)); update_json(sarg, coords, idx);
coords.append(" "); } else {
coords.append(to_string(1 + xmax - xmin)); cout << coords[0] << " " << coords[1] << " " << coords[2] << " " << coords[3] << " " << coords[4] << " " << coords[5] << " " << coords[6] << " " << coords[7] << " ";
coords.append(" "); }
coords.append(to_string(ymin));
coords.append(" ");
coords.append(to_string(1 + ymax - ymin));
coords.append(" ");
coords.append(to_string(zmin));
coords.append(" ");
coords.append(to_string(1 + zmax - zmin));
coords.append(" ");
coords.append(to_string(tmin));
coords.append(" ");
coords.append(to_string(1 + tmax - tmin));
print_value(
sarg,
coords,
json,
jsonOutput);
} }
else if (sarg == "-e") else if (sarg == "-e")
{ {
...@@ -400,12 +380,11 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -400,12 +380,11 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
} }
} }
entropy /= log((double)nbins); entropy /= log((double)nbins);
print_value( if (jsonMode){
sarg, update_json(sarg, entropy, idx);
to_string(entropy), } else {
json, cout << entropy << " ";
jsonOutput }
);
} }
else if (sarg == "-E") else if (sarg == "-E")
{ {
...@@ -427,12 +406,11 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -427,12 +406,11 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
} }
} }
entropy /= log((double)nbins); entropy /= log((double)nbins);
print_value( if (jsonMode){
sarg, update_json(sarg, entropy, idx);
to_string(entropy), } else {
json, cout << entropy << " ";
jsonOutput }
);
} }
else if (sarg == "-k") else if (sarg == "-k")
{ {
...@@ -547,125 +525,125 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -547,125 +525,125 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
if (mask.tsize() == 1) if (mask.tsize() == 1)
nvox = nvox * vol.tsize(); nvox = nvox * vol.tsize();
string nvox_str = ""; auto volume_v = nvox * vol.xdim() * vol.ydim() * vol.zdim();
nvox_str.append(to_string(nvox)); // promote nvox to float to maintain types in list
nvox_str.append(" "); auto nvox_volume = {static_cast<float>(nvox), volume_v};
nvox_str.append(to_string(nvox * vol.xdim() * vol.ydim() * vol.zdim())); if (jsonMode){
print_value( update_json(sarg, nvox_volume, idx);
sarg, } else {
nvox_str, cout << nvox << " " << volume_v << " ";
json, }
jsonOutput
);
} }
else else
{ {
string nvox_str = ""; long int nvox = vol.nvoxels() * vol.tsize();
nvox_str.append(to_string((long int)vol.nvoxels() * vol.tsize())); auto volume_v = nvox * vol.xdim() * vol.ydim() * vol.zdim();
nvox_str.append(" "); // promote nvox to float to maintain types in list
nvox_str.append(to_string(vol.nvoxels() * vol.tsize() * vol.xdim() * vol.ydim() * vol.zdim())); auto nvox_volume = {static_cast<float>(nvox), volume_v};
print_value( if (jsonMode){
sarg, update_json(sarg, nvox_volume, idx);
nvox_str, } else {
json, cout << nvox << " " << volume_v << " ";
jsonOutput); }
} }
} }
else if (sarg == "-V") else if (sarg == "-V")
{ {
if (masknz.nvoxels() > 0) if (masknz.nvoxels() > 0)
{ {
string nvox_str = ""; long int nvox = masknz.sum();
nvox_str.append(to_string((long int)masknz.sum())); auto volume_V = nvox * vol.xdim() * vol.ydim() * vol.zdim();
nvox_str.append(" "); // promote nvox to float to maintain types in list
nvox_str.append(to_string(masknz.sum() * vol.xdim() * vol.ydim() * vol.zdim())); auto nvox_volume = {static_cast<float>(nvox), volume_V};
print_value( if (jsonMode){
sarg, update_json(sarg, nvox_volume, idx);
nvox_str, } else {
json, cout << nvox << " " << volume_V << " ";
jsonOutput); }
} }
else else
{ {
long int nzvox; long int nzvox;
nzvox = nonzerocount(vol); nzvox = nonzerocount(vol);
string nzvox_str = ""; auto volume_V = nzvox * vol.xdim() * vol.ydim() * vol.zdim();
nzvox_str.append(to_string(nzvox)); // promate nzvox to float to maintain types in list
nzvox_str.append(" "); auto nvox_volume = {static_cast<float>(nzvox), volume_V};
nzvox_str.append(to_string(nzvox * vol.xdim() * vol.ydim() * vol.zdim())); if (jsonMode){
print_value( update_json(sarg, nvox_volume, idx);
sarg, } else {
nzvox_str, cout << nzvox << " " << volume_V << " ";
json, }
jsonOutput);
} }
} }
else if (sarg == "-D") else if (sarg == "-D")
{ {
// hidden debug option! // hidden debug option!
print_value( auto sum = vol.sum();
sarg, if (jsonMode){
to_string(vol.sum()), update_json(sarg, sum, idx);
json, } else {
jsonOutput cout << sum << " ";
); }
} }
else if (sarg == "-s") else if (sarg == "-s")
{ {
if (mask.nvoxels() > 0) if (mask.nvoxels() > 0){
print_value( auto stddev = vol.stddev(mask);
sarg, if (jsonMode){
to_string(vol.stddev(mask)), update_json(sarg, stddev, idx);
json, } else {
jsonOutput cout << stddev << " ";
); }
else }
print_value( else {
sarg, auto stddev = vol.stddev();
to_string(vol.stddev()), if (jsonMode){
json, update_json(sarg, stddev, idx);
jsonOutput } else {
); cout << stddev << " ";
}
}
} }
else if (sarg == "-S") else if (sarg == "-S")
{ {
if (masknz.nvoxels() > 0) if (masknz.nvoxels() > 0)
{ {
print_value( auto stddev = vol.stddev(masknz);
sarg, if (jsonMode){
to_string(vol.stddev(masknz)), update_json(sarg, stddev, idx);
json, } else {
jsonOutput cout << stddev << " ";
); }
} }
else else
{ {
print_value( auto stddev = nonzerostddev(vol);
sarg, if (jsonMode){
to_string(nonzerostddev(vol)), update_json(sarg, stddev, idx);
json, } else {
jsonOutput cout << stddev << " ";
); }
} }
} }
else if (sarg == "-r") else if (sarg == "-r")
{ {
vector<float> limits(vol.robustlimits(mask)); vector<float> limits(vol.robustlimits(mask));
print_value( if (jsonMode){
sarg, update_json(sarg, limits, idx);
to_string(limits[0]).append(" ").append(to_string(limits[1])), } else {
json, cout << limits[0] << " " << limits[1] << " ";
jsonOutput }
);
} }
else if (sarg == "-R") else if (sarg == "-R")
{ {
print_value( auto min_R = vol.min(mask);
sarg, auto max_R = vol.max(mask);
to_string(vol.min(mask)).append(" ").append(to_string(vol.max(mask))), auto minmax_R = {min_R, max_R};
json, if (jsonMode){
jsonOutput update_json(sarg, minmax_R, idx);
); } else {
cout << min_R << " " << max_R << " ";
}
} }
else if (sarg == "-c") else if (sarg == "-c")
{ {
...@@ -673,12 +651,15 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -673,12 +651,15 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
cog.SubMatrix(1, 3, 1, 1) = vol.cog(); cog.SubMatrix(1, 3, 1, 1) = vol.cog();
cog(4) = 1.0; cog(4) = 1.0;
cog = vol.newimagevox2mm_mat() * cog; cog = vol.newimagevox2mm_mat() * cog;
print_value( // remove the last element
sarg, cog = cog.Rows(1, 3);
to_string(cog(1)).append(" ").append(to_string(cog(2))).append(" ").append(to_string(cog(3))), // reshape to a row vector
json, cog = cog.t();
jsonOutput if (jsonMode){
); update_json(sarg, cog, idx);
} else {
cout << cog(1) << " " << cog(2) << " " << cog(3) << " ";
}
} }
else if (sarg == "-C") else if (sarg == "-C")
{ {
...@@ -687,12 +668,15 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -687,12 +668,15 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
cog.SubMatrix(1, 3, 1, 1) = vol.cog(); cog.SubMatrix(1, 3, 1, 1) = vol.cog();
cog(4) = 1.0; cog(4) = 1.0;
cog = vol.niftivox2newimagevox_mat().i() * cog; cog = vol.niftivox2newimagevox_mat().i() * cog;
print_value( // remove the last element
sarg, cog = cog.Rows(1, 3);
to_string(cog(1)).append(" ").append(to_string(cog(2))).append(" ").append(to_string(cog(3))), // reshape to a row vector
json, cog = cog.t();
jsonOutput if (jsonMode){
); update_json(sarg, cog, idx);
} else {
cout << cog(1) << " " << cog(2) << " " << cog(3) << " ";
}
} }
else if (sarg == "-p") else if (sarg == "-p")
{ {
...@@ -712,20 +696,22 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -712,20 +696,22 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
cerr << "Percentile must be between 0 and 100" << endl; cerr << "Percentile must be between 0 and 100" << endl;
exit(1); exit(1);
} }
if (mask.nvoxels() > 0) if (mask.nvoxels() > 0){
print_value( auto percentile_p = vol.percentile((float)n / 100.0, mask);
sarg, if (jsonMode){
to_string(vol.percentile((float)n / 100.0, mask)), update_json(sarg, percentile_p, idx);
json, } else {
jsonOutput cout << percentile_p << " ";
); }
else }
print_value( else {
sarg, auto percentile_p = vol.percentile((float)n / 100.0);
to_string(vol.percentile((float)n / 100.0)), if (jsonMode){
json, update_json(sarg, percentile_p, idx);
jsonOutput } else {
); cout << percentile_p << " ";
}
}
} }
else if (sarg == "-P") else if (sarg == "-P")
{ {
...@@ -750,12 +736,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -750,12 +736,12 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
generate_masks(mask, masknz, vol, lthr, uthr); generate_masks(mask, masknz, vol, lthr, uthr);
vol *= mask; vol *= mask;
} }
print_value( auto percentile_P = vol.percentile((float)n / 100.0, masknz);
sarg, if (jsonMode){
to_string(vol.percentile((float)n / 100.0, masknz)), update_json(sarg, percentile_P, idx);
json, } else {
jsonOutput cout << percentile_P << " ";
); }
} }
else if (sarg == "-h") else if (sarg == "-h")
{ {
...@@ -779,41 +765,20 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -779,41 +765,20 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
if (mask.nvoxels() > 0) if (mask.nvoxels() > 0)
{ {
auto hist = vol.histogram(nbins, vol.min(), vol.max(), mask); auto hist = vol.histogram(nbins, vol.min(), vol.max(), mask);
// loop over histogram values and and build a string to print if (jsonMode){
string hist_str = ""; update_json(sarg, hist, idx);
for (int i = 0; i < hist.Nrows(); i++) } else {
{ cout << hist << " ";
hist_str += to_string(hist(i+1));
if (i < hist.Nrows() - 1)
{
hist_str += " ";
}
} }
print_value(
sarg,
hist_str,
json,
jsonOutput
);
} }
else else
{ {
auto hist = vol.histogram(nbins, vol.min(), vol.max()); auto hist = vol.histogram(nbins, vol.min(), vol.max());
string hist_str = ""; if (jsonMode){
for (int i = 0; i < hist.Nrows(); i++) update_json(sarg, hist, idx);
{ } else {
hist_str += to_string(hist(i+1)); cout << hist << " ";
if (i < hist.Nrows() - 1)
{
hist_str += " ";
}
} }
print_value(
sarg,
hist_str,
json,
jsonOutput
);
} }
} }
else if (sarg == "-H") else if (sarg == "-H")
...@@ -859,91 +824,41 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st ...@@ -859,91 +824,41 @@ int fmrib_main_float(int argc, char *argv[], const bool timeseriesMode, const st
} }
if (mask.nvoxels() > 0) if (mask.nvoxels() > 0)
{ {
auto hist = vol.histogram(nbins, min, max, mask); auto hist = vol.histogram(nbins,min,max,mask);
string hist_str = ""; if (jsonMode){
for (int i = 0; i < hist.Nrows(); i++) update_json(sarg, hist, idx);
{ } else {
hist_str += to_string(hist(i+1)); cout << hist << " ";
if (i < hist.Nrows() - 1)
{
hist_str += " ";
}
} }
print_value(
sarg,
hist_str,
json,
jsonOutput
);
} }
else else
{ {
auto hist = vol.histogram(nbins, min, max); auto hist = vol.histogram(nbins, min, max);
string hist_str = ""; if (jsonMode){
for (int i = 0; i < hist.Nrows(); i++) update_json(sarg, hist, idx);
{ } else {
hist_str += to_string(hist(i+1)); cout << hist << " ";
if (i < hist.Nrows() - 1)
{
hist_str += " ";
}
} }
print_value(
sarg,
hist_str,
json,
jsonOutput
);
} }
} }
else else
{ {
// only print cerr if not the --json option cerr << "Unrecognised option: " << sarg << endl;
if (sarg != "--json") exit(3);
{
cerr << "Unrecognised option: " << sarg << endl;
exit(3);
} else if (sarg == "--json" && last) {
// remove the trailing comma and space
json.pop_back();
json.pop_back();
}
} }
narg++; narg++;
} }
// if json mode is enabled tp["indices"] += idx;
if (jsonOutput) if (!jsonMode)
{
// close the json object
json.append("}");
}
if (index < nIndices && !jsonOutput)
{
cout << endl; cout << endl;
}
// cout << endl;
} // end nIndices } // end nIndices
// if json mode is enabled // append the timepoint to the the volumes array
if (jsonOutput) js["volumes"] += tp;
{
// close the json object
json.append("]}");
}
if (timepoint < nTimepoints - 1 && !jsonOutput)
{
cout << endl;
}
// cout << endl;
} // end timepoints } // end timepoints
if (jsonOutput) if (jsonMode){
{ // print the json with 4 spaces of indentation
// close the json object cout << js.dump(4) << endl;
json.append("]}"); }
cout << json;
} else {
cout << endl;
}
return 0; return 0;
} }
...@@ -953,11 +868,14 @@ int main(int argc, char *argv[]) ...@@ -953,11 +868,14 @@ int main(int argc, char *argv[])
Tracer tr("main"); Tracer tr("main");
string progname(argv[0]); string progname(argv[0]);
bool timeseriesMode(false); bool timeseriesMode(false);
bool jsonMode(false);
string indexMask(""); string indexMask("");
while (argc > 2 && (string(argv[1]) == "-t" || string(argv[1]) == "-K")) while (argc > 2 && (string(argv[1]) == "-t" || string(argv[1]) == "-K" || string(argv[1]) == "-json"))
{ {
if (string(argv[1]) == "-t") if (string(argv[1]) == "-t")
timeseriesMode = true; timeseriesMode = true;
if (string(argv[1]) == "-json")
jsonMode = true;
if (string(argv[1]) == "-K") if (string(argv[1]) == "-K")
{ {
indexMask = string(argv[2]); indexMask = string(argv[2]);
...@@ -971,7 +889,7 @@ int main(int argc, char *argv[]) ...@@ -971,7 +889,7 @@ int main(int argc, char *argv[])
return print_usage(progname); return print_usage(progname);
try try
{ {
return fmrib_main_float(argc, argv, timeseriesMode, indexMask); return fmrib_main_float(argc, argv, timeseriesMode, indexMask, jsonMode);
} }
catch (std::exception &e) catch (std::exception &e)
{ {
......
...@@ -53,6 +53,8 @@ options = [ ...@@ -53,6 +53,8 @@ options = [
{"option": "-v", "expected": "1000 1000.000000"}, {"option": "-v", "expected": "1000 1000.000000"},
{"option": "-V", "expected": "1000 1000.000000"}, {"option": "-V", "expected": "1000 1000.000000"},
{"option": "-m", "expected": "0.495922"}, {"option": "-m", "expected": "0.495922"},
{"option": "-m -a", "expected": "0.495922"},
{"option": "-m -a -n", "expected": "0.495922"},
{"option": "-M", "expected": "0.495922"}, {"option": "-M", "expected": "0.495922"},
{"option": "-s", "expected": "0.290744"}, {"option": "-s", "expected": "0.290744"},
{"option": "-S", "expected": "0.290744"}, {"option": "-S", "expected": "0.290744"},
...@@ -63,6 +65,12 @@ options = [ ...@@ -63,6 +65,12 @@ options = [
{"option": "-C", "expected": "4.498706 4.545873 4.613188"}, {"option": "-C", "expected": "4.498706 4.545873 4.613188"},
{"option": "-p 50", "expected": "0.482584"}, {"option": "-p 50", "expected": "0.482584"},
{"option": "-P 50", "expected": "0.482584"}, {"option": "-P 50", "expected": "0.482584"},
{"option": "-m -h 10 -c", "expected": "0.495922 101.000000 \n99.000000 \n108.000000 \n107.000000 \n102.000000 \n93.000000 \n99.000000 \n88.000000 \n95.000000 \n108.000000 \n 4.498706 4.545873 4.613188"},
{"option": "-m -h 10", "expected": "0.495922 101.000000 \n99.000000 \n108.000000 \n107.000000 \n102.000000 \n93.000000 \n99.000000 \n88.000000 \n95.000000 \n108.000000"},
{"option": "-h 10", "expected": "101.000000 \n99.000000 \n108.000000 \n107.000000 \n102.000000 \n93.000000 \n99.000000 \n88.000000 \n95.000000 \n108.000000"},
{"option": "-H 10 0 1", "expected": "98.000000 \n102.000000 \n107.000000 \n108.000000 \n102.000000 \n93.000000 \n99.000000 \n88.000000 \n95.000000 \n108.000000"},
{"option": "-l 0.25 -u 0.75 -m", "expected": "0.495150"},
{"option": "-d imageForDiff.nii.gz -m", "expected": "0.000000"},
] ]
# create a list of tests and expected results # create a list of tests and expected results
...@@ -82,55 +90,24 @@ tests_with_preoptions = [ ...@@ -82,55 +90,24 @@ tests_with_preoptions = [
"preoptions": "-K", "preoptions": "-K",
"mask": "mask.nii.gz", "mask": "mask.nii.gz",
"options": "-m", "options": "-m",
"expected": "0.948930 \n0.947671 \n1.003258 \n1.010696", "expected": "1.078044 \n1.028827 \n0.986428 \n1.020869",
}, },
{ {
"preoptions": "-t -K", "preoptions": "-t -K",
"mask": "mask.nii.gz", "mask": "mask.nii.gz",
"options": "-m", "options": "-m",
"expected": "0.459736 \n0.476035 \n0.504080 \n0.549485 \n0.489194 \n0.471636 \n0.499178 \n0.461211", "expected": "0.526682 \n0.515652 \n0.492337 \n0.511661 \n0.551362 \n0.513176 \n0.494091 \n0.509208",
}, },
{ {
"preoptions": "-t", "preoptions": "-t",
"mask": "", "mask": "",
"options": "-m", "options": "-m",
"expected": "0.496675 \n0.487950", "expected": "0.503236 \n0.504035",
}, },
] ]
# taken from fslchpixdim test
def create_image(shape, pixdim):
pixdim = list(pixdim)
data = np.random.random(shape).astype(np.float32)
hdr = nib.Nifti1Header()
hdr.set_data_dtype(np.float32)
hdr.set_data_shape(shape)
hdr.set_zooms(pixdim[:len(shape)])
return nib.Nifti1Image(data, np.eye(4), hdr)
def create_mask(input_img, n_rois=4):
'''
Create a mask image from the input image.
The mask image will have n_rois different values with random sizes randomly placed in the image.
'''
data = input_img.get_fdata()
mask = np.zeros_like(data)
for i in range(n_rois + 1):
roi_size = np.random.randint(1, data.size)
roi_value = i
roi = np.random.choice(data.size, roi_size, replace=False)
mask.flat[roi] = roi_value
return nib.Nifti1Image(mask, input_img.affine, input_img.header)
def test_fslstats(): def test_fslstats():
imgfile = 'test.nii.gz' imgfile = 'test.nii.gz'
shape = (10,10,10)
img = create_image(shape, [1] * len(shape))
img.to_filename(imgfile)
mask = create_mask(img)
mask.to_filename('mask.nii.gz')
# run the tests witoout preoptions # run the tests witoout preoptions
for test in tests: for test in tests:
...@@ -151,9 +128,6 @@ def test_fslstats(): ...@@ -151,9 +128,6 @@ def test_fslstats():
# run the tests with preoptions # run the tests with preoptions
imgfile = 'test_4d.nii.gz' imgfile = 'test_4d.nii.gz'
shape = (10,10,10, 2)
img = create_image(shape, [1] * len(shape))
img.to_filename(imgfile)
for test in tests_with_preoptions: for test in tests_with_preoptions:
cmd = f"fslstats {test['preoptions']} {test['mask']} {imgfile} {test['options']}" cmd = f"fslstats {test['preoptions']} {test['mask']} {imgfile} {test['options']}"
# remove any double spaces from empty test options # remove any double spaces from empty test options
...@@ -175,6 +149,4 @@ def test_fslstats(): ...@@ -175,6 +149,4 @@ def test_fslstats():
if __name__ == "__main__": if __name__ == "__main__":
if len(sys.argv) > 1:
os.chdir(sys.argv[1])
test_fslstats() test_fslstats()
File added
File added
File added
File added
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment