1 package nom.tam.fits;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 import java.io.IOException;
35 import java.nio.Buffer;
36 import java.util.Arrays;
37
38 import nom.tam.fits.header.Bitpix;
39 import nom.tam.fits.header.NonStandard;
40 import nom.tam.fits.header.Standard;
41 import nom.tam.image.ImageTiler;
42 import nom.tam.image.StandardImageTiler;
43 import nom.tam.util.ArrayDataInput;
44 import nom.tam.util.ArrayDataOutput;
45 import nom.tam.util.ArrayFuncs;
46 import nom.tam.util.ComplexValue;
47 import nom.tam.util.Cursor;
48 import nom.tam.util.FitsEncoder;
49 import nom.tam.util.Quantizer;
50 import nom.tam.util.RandomAccess;
51 import nom.tam.util.array.MultiArrayIterator;
52 import nom.tam.util.type.ElementType;
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class ImageData extends Data {
71
72 private static final String COMPLEX_TYPE = "COMPLEX";
73
74
75
76
77 protected static class ArrayDesc {
78
79 private final Class<?> type;
80 private int[] dims;
81
82 private Quantizer quant;
83
84 private int complexAxis = -1;
85
86 ArrayDesc(int[] dims, Class<?> type) {
87 this.dims = dims;
88 this.type = type;
89
90 if (ComplexValue.class.isAssignableFrom(type)) {
91 complexAxis = dims.length;
92 }
93 }
94 }
95
96
97
98
99 protected class ImageDataTiler extends StandardImageTiler {
100
101 ImageDataTiler(RandomAccess o, long offset, ArrayDesc d) {
102 super(o, offset, d.dims, d.type);
103 }
104
105 @Override
106 protected Object getMemoryImage() {
107 return dataArray;
108 }
109 }
110
111
112
113
114 private long byteSize;
115
116
117
118
119
120
121 private Object dataArray;
122
123
124 private ArrayDesc dataDescription;
125
126
127 private StandardImageTiler tiler;
128
129
130
131
132 public ImageData() {
133 this(new byte[0]);
134 }
135
136
137
138
139
140
141
142
143
144 public ImageData(Header h) throws FitsException {
145 dataDescription = parseHeader(h);
146 }
147
148
149
150
151
152
153
154
155
156 public ImageData(Object x) throws IllegalArgumentException {
157 try {
158 checkCompatible(x);
159 } catch (FitsException e) {
160 throw new IllegalArgumentException(e.getMessage(), e);
161 }
162
163 dataDescription = new ArrayDesc(ArrayFuncs.getDimensions(x), ArrayFuncs.getBaseClass(x));
164 dataArray = x;
165 byteSize = FitsEncoder.computeSize(x);
166 }
167
168 @Override
169 protected void loadData(ArrayDataInput in) throws IOException, FitsException {
170 if (tiler != null) {
171 dataArray = tiler.getCompleteImage();
172 } else {
173 dataArray = ArrayFuncs.newInstance(getType(), getDimensions());
174 in.readImage(dataArray);
175 }
176 }
177
178 @Override
179 public void read(ArrayDataInput in) throws FitsException {
180 tiler = (in instanceof RandomAccess) ?
181 new ImageDataTiler((RandomAccess) in, ((RandomAccess) in).getFilePointer(), dataDescription) :
182 null;
183 super.read(in);
184 }
185
186 @Override
187 protected Object getCurrentData() {
188 return dataArray;
189 }
190
191
192
193
194
195
196
197
198 public StandardImageTiler getTiler() {
199 return tiler != null ? tiler : new ImageDataTiler(null, 0, dataDescription);
200 }
201
202
203
204
205
206
207 public void setBuffer(Buffer data) {
208 ElementType<Buffer> elementType = ElementType.forClass(getType());
209 dataArray = ArrayFuncs.newInstance(getType(), getDimensions());
210 MultiArrayIterator<?> iterator = new MultiArrayIterator<>(dataArray);
211 Object array = iterator.next();
212 while (array != null) {
213 elementType.getArray(data, array);
214 array = iterator.next();
215 }
216 tiler = new ImageDataTiler(null, 0, dataDescription);
217 }
218
219 @SuppressWarnings({"resource", "deprecation"})
220 @Override
221 public void write(ArrayDataOutput o) throws FitsException {
222
223 if (byteSize == 0) {
224 return;
225 }
226
227 if (o != getRandomAccessInput()) {
228 ensureData();
229 }
230
231 try {
232 o.writeArray(dataArray);
233 } catch (IOException e) {
234 throw new FitsException("IO Error on image write" + e);
235 }
236
237 FitsUtil.pad(o, getTrueSize());
238 }
239
240 @SuppressWarnings("deprecation")
241 @Override
242 protected void fillHeader(Header head) throws FitsException {
243
244 if (dataArray == null) {
245 head.nullImage();
246 return;
247 }
248
249 Standard.context(ImageData.class);
250
251
252
253 head.deleteKey(Standard.XTENSION);
254
255 Cursor<String, HeaderCard> c = head.iterator();
256 c.add(HeaderCard.create(Standard.SIMPLE, true));
257
258 Class<?> base = getType();
259 int[] dims = getDimensions();
260
261 if (ComplexValue.class.isAssignableFrom(base)) {
262 dims = Arrays.copyOf(dims, dims.length + 1);
263 dims[dims.length - 1] = 2;
264 base = ComplexValue.Float.class.isAssignableFrom(base) ? float.class : double.class;
265 }
266
267 c.add(HeaderCard.create(Standard.BITPIX, Bitpix.forPrimitiveType(base).getHeaderValue()));
268
269 c.add(HeaderCard.create(Standard.NAXIS, dims.length));
270 for (int i = 1; i <= dims.length; i++) {
271 c.add(HeaderCard.create(Standard.NAXISn.n(i), dims[dims.length - i]));
272 }
273
274
275 c.add(HeaderCard.create(Standard.PCOUNT, 0));
276 c.add(HeaderCard.create(Standard.GCOUNT, 1));
277 c.add(HeaderCard.create(Standard.EXTEND, true));
278
279 if (isComplexValued()) {
280 c.add(HeaderCard.create(Standard.CTYPEn.n(dims.length - dataDescription.complexAxis), COMPLEX_TYPE));
281 }
282
283 if (dataDescription.quant != null) {
284 dataDescription.quant.editImageHeader(head);
285 }
286
287 Standard.context(null);
288 }
289
290 @Override
291 protected long getTrueSize() {
292 return byteSize;
293 }
294
295
296
297
298
299
300
301
302
303
304 protected ArrayDesc parseHeader(Header h) throws FitsException {
305 String ext = h.getStringValue(Standard.XTENSION, Standard.XTENSION_IMAGE);
306
307 if (!ext.equalsIgnoreCase(Standard.XTENSION_IMAGE) && !ext.equalsIgnoreCase(NonStandard.XTENSION_IUEIMAGE)) {
308 throw new FitsException("Not an image header (XTENSION = " + h.getStringValue(Standard.XTENSION) + ")");
309 }
310
311 int gCount = h.getIntValue(Standard.GCOUNT, 1);
312 int pCount = h.getIntValue(Standard.PCOUNT, 0);
313 if (gCount > 1 || pCount != 0) {
314 throw new FitsException("Group data treated as images");
315 }
316
317 Bitpix bitpix = Bitpix.fromHeader(h);
318 Class<?> baseClass = bitpix.getPrimitiveType();
319 int ndim = h.getIntValue(Standard.NAXIS, 0);
320 int[] dims = new int[ndim];
321
322
323
324
325 byteSize = ndim > 0 ? 1 : 0;
326 for (int i = 1; i <= ndim; i++) {
327 int cdim = h.getIntValue(Standard.NAXISn.n(i), 0);
328 if (cdim < 0) {
329 throw new FitsException("Invalid array dimension:" + cdim);
330 }
331 byteSize *= cdim;
332 dims[ndim - i] = cdim;
333 }
334 byteSize *= bitpix.byteSize();
335
336 ArrayDesc desc = new ArrayDesc(dims, baseClass);
337
338 if (COMPLEX_TYPE.equals(h.getStringValue(Standard.CTYPEn.n(1))) && dims[ndim - 1] == 2) {
339 desc.complexAxis = ndim - 1;
340 } else if (COMPLEX_TYPE.equals(h.getStringValue(Standard.CTYPEn.n(ndim))) && dims[0] == 2) {
341 desc.complexAxis = 0;
342 }
343
344 desc.quant = Quantizer.fromImageHeader(h);
345 if (desc.quant.isDefault()) {
346 desc.quant = null;
347 }
348
349 return desc;
350 }
351
352 void setTiler(StandardImageTiler tiler) {
353 this.tiler = tiler;
354 }
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372 public static void overrideHeaderAxes(Header header, int... sizes) throws FitsException, IllegalArgumentException {
373 String extType = header.getStringValue(Standard.XTENSION, Standard.XTENSION_IMAGE);
374 if (!extType.equals(Standard.XTENSION_IMAGE) && !extType.equals(NonStandard.XTENSION_IUEIMAGE)) {
375 throw new FitsException("Not an image header (XTENSION = " + extType + ")");
376 }
377
378
379 int n = header.getIntValue(Standard.NAXIS);
380 for (int i = 1; i <= n; i++) {
381 header.deleteKey(Standard.NAXISn.n(i));
382 }
383
384 Cursor<String, HeaderCard> c = header.iterator();
385 c.setKey(Standard.NAXIS.key());
386
387 c.add(HeaderCard.create(Standard.NAXIS, sizes.length));
388
389 for (int i = 1; i <= sizes.length; i++) {
390 int l = sizes[sizes.length - i];
391 if (l < 0) {
392 throw new FitsException("Invalid size[ " + i + "] = " + l);
393 }
394 c.add(HeaderCard.create(Standard.NAXISn.n(i), l));
395 }
396 }
397
398
399
400
401
402
403
404
405
406
407
408
409
410 public static ImageData from(Object data) throws IllegalArgumentException {
411 return new ImageData(data);
412 }
413
414
415
416
417
418
419
420
421
422
423
424
425
426 static void checkCompatible(Object data) throws IllegalArgumentException, FitsException {
427 if (data != null) {
428 Class<?> base = ArrayFuncs.getBaseClass(data);
429 if (ComplexValue.Float.class.isAssignableFrom(base)) {
430 base = float.class;
431 } else if (ComplexValue.class.isAssignableFrom(base)) {
432 base = double.class;
433 }
434 Bitpix.forPrimitiveType(base);
435 ArrayFuncs.checkRegularArray(data, false);
436 }
437 }
438
439 @Override
440 @SuppressWarnings("deprecation")
441 public ImageHDU toHDU() throws FitsException {
442 Header h = new Header();
443 fillHeader(h);
444 return new ImageHDU(h, this);
445 }
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461 public void setQuantizer(Quantizer quant) {
462 dataDescription.quant = quant;
463 }
464
465
466
467
468
469
470
471
472
473
474
475
476 public final Quantizer getQuantizer() {
477 return dataDescription.quant;
478 }
479
480
481
482
483
484
485
486
487
488
489
490
491
492 public final Class<?> getType() {
493 return dataDescription.type;
494 }
495
496
497
498
499
500
501
502
503
504
505
506 public final int[] getDimensions() {
507 return Arrays.copyOf(dataDescription.dims, dataDescription.dims.length);
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527 public final boolean isComplexValued() {
528 return dataDescription.complexAxis >= 0;
529 }
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554 public ImageData convertTo(Class<?> type) throws FitsException {
555 if (type.isAssignableFrom(getType())) {
556 return this;
557 }
558
559 ensureData();
560
561 ImageData typed = null;
562
563 boolean toComplex = ComplexValue.class.isAssignableFrom(type) && !ComplexValue.class.isAssignableFrom(getType());
564
565 if (toComplex && dataDescription.complexAxis == 0) {
566
567
568
569 Class<?> numType = ComplexValue.Float.class.isAssignableFrom(type) ? float.class : double.class;
570 Object[] t = (Object[]) ArrayFuncs.convertArray(dataArray, numType, getQuantizer());
571 ImageData f = new ImageData(ArrayFuncs.decimalsToComplex(t[0], t[1]));
572 f.dataDescription.quant = getQuantizer();
573
574
575 return f.convertTo(type);
576 }
577
578 typed = new ImageData(ArrayFuncs.convertArray(dataArray, type, getQuantizer()));
579 typed.dataDescription.quant = getQuantizer();
580 return typed;
581 }
582 }