Dear FSL(view) author(s),
As a part of NeuroSynth (http://neurosynth.org) hackathon
(http://hackathon.neurosynth.org) I have developed a patch to fslview to
support atlases providing various, no necessarily "probability",
statistic. I would like to include this patch at least in a
(Neuro)Debian distribution of fslview, and ideally I would appreciate if
you adopt it upstream for your future releases of fslview. Although
patch changes fslview's API, it should (I hope) not effect how existing
Label and Probabilistic atlases are handled, exposed to the user.
Please find patch attached, and the description excerpt below.
I would greatly appreciate your feedback (e.g. "it will not be adopted
upstream, because ..."/"we will adopt it with following changes..."/etc.).
FSLView is capable of consuming various atlases, but unfortunately
Probabilistic atlas is too restrictive -- it assumes values from 0 to 100,
which forbids construction of atlases which might provide a different statistic
describing any given voxel.
Usecase:
E.g. neurosynth.org provides posterior probabilities for a set of terms
harvested from publications. Since those are not "population percents",
statistic values expressed as z-scores could go as high as 20, and
visualization of corresponding p-values is not visually appealing.
Moreover z-scores are easier to comprehend in this context, and they could be
as well negative (to signal areas which were unlikely to be reported for any
given location).
Changes:
Generalize ProbabilisticAtlas into StatisticAtlas, which gets parameterized
with additional parameters previously hardcoded in the "Probabilistic" logic.
Corresponding atlas's .xml file could still be "Probabilistic" but also could
be of type "Statistic" with additional parameters:
upper, lower (float) -- values for thresholding of the maps ( 5, 100 for Probabilistic)
precision (int) -- to which decimal precision (10^precision) to consider/report
the statistic value (0 for Probabilistic, where int % are reported)
statistic (string)-- name of the statistic which would prepend its reports (empty for
Probabilistic, "z" in case of neurosynth's z-score)
units (string)-- reported units ("%" in Probabilistic, "" in neurosynth)
Such methods as "getProbability" were left with old names for now but now would
provide float instead of unsigned int.
--
Yaroslav O. Halchenko, Ph.D.
http://neuro.debian.net http://www.pymvpa.org http://www.fail2ban.org
Senior Research Associate, Psychological and Brain Sciences Dept.
Dartmouth College, 419 Moore Hall, Hinman Box 6207, Hanover, NH 03755
Phone: +1 (603) 646-9834 Fax: +1 (603) 646-1419
WWW: http://www.linkedin.com/in/yarik
From: Yaroslav Halchenko <[log in to unmask]>
Subject: Generalize ProbabilisticAtlas into StatisticAtlas
FSLView is capable of consuming various atlases, but unfortunately
Probabilistic atlas is too restrictive -- it assumes values from 0 to 100,
which forbids construction of atlases which might provide a different statistic
describing any given voxel.
Usecase:
E.g. neurosynth.org provides posterior probabilities for a set of terms
harvested from publications. Since those are not "population percents",
statistic values expressed as z-scores could go as high as 20, and
visualization of corresponding p-values is not visually appealing.
Moreover z-scores are easier to comprehend in this context, and they could be
as well negative (to signal areas which were unlikely to be reported for any
given location).
Changes:
Generalize ProbabilisticAtlas into StatisticAtlas, which gets parameterized
with additional parameters previously hardcoded in the "Probabilistic" logic.
Corresponding atlas's .xml file could still be "Probabilistic" but also could
be of type "Statistic" with additional parameters:
upper, lower (float) -- values for thresholding of the maps ( 5, 100 for Probabilistic)
precision (int) -- to which decimal precision (10^precision) to consider/report
the statistic value (0 for Probabilistic, where int % are reported)
statistic (string)-- name of the statistic which would prepend its reports (empty for
Probabilistic, "z" in case of neurosynth's z-score)
units (string)-- reported units ("%" in Probabilistic, "" in neurosynth)
Such methods as "getProbability" were left with old names for now but now would
provide float instead of unsigned int.
Origin: NeuroDebian
Last-Update: 2013-05-26
--- a/src/fslview/atlas.cpp
+++ b/src/fslview/atlas.cpp
@@ -10,7 +10,9 @@
#include "filemanager.h"
#include "preferences.h"
+#include <cmath>
#include <sstream>
+#include <iomanip> // std::setprecision
#include <map>
using namespace std;
@@ -47,9 +49,11 @@ struct Atlas::Implementation
Atlas::Type m_type;
};
-Atlas::Handle ProbabilisticAtlas::create(const ImageStore& i, const ImageStore& s, const string& n)
+Atlas::Handle StatisticAtlas::create(const ImageStore& i, const ImageStore& s, const string& n,
+ const float lo, const float up, const int p,
+ const std::string& st, const std::string& u)
{
- Handle atlas(new ProbabilisticAtlas(i, s, n));
+ Handle atlas(new StatisticAtlas(i, s, n, lo, up, p, st, u));
return atlas;
}
@@ -66,10 +70,17 @@ Atlas::Atlas(const ImageStore& i, const
m_impl->m_summary = s.at(0);
}
-ProbabilisticAtlas::ProbabilisticAtlas(const ImageStore& i, const ImageStore& s, const string& n):
- Atlas(i, s, n)
+StatisticAtlas::StatisticAtlas(const ImageStore& i, const ImageStore& s, const string& n,
+ const float lo, const float up, const int p,
+ const std::string& st, const std::string& u):
+ Atlas(i, s, n),
+ m_lower(lo),
+ m_upper(up),
+ m_precision(p),
+ m_statisticName(st),
+ m_units(u)
{
- m_impl->m_type=Probabilistic;
+ m_impl->m_type=Statistic;
}
LabelAtlas::LabelAtlas(const ImageStore& i, const ImageStore& s, const string& n):
@@ -169,7 +180,7 @@ string LabelAtlas::getDescription(float
return text.str();
}
-unsigned int LabelAtlas::getProbability(unsigned int structure, float x, float y, float z) const
+float LabelAtlas::getProbability(unsigned int structure, float x, float y, float z) const
{
ImageInfo::Handle info(m_impl->m_image->getInfo());
short tx(0), ty(0), tz(0);
@@ -179,39 +190,43 @@ unsigned int LabelAtlas::getProbability(
( (vol->value(tx, ty, tz) == structure) ? 100 : 0 ) : 0;
}
-string ProbabilisticAtlas::getDescription(float x, float y, float z) const
+string StatisticAtlas::getDescription(float x, float y, float z) const
{
ImageInfo::Handle info(m_impl->m_image->getInfo());
short tx(0), ty(0), tz(0);
int nvols(info->inqNumVolumes());
+ float precision10 = std::pow(10, m_precision);
info->mmToVoxCoord(x, y, z, tx, ty, tz);
- multimap<int,string> labels;
+ multimap<float,string> labels;
for(int v = 0; v < nvols; ++v) {
Volume::Handle vol(m_impl->m_image->getVolume(v));
- int prob( info->isValidCoordinate(tx, ty, tz) ?
- int(vol->value(tx, ty, tz)) : 0 );
-
- if( prob > 0 ) {
+ float stat( info->isValidCoordinate(tx, ty, tz) ?
+ vol->value(tx, ty, tz) : 0 );
+
+ if(nearbyint(stat * precision10) != 0) {
Implementation::LabelsConstIterator pos = m_impl->findLabel(v);
if(pos != m_impl->m_labels.end())
- labels.insert(make_pair(prob, pos->second));
+ labels.insert(make_pair(stat, pos->second));
}
}
unsigned int count(0);
ostringstream text;
-
text << "<b>" << m_impl->m_name << "</b><br>";
-
- for(map<int,string>::reverse_iterator it = labels.rbegin();
+ for(map<float,string>::reverse_iterator it = labels.rbegin();
it != labels.rend(); ++it) {
if(count++)
text << ", ";
- text << it->first << "% " << it->second;
+ if(m_statisticName.length())
+ text << m_statisticName << "=";
+ text << std::setprecision(m_precision) << std::fixed << it->first;
+ if(m_units.length())
+ text << m_units;
+ text << " " << it->second;
}
if(!count)
@@ -244,17 +259,17 @@ string Atlas::inqStructureNameByIndex(un
return name;
}
-unsigned int ProbabilisticAtlas::getProbability(unsigned int index, float x, float y, float z) const
+float StatisticAtlas::getProbability(unsigned int index, float x, float y, float z) const
{
ImageInfo::Handle info(m_impl->m_image->getInfo());
short tx(0), ty(0), tz(0);
info->mmToVoxCoord(x, y, z, tx, ty, tz);
Volume::Handle vol(m_impl->m_image->getVolume(index));
return info->isValidCoordinate(tx, ty, tz) ?
- int(vol->value(tx, ty, tz)) : 0;
+ vol->value(tx, ty, tz) : 0;
}
-float ProbabilisticAtlas::getAverageProbability(Image::Handle mask, unsigned int index) const
+float StatisticAtlas::getAverageProbability(Image::Handle mask, unsigned int index) const
{
Volume::Handle m(mask->getVolume(0));
Volume::Handle p(m_impl->m_image->getVolume(index));
@@ -283,7 +298,7 @@ float ProbabilisticAtlas::getAverageProb
return total / sum;
}
-unsigned int ProbabilisticAtlas::inqNumLabels() const
+unsigned int StatisticAtlas::inqNumLabels() const
{
return m_impl->m_labels.size();
}
--- a/src/fslview/atlas.h
+++ b/src/fslview/atlas.h
@@ -45,7 +45,7 @@ public:
typedef std::map<int, std::string>::const_iterator ConstLabelIterator;
typedef std::vector<Image::Handle> ImageStore;
- enum Type { Unknown, Label, Probabilistic };
+ enum Type { Unknown, Label, Statistic, Probabilistic };
void addLabel(int, const std::string&);
void addCentre(int, short, short, short, short);
@@ -63,7 +63,7 @@ public:
void selectCompatibleImages(const Image::Handle&);
virtual std::string getDescription(float, float, float) const = 0;
- virtual unsigned int getProbability(unsigned int, float, float, float) const = 0;
+ virtual float getProbability(unsigned int, float, float, float) const = 0;
virtual float getAverageProbability(Image::Handle, unsigned int) const = 0;
Cursor::Handle getCursor(const Image::Handle&, int) const;
@@ -81,20 +81,30 @@ protected:
const std::auto_ptr<Implementation> m_impl;
};
-class ProbabilisticAtlas: public Atlas
+class StatisticAtlas: public Atlas
{
public:
virtual unsigned int inqNumLabels() const;
virtual std::string getDescription(float, float, float) const;
- virtual unsigned int getProbability(unsigned int, float, float, float) const;
+ virtual float getProbability(unsigned int, float, float, float) const;
virtual float getAverageProbability(Image::Handle, unsigned int) const;
- virtual Type inqType() const { return Atlas::Probabilistic; }
+ virtual Type inqType() const { return Atlas::Statistic; }
- static Handle create(const ImageStore&, const ImageStore&, const std::string&);
+ virtual float getLower() const { return m_lower; }
+ virtual float getUpper() const { return m_upper; }
+
+ static Handle create(const ImageStore&, const ImageStore&, const std::string&,
+ const float, const float, const int, const std::string&, const std::string&);
+protected:
+ const float m_lower, m_upper;
+ const int m_precision;
+ const std::string m_statisticName;
+ const std::string m_units;
private:
- ProbabilisticAtlas(const ImageStore&, const ImageStore&, const std::string&);
+ StatisticAtlas(const ImageStore&, const ImageStore&, const std::string&,
+ const float, const float, const int, const std::string&, const std::string&);
};
class LabelAtlas: public Atlas
@@ -103,7 +113,7 @@ public:
virtual unsigned int inqNumLabels() const;
virtual std::string getDescription(float, float, float) const;
- virtual unsigned int getProbability(unsigned int, float, float, float) const;
+ virtual float getProbability(unsigned int, float, float, float) const;
virtual float getAverageProbability(Image::Handle, unsigned int) const;
virtual Type inqType() const { return Atlas::Label; }
--- a/src/fslview/filemanager.cpp
+++ b/src/fslview/filemanager.cpp
@@ -405,6 +405,10 @@ Atlas::Handle FileManager::readXMLAtlas(
Atlas::Type type(Atlas::Unknown);
Atlas::ImageStore images, summaries;
+ float lower=-100, upper=100;
+ int precision=0;
+ std::string statisticName("");
+ std::string units("");
node = root.firstChild();
while ( !node.isNull() ) {
@@ -418,13 +422,45 @@ Atlas::Handle FileManager::readXMLAtlas(
if ( !textChild.isNull() ) {
atlasname = string(textChild.data().ascii());
}
+ } else if ( currentNode.nodeName() == "statistic" ) {
+ QDomText textChild = currentNode.firstChild().toText();
+ if ( !textChild.isNull() ) {
+ statisticName = string(textChild.data().ascii());
+ }
+ } else if ( currentNode.nodeName() == "units" ) {
+ QDomText textChild = currentNode.firstChild().toText();
+ if ( !textChild.isNull() ) {
+ units = string(textChild.data().ascii());
+ }
+ } else if ( currentNode.nodeName() == "lower" ) {
+ QDomText textChild = currentNode.firstChild().toText();
+ if ( !textChild.isNull() ) {
+ lower = ::atof(textChild.data().ascii());
+ }
+ } else if ( currentNode.nodeName() == "upper" ) {
+ QDomText textChild = currentNode.firstChild().toText();
+ if ( !textChild.isNull() ) {
+ upper = ::atof(textChild.data().ascii());
+ }
+ } else if ( currentNode.nodeName() == "precision" ) {
+ QDomText textChild = currentNode.firstChild().toText();
+ if ( !textChild.isNull() ) {
+ precision = ::atoi(textChild.data().ascii());
+ }
} else if ( currentNode.nodeName() == "type" ) {
QDomText textChild = currentNode.firstChild().toText();
if ( !textChild.isNull() ) {
- if(textChild.data() == "Label")
+ if(textChild.data() == "Label")
type = Atlas::Label;
- else
- type = Atlas::Probabilistic;
+ else if(textChild.data() == "Probabilistic") {
+ type = Atlas::Statistic;
+ units = "%";
+ lower = 0;
+ upper = 100;
+ precision = 0;
+ }
+ else
+ type = Atlas::Statistic;
}
} else if ( currentNode.nodeName() == "images" ) {
images.push_back(readImage(atlasdir, currentNode, "imagefile"));
@@ -441,8 +477,10 @@ Atlas::Handle FileManager::readXMLAtlas(
switch(type)
{
- case Atlas::Probabilistic:
- atlas = ProbabilisticAtlas::create(images, summaries, atlasname);
+ case Atlas::Statistic:
+ atlas = StatisticAtlas::create(images, summaries, atlasname,
+ lower, upper, precision,
+ statisticName, units);
break;
case Atlas::Label:
atlas = LabelAtlas::create(images, summaries, atlasname);
--- a/src/fslview/atlasoptionsdialog.cpp
+++ b/src/fslview/atlasoptionsdialog.cpp
@@ -71,7 +71,7 @@ void AtlasOptionsDialog::setAtlas(Atlas:
// m_structureList->insertItem("None");
for(Atlas::ConstLabelIterator it = m_atlas->begin(); it != m_atlas->end(); ++it)
m_structureList->insertItem(it->second.c_str());
- if( atlas->inqType() != Atlas::Probabilistic ) {
+ if( atlas->inqType() != Atlas::Statistic ) {
m_superimpose->setChecked(false);
m_superimpose->setEnabled(false);
} else {
@@ -88,8 +88,15 @@ void AtlasOptionsDialog::accept()
void AtlasOptionsDialog::addStructure()
{
if( Image::Handle simage = getStructureImage(m_options.structure()) ) {
+ Atlas::Handle atlas( m_atlases->getAtlasByName(m_atlasSelection->currentText().toUtf8().constData()) );
+ float lower=5, upper=100;
+ if( atlas->inqType() == Atlas::Statistic ) {
+ boost::shared_ptr< StatisticAtlas > stAtlas = boost::dynamic_pointer_cast<StatisticAtlas>(atlas);
+ lower = stAtlas->getLower();
+ upper = stAtlas->getUpper();
+ }
m_overlayList->getImageGroup()->addOverlay(simage);
- m_overlayList->getActiveMetaImage()->getDs()->inqBriCon()->setRange(5, 100);
+ m_overlayList->getActiveMetaImage()->getDs()->inqBriCon()->setRange(lower, upper);
m_overlayList->setLookUpTable(LookUpTable::redYellow());
}
}
@@ -99,20 +106,23 @@ void AtlasOptionsDialog::structureSelect
m_options.structure(i);
m_options.structureName(m_structureList->currentText().toUtf8().constData());
+ Atlas::Handle atlas( m_atlases->getAtlasByName(m_atlasSelection->currentText().toUtf8().constData()) );
+
if(m_options.locate()) {
- Atlas::Handle atlas( m_atlases->getAtlasByName(m_atlasSelection->currentText().toUtf8().constData()) );
Image::Handle refimage( m_overlayList->getActiveMetaImage()->getImage() );
m_cursor->setCursor( atlas->getCursor(refimage, m_options.structure()) );
}
if(m_options.superimpose()) {
+ boost::shared_ptr< StatisticAtlas > stAtlas = boost::dynamic_pointer_cast<StatisticAtlas>(atlas);
if(m_probImage)
m_overlayList->getImageGroup()->remOverlay(m_probImage);
if( (m_probImage = getStructureImage(m_options.structure())) &&
m_overlayList->getMainImage()->getInfo()->isCompatible(m_probImage->getInfo()) ) {
m_overlayList->getImageGroup()->addOverlay(m_probImage);
- m_overlayList->getActiveMetaImage()->getDs()->inqBriCon()->setRange(5, 100);
+ m_overlayList->getActiveMetaImage()->getDs()->inqBriCon()->setRange(
+ stAtlas->getLower(), stAtlas->getUpper());
m_overlayList->setLookUpTable(LookUpTable::redYellow());
}
} else {
|