AFNI program: obliquity_remover.py

Output of -help


Overview ~1~

This program is primarily for removing oblquity from an input
dataset's header, with the two important properties:

+ **not** regridding (= smoothing/interpolating) the data itself

+ preserving the coordinate origin, (x, y, z) = (0, 0, 0).

**Also** note this important bonus feature: the removed obliquity can
also be transferred to other datasets (called "child" datasets).
Doing this preserves the relative overlap of the main input and child
datasets, in the real terms of where they are in real/scanner space
when obliquity is taken into account.

This program makes a convenient step in preparing FMRI data
collections to be processed (e.g., before sswarper2, FreeSurfer,
afni_proc.py, etc.).  Users can provide the T1w anatomical reference
as the inset to have its obliquity removed, *while also* providing the
EPIs from the same session as child datasets.  In this way, the
relative overlap of the EPI and anatomical will be preserved during
processing.

auth = PA Taylor (SSCC, NIMH, NIH, USA)

------------------------------------------------------------------------

Usage ~1~

-inset INSET   :(req) name of the input dataset

-prefix PREFIX :(req) name of output dset

-child_dsets CD1 [CD2 CD3 ...] 
               :one or more datasets that can inherit the obliquity 
                that gets purged from the inset.

     ... and if using '-child_dsets', at least one of the following
         '-child_*' options must be included to specify their output
         naming:

-child_prefixes CP1 [CP2 CP3 ...] 
               :when using '-child_dsets ..', users can specify the
                output path+name for each child dset here. The number 
                of entries here must match number of child dsets.  
                This option canNOT be combined with '-child_outdir' 
                or '-child_suffix'

-child_outdir CO :when using '-child_dsets ..', users can specify a
                single output directory for all output childs.  If
                '-child_suffix ..' is not also provided, then the each
                output file will have the same name as its input (and
                in such a case, the CO should differ from each child 
                dset's original directory, unless '-overwrite' is used)

-child_suffix CS :when using '-child_dsets ..', users can specify a
                suffix to be inserted just before each output child's file
                extension.  Typically, uses will want to start the
                suffix with '_'.
                This option can be used along with '-child_outdir ..';
                otherwise, each child will be output in same directory as
                its original child dset

-do_qc DQ      :state whether to make QC images when using '-child_dsets ..',
                which means showing the overlap of each child dset with the
                main inset both before and after obliquity changes;
                allowed values are:  Yes, 1, No, 0
                (def: 'Yes')

-workdir WD    : working directory name, without path; the working dir
                will be subdirectory of the output location
                (def: name with random chars)

-do_clean DC   :state whether to clean up any intermediate files;
                allowed values are:  Yes, 1, No, 0
                (def: 'Yes')

-do_log        :add this opt to turn on making a text log of all the
                shell commands that are run when this program is
                executed.  Mainly for debugging purposes.

-help, -h      :display program help file

-hist          :display program history

-ver           :display program version number

-verb  VVV     :control verbosity (def: 1)

-show_valid_opts :show valid options for this program

------------------------------------------------------------------------

Notes ~1~

What is obliquity (and cardinality)? ~2~

When we acquire data in an MRI scanner, we define a field of view
(FOV) box within which we get the data.  That box takes the form of a
3D grid of voxels of a given size, each one a little tiny cube where
information is stored (either a single value or a time series).  There
is one particularly special corner of the FOV called the "dataset
origin", which is where we start mapping data from on the disk.  The 3
FOV edge lines that emanate from that corner define the major or
primary axes of the FOV; these are referred to as "i-, j-, k-" axes if
we are counting voxels along an edge, or "x-, y-, z-" axes if we are
counting in physical units like mm along the edge.

The scanner also has major axes.  Its z-axis is defined as the one
that is along the scanner bore.  Then, there is an xy-plane
perpendicular to that, where the x-axis is the same one that a person
lying flat on their back in the scanner would use; and the y-axis is
perpendicular to that, namely from the floor upwards through the
scanner (from the perspective of a person lying on their back,
effectively from the back of their head through their nose).

The definitions of obliquity and cardinality refer to whether the
major axes of these two FOV systems match up (that is, the z-axis of
each is parallel, the y-axis of each is parallel, etc.; this is
independent of consideration of coordinate origins), or not.

**Cardinal** coordinates are those in which the 3 major FOV axes match
those of the scanner (they are also sometimes called 'plumb').
'Cardinality' describes being in such a situation.

**Oblique** coordinates are those in which there is a relative
rotation between 1 or more of the major FOV axes; since it is hard to
move the scanner, typically that means that the FOV box was rotated
during acquisition.  'Obliquity' describes being in such a situation.


Why does obliquity matter? ~2~

What does it matter whether the data's coordinates are oblique or not?

Well, the presence of obliquity forces a choice when displaying the
data volumetrically, since the GUI defines its own rectangular FOV,
and *that* typically can't be rotated.

* For cardinal data, life is easy because the GUI FOV's major axes
  just match the dataset FOV axes naturally, and therefore clicking on
  a coordinate and navigating 'upwards' through a slice is consistent
  with scanner-based coordinates and directionality.  

* For oblique data, life is challenging because the dataset FOV should
  really be tilted to navigate in scanner-based coords, but the GUI 
  FOV window(s) typically can't do this. So, they have to make a choice:

  + Use the dataset 'as is' and ignore the obliquity, and treat the
    local data FOV as the 'real' one, in term of coordinate locations
    and moving around in slices; this does nothing to change any data
    values, it's just that the coordinate values are only relative
    within the data FOV and don't reflect the exact scanner
    coordinates.  When there are multiple datasets involved, with
    different grids and obliquity, then this can present a challenge
    for really seeing the actual overlaps of the datasets in 'real'
    scanner coordinates.

  + Make a new dataset by applying the obliquity rotation, which
    necessarily means regridding the data and slightly blurring it via
    interpolation; this changes the values shown in the GUI and
    smooths it, but the location of places will accurately reflect
    scanner coordinates. When there are multiple datasets involved in
    this scenario, since each of them has their obliquity applied, the
    data of each is shown in scanner coordinates and the overlap their
    should be represented in the same way here (though both dsets are
    interpolated).

Note that in surface renderings or non-slice-based viewers, the above
issues are less of a major consideration, because the visualization
FOV tends to not be locked to a grid with its own major axes in the
same way.

Different software deal with obliquity in different ways, choosing
different sides of the above tradeoff (namely, data not interpolated,
but the coordinates are local and not scanner coords; vs coordinates
are global/scanner coords, but the data values are
interpolated/smoothed).  The AFNI GUI chooses to not interpolate
(which has particular benefits when using the Graph Viewer to view
time series).  During processing, AFNI programs will tend to ignore
obliquity, in order to avoid interpolation.  Some other software tools
immediately apply obliquity at the start of processing, so that the
data become slightly blurred, but again, the coordinates are
consistent with those of the scanner. 


What is "deobliquing"---and better terminology for it? ~2~

The term "deoblique" is generally used to mean doing something to the
dataset so that there isn't a separate obliquity rotation between the
data and the original scanner coordinates to have to navigate.

*However*, there are different ways deobliquing can be done, with very
different consequences and trade-offs in each case; different
programs/software perform that action differently; and the term is
vague, not distinguishing the various methods.  Even within AFNI,
3dWarp and 3drefit each have a '-deoblique' option, but it does *very*
different things in each case.  Hence we try to avoid that term here,
to improve clarity henceforth.

Here are a couple of the main methods of deobliquing, which we label
with a preferred, more specific term:

+ **purge** obliquity: remove the obliquity (=extra rotation)
  information from the dataset header, without regridding (and
  therefore interpolating and smoothing) the data itself, being aware
  that this will effectively changing the coordinate locations of
  data.
  This is what '3drefit -deoblique ...' does (but we discuss better
  ways of navigating this kind of procedure below).

+ **apply** obliquity: use the obliquity (=extra rotation) and create
  a new dataset whose voxels and data locations are in original
  locations defined by the scanner coordinates, so there is no longer
  any extra rotation information in the header; this will necessarily
  require interpolating/regridding the data, which is a
  smoothing/blurring procedure.
  This is what '3dWarp -deoblique ...' does (and we use this on
  occasion to check/verify procedures).

Each approach can have its own use cases.  The present
obliquity_remover.py program purges obliquity from a primary input
dataset.  It does so in a way to preserve the input data's coordinate
origin.  It also outputs the value of the excised/purged obliquity
transform, so that it can still be used later, if needed.  And quite
usefully, it allows users to pass that removed obliquity (which again,
is a relative rotation of coordinates) to other datasets, so that
relative overlap can still be maintained, at a time when it is
convenient and appropriate.


Why purge obliquity (esp. with obliquity_remover.py specifically)? ~2~

It is generally convenient for the anatomical dataset to have its
obliquity removed in the way this program does it.

+ Different software deal with obliquity differently (e.g.,
  ignoring or applying it), and so when integrating different tools
  (like FS, SUMA and AFNI), this can be minorly annoying.  

+ *Purging* obliquity before processing (or pre-processing) can remove
  an unnecessary resampling/interpolation/blur procedure during
  processing itself.  In contrast, *applying* obliquity leads to
  resampling and interpolation, and hence blurring of the anatomical
  dataset.

+ When one has many different types of datasets in a given session
  (like anatomical and EPI/phase/blip dsets), it is convenient to
  retain relative overlap.  This program not only purges obliquity
  from one dataset, but it can append that rotation to other relevant
  datasets in that session, to preserve overlap and relative locations.

  While in many cases AFNI alignment programs can overcome a fair bit
  of relative rotation, some cases are more extreme (looking at you,
  slab EPI datasets!) and so really benefit from maintaining original
  overlap. Also, the stability of all alignment procedures (AFNI's or
  other tools') benefits from closer starting overlap of datasets, and
  who needs to add more uncertainty into data processing?


When should I remove obliquity from the *T1w/anatomical* dataset? ~2~

We would recommend removing obliquity from the *anatomical* dataset in
the following cases (which is the majority, in FMRI and MRI):

+ When processing anatomical data in isolation (i.e., for its own sake,
  not just as a supplementary dataset for FMRI studies);

+ When processing FMRI data, and using either the participant's
  anatomical or a reference template dataset for the final space. NB:
  these are the majority of FMRI processing cases. 

All the benefits of purging obliquity from the prior subsection apply.

Additionally, after removing obliquity like this, the anatomical
should overlap better with any reference template dataset (since it
will "sit more squarely" within the FOV).

In this case, the inset for obliquity_remover.py would be the
anatomical data, and the child dsets would be accompanying FMRI,
phase, or blip up/down datasets.


When should I remove obliquity from the *EPI/functional* dataset? ~2~

We would recommend removing obliquity from the *EPI* dataset in the
following cases:

+ When processing FMRI data, and using the EPI dataset itself for the
  final space, particularly in high-resolution functional studies.

In this case, the inset for obliquity_remover.py would be the EPI
data, and the child dset would be the anatomical.


------------------------------------------------------------------------

Examples ~1~

 1) Remove obliquity from a dset (making it cardinal):

    obliquity_remover.py                                                  \
        -inset           sub-017_T1w.nii.gz                               \
        -prefix          sub-017_T1w_CARD.nii.gz

 2) Remove obliquity from a dset, and pass it along to its associated 
    EPI datasets; those EPI datasets might already have obliquity (in which
    case they just end up with a new obliquity/rotational value), or not (in 
    which case they go from being cardinal to oblique):

    obliquity_remover.py                                                  \
        -inset           anat/sub-017_T1w.nii.gz                          \
        -prefix          anat/sub-017_T1w_CARD.nii.gz                     \
        -child_dsets     func/sub-017_task-rest_run-01_bold.nii.gz        \
                         func/sub-017_task-rest_run-02_bold.nii.gz        \
                         func/sub-017_task-rest_run-03_bold.nii.gz        \
        -child_prefixes  func/sub-017_task-rest_run-01_bold_NEWOBL.nii.gz \
                         func/sub-017_task-rest_run-02_bold_NEWOBL.nii.gz \
                         func/sub-017_task-rest_run-03_bold_NEWOBL.nii.gz

 3) Same as #2, but with a succinct method of adding a suffix to each child:
           
    obliquity_remover.py                                                  \
        -inset           anat/sub-017_T1w.nii.gz                          \
        -prefix          anat/sub-017_T1w_CARD.nii.gz                     \
        -child_dsets     func/sub-017_task-rest_run-01_bold.nii.gz        \
                         func/sub-017_task-rest_run-02_bold.nii.gz        \
                         func/sub-017_task-rest_run-03_bold.nii.gz        \
        -child_suffix    _NEWOBL

 4) Same as #3, but putting each output child into a new dir:

    obliquity_remover.py                                                  \
        -inset           anat/sub-017_T1w.nii.gz                          \
        -prefix          anat/sub-017_T1w_CARD.nii.gz                     \
        -child_dsets     func/sub-017_task-rest_run-01_bold.nii.gz        \
                         func/sub-017_task-rest_run-02_bold.nii.gz        \
                         func/sub-017_task-rest_run-03_bold.nii.gz        \
        -child_suffix    _NEWOBL                                          \
        -child_outdir    func_newobl

 5) Same as #4, but renaming in the outputs in a particular way: all
    output datasets have the same filenames but different paths. This
    might be quite useful when doing group processing in a way that
    each major step outputs the data in parallel directory trees:

    obliquity_remover.py                                                  \
        -inset            p1/anat/sub-017_T1w.nii.gz                      \
        -prefix           p2/anat/sub-017_T1w.nii.gz                      \
        -child_dsets      p1/func/sub-017_task-rest_run-*_bold.nii.gz     \
        -child_outdir     p2/func




This page auto-generated on Wed Jan 7 07:17:35 PM EST 2026