View Javadoc
1   package nom.tam.image.tile.operation;
2   
3   /*
4    * #%L
5    * nom.tam FITS library
6    * %%
7    * Copyright (C) 1996 - 2024 nom-tam-fits
8    * %%
9    * This is free and unencumbered software released into the public domain.
10   *
11   * Anyone is free to copy, modify, publish, use, compile, sell, or
12   * distribute this software, either in source code form or as a compiled
13   * binary, for any purpose, commercial or non-commercial, and by any
14   * means.
15   *
16   * In jurisdictions that recognize copyright laws, the author or authors
17   * of this software dedicate any and all copyright interest in the
18   * software to the public domain. We make this dedication for the benefit
19   * of the public at large and to the detriment of our heirs and
20   * successors. We intend this dedication to be an overt act of
21   * relinquishment in perpetuity of all present and future rights to this
22   * software under copyright law.
23   *
24   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25   * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26   * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
27   * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
28   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
29   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
30   * OTHER DEALINGS IN THE SOFTWARE.
31   * #L%
32   */
33  
34  import java.lang.reflect.Array;
35  import java.nio.Buffer;
36  import java.util.ArrayList;
37  import java.util.Arrays;
38  
39  import nom.tam.fits.FitsException;
40  import nom.tam.util.ArrayFuncs;
41  import nom.tam.util.type.ElementType;
42  
43  /**
44   * A base implementation of 2D image tile compression.
45   *
46   * @param <OPERATION> The generic type of tile operation that handles parallel processing
47   */
48  public abstract class AbstractTiledImageOperation<OPERATION extends ITileOperation> implements ITiledImageOperation {
49  
50      /** Image axes in Java array index order (is is last!). */
51      private int[] axes;
52  
53      /**
54       * Interprets the value of the BITPIX keyword in the uncompressed FITS image
55       */
56      private ElementType<Buffer> baseType;
57  
58      /** Tile dimensions in Java array index order (x is last!) */
59      private int[] tileAxes;
60  
61      private OPERATION[] tileOperations;
62  
63      private final Class<OPERATION> operationClass;
64  
65      /**
66       * Creates a new tiling foperation.
67       * 
68       * @param      operationClass the class of tile operation.
69       * 
70       * @deprecated                (<i>for internal use</i>) This constructor should have protected visibility.
71       */
72      @Deprecated
73      public AbstractTiledImageOperation(Class<OPERATION> operationClass) {
74          this.operationClass = operationClass;
75      }
76  
77      @Override
78      public ElementType<Buffer> getBaseType() {
79          return baseType;
80      }
81  
82      /**
83       * Returns the number of elements that a buffer must have to store the entire image.
84       * 
85       * @return The number of points in the full image.
86       */
87      public int getBufferSize() {
88          int bufferSize = 1;
89          for (int axisValue : axes) {
90              bufferSize *= axisValue;
91          }
92          return bufferSize;
93      }
94  
95      @Override
96      public int getImageWidth() {
97          return axes[axes.length - 1];
98      }
99  
100     @Override
101     public OPERATION getTileOperation(int i) {
102         return tileOperations[i];
103     }
104 
105     /**
106      * Sets the image dimensions, in Java array index order.
107      *
108      * @param axes Image dimensions in Java array index order (x is last!).
109      */
110     public void setAxes(int[] axes) {
111         this.axes = Arrays.copyOf(axes, axes.length);
112     }
113 
114     /**
115      * <p>
116      * Sets the tile dimension. Here the dimensions are in Java array index order, that is the x-dimension (width of
117      * tile) is last!
118      * </p>
119      * <p>
120      * Note, that because tile compression is essentially 2D, the tile sizes in higher dimensions will be forced to 1,
121      * even if specified otherwise by the argument (see
122      * <a href="https://heasarc.gsfc.nasa.gov/docs/software/fitsio/compression.html">FITSIO convention</a>).
123      * </p>
124      *
125      * @param  value         The tile dimensions in Java array index order (x is last!). Only up to the last 2
126      *                           components are considered. The rest will be assumed to have values equals to 1.
127      *
128      * @throws FitsException If the leading dimensions (before the last 2) have sizes not equal to 1
129      */
130     public void setTileAxes(int[] value) throws FitsException {
131         for (int i = value.length - 2; --i >= 0;) {
132             if (value[i] != 1) {
133                 throw new FitsException("Tile sizes in higher dimensions (>2) must be 1 as per the FITSIO convention (" + i
134                         + ":" + value[i] + ")");
135             }
136         }
137         tileAxes = Arrays.copyOf(value, value.length);
138     }
139 
140     /**
141      * Checks if the image size has been defined.
142      * 
143      * @return <code>true</code> if the size of the image to be tiled has been set, otherwise <code>false</code>.
144      */
145     protected boolean hasAxes() {
146         return axes != null;
147     }
148 
149     /**
150      * Checks if the tiling has been defined and tile sizes are set.
151      * 
152      * @return <code>true</code> if the tile sizes have been defined already, otherwise <code>false</code>
153      */
154     protected boolean hasTileAxes() {
155         return tileAxes != null;
156     }
157 
158     private int getBufferOffset(int[] index) {
159         int l = 0;
160         int blockSize = 1;
161         for (int i = index.length; --i >= 0;) {
162             l += index[i] * blockSize;
163             blockSize *= axes[i];
164         }
165         return l;
166     }
167 
168     /**
169      * Creates a tiling pattern for an image.
170      * 
171      * @param  init          the parameters that determine the tiling pattern
172      * 
173      * @throws FitsException if the parameters are invalid.
174      */
175     @SuppressWarnings("unchecked")
176     protected void createTiles(ITileOperationInitialisation<OPERATION> init) throws FitsException {
177         int[] offset = new int[axes.length]; // Tile start in image (Java index order)
178         int[] tileSize = new int[2]; // {w, h}
179         int pos = 0;
180 
181         int imLength = 1;
182         for (int i = axes.length; --i >= 0;) {
183             imLength *= axes[i];
184         }
185 
186         tileSize[1] = 1;
187 
188         // If tile is not defined along all axes, pad with 1.
189         if (tileAxes.length < axes.length) {
190             int[] tile = new int[axes.length];
191             System.arraycopy(tileAxes, 0, tile, tile.length - tileAxes.length, tileAxes.length);
192             Arrays.fill(tile, 0, tile.length - tileAxes.length, 1);
193             tileAxes = tile;
194         }
195 
196         ArrayList<OPERATION> opList = new ArrayList<>();
197 
198         // Create 2D tiles to cover image (in N dimensions, where N need not be 2)
199         for (int tileIndex = 0; pos < imLength; tileIndex++) {
200             // Calculate the actual size of the current tile
201             for (int i = Math.min(tileSize.length, tileAxes.length); --i >= 0;) {
202                 int k = axes.length - 1 - i;
203                 tileSize[i] = (offset[k] + tileAxes[k] > axes[k]) ? axes[k] - offset[k] : tileAxes[k];
204             }
205 
206             // Create the tile at the current buffer offset and tile size
207             OPERATION op = init.createTileOperation(tileIndex, new TileArea().start(ArrayFuncs.getReversed(offset)));
208             op.setDimensions(pos, tileSize[0], tileSize[1]);
209             opList.add(op);
210 
211             // Calculate the image indices where the next tile starts.
212             for (int k = axes.length; --k >= 0;) {
213                 offset[k] += tileAxes[k]; // Try next tile along the current dimension...
214                 if (offset[k] < axes[k]) {
215                     break; // OK, tile is within image bounds
216                 }
217                 if (k > 0) {
218                     offset[k] = 0; // Otherwise reset the tile pos in the subarray dimensions
219                 }
220             }
221 
222             // Calculate the buffer position where the next tile starts.
223             pos = getBufferOffset(offset);
224         }
225 
226         tileOperations = (OPERATION[]) Array.newInstance(operationClass, opList.size());
227         opList.toArray(tileOperations);
228 
229         init.tileCount(tileOperations.length);
230 
231         for (OPERATION op : tileOperations) {
232             init.init(op);
233         }
234     }
235 
236     /**
237      * Returns the dimensionality of the image.
238      * 
239      * @return the dimensinality of the image, that is the number of cartesian axes it contains.
240      */
241     protected int getNAxes() {
242         return axes.length;
243     }
244 
245     /**
246      * Returns the number of tile operations that are needed to cover a tiled image.
247      * 
248      * @return the number of tiles in the image.
249      */
250     protected int getNumberOfTileOperations() {
251         return tileOperations.length;
252     }
253 
254     /**
255      * Returns the reference to the tile dimensions array. The dimensions are stored in Java array index order, i.e.,
256      * the x-dimension (width) is last.
257      *
258      * @return The tile dimensions in Java array index order (x is last!).
259      */
260     protected int[] getTileAxes() {
261         return tileAxes;
262     }
263 
264     /**
265      * Returns an array of parallel tile oprations, which cover the full image.
266      * 
267      * @return an array of parallel tile operations.
268      */
269     protected OPERATION[] getTileOperations() {
270         return tileOperations;
271     }
272 
273     /**
274      * Sets the FITS element type that is contained in the tiles for this operation.
275      * 
276      * @param baseType the FITS element type of data in the tile.
277      */
278     protected void setBaseType(ElementType<Buffer> baseType) {
279         this.baseType = baseType;
280     }
281 }