/* Last edited on 2012-12-25 01:46:24 by stolfilocal */ /* Representation and basic tools for multidimensional signals with {float}-valued samples. */ package minetto.utils; import java.lang.Math; import java.lang.Float; import java.util.Arrays; import minetto.utils.FloatSignal; public class FloatSignal { int nd; /* Number of indices (domain `dimensions'). */ public final int start; /* Position in {smp} of the sample with indices {[0,0,...,0]}. */ public final int[] size; /* The signal has {size[r]} samples along dimension {r}. */ public final int[] step; /* Distance in {smp} between successive samples along dimension {r} is {step[r]}. */ public final float[] smp; /* Sample storage area. */ /* An instance of this class is a discrete finite multi-dimensional signal with {float}-valued samples. Samples of the signal are indexed by {nd} indices {ix[0..nd-1]}, each index defining one `dimension' of the signal's domain. For example a video signal could be stored as a 4-dimensional signal where {ix[0]} selects the frame in the video, {ix[1],ix[2]} select the column and row in that frame, and {ix[3]} selects the color channel. The samples are stored in the array {smp}. The element with indices {ix = [0,0,..,0]} is stored in {smp[start]}. The range of each index {ix[r]} is {0..size[r]-1}. Incrementing an index {ix[r]} by 1 changes the position of the element in {smp} from some {smp[p]} to {smp[p + step[r]]}; that step may be negative or even zero. The {smp} array may be shared with other signals. Thus one can take mutable slices of a signal (including diagonal ones), permute its indices, flip the direction of any axis, extract every {m}th sample, etc., all in constant time. A signal has no samples if and only if {size[0..nd-1]} includes at least one zero. Note that {size[0..nd-1]} are meaningful even in this case. Note that a signal with zero indices isnot empty: it has exactly one sample, indexed by the empty index tuple. */ /* ---------------------------------------------------------------------- Creates a signal from its sample array {smp} and indexing parameters {nd,start,size,step}. The arrays {size} and {step} are copied, but {smp} is not copied. If the array has no samples (that is, if {size[r]} is zero for some {r}), then {smp} may be null. */ public FloatSignal (int nd, int[] size, int start, int[] step, float[] smp) { this.nd = nd; this.start = start; this.size = Arrays.copyOf(size, size.length); this.step = Arrays.copyOf(step, step.length); this.smp = smp; } /* ---------------------------------------------------------------------- Creates a signal with {nd} domain dimensions and {size[r]} samples along each dimension {r}, for {r} in {0..nd-1}. Allocates a new sample array of sufficient size. Sets up the base position {start} and the {step} vector so that the samples are packed in distinct positions without gaps. */ public FloatSignal (int nd, int[] size) { this.nd = nd; this.start = 0; this.size = Arrays.copyOf(size, size.length); this.step = new int[nd]; int nel = 1; for (int r = 1; r < nd; r++) { assert(size[r] >= 0); this.step[r] = nel; nel *= size[r]; } this.smp = (nel == 0 ? null : new float[nel] ); } /* ---------------------------------------------------------------------- Returns {true} iff the indexing parameters of {sig} are consistent. */ public boolean is_consistent () { /* Length of {step,size} must be {nd}: */ if (nd < 0) { return false; } if (size.length != nd) { return false; } if (step.length != nd) { return false; } /* Check if signal has any samples: */ boolean is_empty = false; for (int r = 0; r < nd; r++) { if (size[r] < 0) { return false; } if (size[r] == 0) { is_empty = true; } } /* If there are no samples, the other indexing parameters are irrelevant and {smp} may even be null: */ if (is_empty) { return true; } /* If there are samples, there mustbe {smp}, and the indexing formula must be meaningful: */ if (smp == null) { return false; } int min_pos = start; int max_pos = start; for (int r = 0; r < nd; r++) { if (size[r] > 0) { int last_incr = step[r]*(size[r]-1); /* Increment for max index in dimension {r}. */ if (step[r] < 0) { min_pos += last_incr; } else { max_pos += last_incr; } } } if ((min_pos < 0) || (max_pos >= smp.length)) { return false; } return true; } /* === SAMPLE INDEXING ================================================== */ /* ---------------------------------------------------------------------- Returns the position in {smp} of the sample with indices {[ix[0],ix[1],...,ix[nd-1]]}. */ public int getSampleIndex(int[] ix) { assert(ix.length == nd); int pos = start; for (int r = 0; r < nd; r++) { assert((ix[r] >= 0) && (ix[r] < size[r])); pos += ix[r]*step[r]; } return pos; } /* ---------------------------------------------------------------------- Returns the sample with indices {[ix[0],ix[1],...,ix[nd-1]]}. */ public float getSample(int[] ix) { return smp[getSampleIndex(ix)]; } /* ---------------------------------------------------------------------- Stores {val} into the sample with indices {[ix[0],ix[1],...,ix[nd-1]]}. */ public void setSample(int[] ix, float val) { smp[getSampleIndex(ix)] = val; } /* ---------------------------------------------------------------------- Modifies the index tuple {ix[0..nd-1]} to the next valid tuple in lexicographic order that is compatible with the sizes {size[0..nd-1]}. Assumes that {ix} is a valid tuple to start with. Returns {false} if it succeeds. If there is no further valid tuple, resets {ix} to all zeros and returns {true}. If the {pos} array is not null, will also update each position {pos[i]} using the step vector {step[i]}. */ public static boolean next_index_tuple(int nd, int[] ix, int[] size, int[] pos, int[][] step) { assert(ix.length == nd); assert(size.length == nd); int r = nd - 1; while (r >= 0) { /* Try to bump {ix[r]}: */ if (ix[r]+1 < size[r]) { /* We can increment {ix[r]}: */ ix[r]++; if (pos != null) { for (int i = 0; i < pos.length; i++) { if (step[i] != null) { pos[i] += step[i][r]; } } } return false; } else { /* We must reset {ix[r]} and carry on: */ int ixr = ix[r]; if (ixr > 0) { if (pos != null) { for (int i = 0; i < pos.length; i++) { if (step[i] != null) { pos[i] -= ixr*step[i][r]; } } } ix[r] = 0; } } r--; } return true; } }