Scalefactors and intercepts

SPM Analyze and nifti1 images have scalefactors. nifti1 images also have intercepts. If A is an array in memory, and S is the array that will be written to disk, then:

R = (A - intercept) / scalefactor

and R == S if R is already the data dtype we need to write.

If we load the image from disk, we exactly recover S (and R). To get something approximating A (say Aprime) we apply the intercept and scalefactor:

Aprime = (S * scalefactor) + intercept

In a perfect world A would be exactly the same as Aprime. However scalefactor and intercept are floating point values. With floating point, if r = (a - b) / c; p = (r * c) + b it is not necessarily true that p == a. For example:

>>> import numpy as np
>>> a = 10
>>> b = np.e
>>> c = np.pi
>>> r = (a - b) / c
>>> p = (r * c) + b
>>> p == a

So there will be some error in this reconstruction, even when R is the same type as S.

More common is the situation where R is a different type from S. If R is of type r_dtype, S is of type s_dtype and cast_function(R, dtype) is some function that casts R to the desired type dtype, then:

R = (A - intercept) / scalefactor
S = cast_function(R, s_dtype)
R_prime = cast_function(S, r_dtype)
A_prime = (R_prime * scalefactor) + intercept

The type of R will depend on what numpy did for upcasting A, intercept, scalefactor.

In order that cast_function(S, r_dtype) can best reverse cast_function(R, s_dtype), the second needs to know the type of R, which is not stored. The type of R depends on the types of A and of intercept, scalefactor. We don’t know the type of A because it is not stored.

R is likely to be a floating point type because of the application of scalefactor and intercept. If (intercept, scalefactor) are not the identity (0, 1), then we can ensure that R is at minimum the type of the intercept, scalefactor by making these be at least 1D arrays, so that floating point types will upcast in R = (A - intercept) / scalefactor.

The cast of R to S and back to R_prime can lose resolution if the types of R and S have different resolution.

Our job is to select:

  • scalefactor

  • intercept

  • cast_function

such that we minimize some measure of difference between A and A_prime.