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 public StandardImageTiler getTiler() {
198 return tiler;
199 }
200
201
202
203
204
205
206 public void setBuffer(Buffer data) {
207 ElementType<Buffer> elementType = ElementType.forClass(getType());
208 dataArray = ArrayFuncs.newInstance(getType(), getDimensions());
209 MultiArrayIterator<?> iterator = new MultiArrayIterator<>(dataArray);
210 Object array = iterator.next();
211 while (array != null) {
212 elementType.getArray(data, array);
213 array = iterator.next();
214 }
215 tiler = new ImageDataTiler(null, 0, dataDescription);
216 }
217
218 @SuppressWarnings({"resource", "deprecation"})
219 @Override
220 public void write(ArrayDataOutput o) throws FitsException {
221
222 if (byteSize == 0) {
223 return;
224 }
225
226 if (o != getRandomAccessInput()) {
227 ensureData();
228 }
229
230 try {
231 o.writeArray(dataArray);
232 } catch (IOException e) {
233 throw new FitsException("IO Error on image write" + e);
234 }
235
236 FitsUtil.pad(o, getTrueSize());
237 }
238
239 @SuppressWarnings("deprecation")
240 @Override
241 protected void fillHeader(Header head) throws FitsException {
242
243 if (dataArray == null) {
244 head.nullImage();
245 return;
246 }
247
248 Standard.context(ImageData.class);
249
250
251
252 head.deleteKey(Standard.XTENSION);
253
254 Cursor<String, HeaderCard> c = head.iterator();
255 c.add(HeaderCard.create(Standard.SIMPLE, true));
256
257 Class<?> base = getType();
258 int[] dims = getDimensions();
259
260 if (ComplexValue.class.isAssignableFrom(base)) {
261 dims = Arrays.copyOf(dims, dims.length + 1);
262 dims[dims.length - 1] = 2;
263 base = ComplexValue.Float.class.isAssignableFrom(base) ? float.class : double.class;
264 }
265
266 c.add(HeaderCard.create(Standard.BITPIX, Bitpix.forPrimitiveType(base).getHeaderValue()));
267
268 c.add(HeaderCard.create(Standard.NAXIS, dims.length));
269 for (int i = 1; i <= dims.length; i++) {
270 c.add(HeaderCard.create(Standard.NAXISn.n(i), dims[dims.length - i]));
271 }
272
273
274 c.add(HeaderCard.create(Standard.PCOUNT, 0));
275 c.add(HeaderCard.create(Standard.GCOUNT, 1));
276 c.add(HeaderCard.create(Standard.EXTEND, true));
277
278 if (isComplexValued()) {
279 c.add(HeaderCard.create(Standard.CTYPEn.n(dims.length - dataDescription.complexAxis), COMPLEX_TYPE));
280 }
281
282 if (dataDescription.quant != null) {
283 dataDescription.quant.editImageHeader(head);
284 }
285
286 Standard.context(null);
287 }
288
289 @Override
290 protected long getTrueSize() {
291 return byteSize;
292 }
293
294
295
296
297
298
299
300
301
302
303 protected ArrayDesc parseHeader(Header h) throws FitsException {
304 String ext = h.getStringValue(Standard.XTENSION, Standard.XTENSION_IMAGE);
305
306 if (!ext.equalsIgnoreCase(Standard.XTENSION_IMAGE) && !ext.equalsIgnoreCase(NonStandard.XTENSION_IUEIMAGE)) {
307 throw new FitsException("Not an image header (XTENSION = " + h.getStringValue(Standard.XTENSION) + ")");
308 }
309
310 int gCount = h.getIntValue(Standard.GCOUNT, 1);
311 int pCount = h.getIntValue(Standard.PCOUNT, 0);
312 if (gCount > 1 || pCount != 0) {
313 throw new FitsException("Group data treated as images");
314 }
315
316 Bitpix bitpix = Bitpix.fromHeader(h);
317 Class<?> baseClass = bitpix.getPrimitiveType();
318 int ndim = h.getIntValue(Standard.NAXIS, 0);
319 int[] dims = new int[ndim];
320
321
322
323
324 byteSize = ndim > 0 ? 1 : 0;
325 for (int i = 1; i <= ndim; i++) {
326 int cdim = h.getIntValue(Standard.NAXISn.n(i), 0);
327 if (cdim < 0) {
328 throw new FitsException("Invalid array dimension:" + cdim);
329 }
330 byteSize *= cdim;
331 dims[ndim - i] = cdim;
332 }
333 byteSize *= bitpix.byteSize();
334
335 ArrayDesc desc = new ArrayDesc(dims, baseClass);
336
337 if (COMPLEX_TYPE.equals(h.getStringValue(Standard.CTYPEn.n(1))) && dims[ndim - 1] == 2) {
338 desc.complexAxis = ndim - 1;
339 } else if (COMPLEX_TYPE.equals(h.getStringValue(Standard.CTYPEn.n(ndim))) && dims[0] == 2) {
340 desc.complexAxis = 0;
341 }
342
343 desc.quant = Quantizer.fromImageHeader(h);
344 if (desc.quant.isDefault()) {
345 desc.quant = null;
346 }
347
348 return desc;
349 }
350
351 void setTiler(StandardImageTiler tiler) {
352 this.tiler = tiler;
353 }
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371 public static void overrideHeaderAxes(Header header, int... sizes) throws FitsException, IllegalArgumentException {
372 String extType = header.getStringValue(Standard.XTENSION, Standard.XTENSION_IMAGE);
373 if (!extType.equals(Standard.XTENSION_IMAGE) && !extType.equals(NonStandard.XTENSION_IUEIMAGE)) {
374 throw new FitsException("Not an image header (XTENSION = " + extType + ")");
375 }
376
377
378 int n = header.getIntValue(Standard.NAXIS);
379 for (int i = 1; i <= n; i++) {
380 header.deleteKey(Standard.NAXISn.n(i));
381 }
382
383 Cursor<String, HeaderCard> c = header.iterator();
384 c.setKey(Standard.NAXIS.key());
385
386 c.add(HeaderCard.create(Standard.NAXIS, sizes.length));
387
388 for (int i = 1; i <= sizes.length; i++) {
389 int l = sizes[sizes.length - i];
390 if (l < 0) {
391 throw new FitsException("Invalid size[ " + i + "] = " + l);
392 }
393 c.add(HeaderCard.create(Standard.NAXISn.n(i), l));
394 }
395 }
396
397
398
399
400
401
402
403
404
405
406
407
408
409 public static ImageData from(Object data) throws IllegalArgumentException {
410 return new ImageData(data);
411 }
412
413
414
415
416
417
418
419
420
421
422
423
424
425 static void checkCompatible(Object data) throws IllegalArgumentException, FitsException {
426 if (data != null) {
427 Class<?> base = ArrayFuncs.getBaseClass(data);
428 if (ComplexValue.Float.class.isAssignableFrom(base)) {
429 base = float.class;
430 } else if (ComplexValue.class.isAssignableFrom(base)) {
431 base = double.class;
432 }
433 Bitpix.forPrimitiveType(base);
434 ArrayFuncs.checkRegularArray(data, false);
435 }
436 }
437
438 @Override
439 @SuppressWarnings("deprecation")
440 public ImageHDU toHDU() throws FitsException {
441 Header h = new Header();
442 fillHeader(h);
443 return new ImageHDU(h, this);
444 }
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460 public void setQuantizer(Quantizer quant) {
461 dataDescription.quant = quant;
462 }
463
464
465
466
467
468
469
470
471
472
473
474
475 public final Quantizer getQuantizer() {
476 return dataDescription.quant;
477 }
478
479
480
481
482
483
484
485
486
487
488
489
490
491 public final Class<?> getType() {
492 return dataDescription.type;
493 }
494
495
496
497
498
499
500
501
502
503
504
505 public final int[] getDimensions() {
506 return Arrays.copyOf(dataDescription.dims, dataDescription.dims.length);
507 }
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526 public final boolean isComplexValued() {
527 return dataDescription.complexAxis >= 0;
528 }
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 public ImageData convertTo(Class<?> type) throws FitsException {
554 if (type.isAssignableFrom(getType())) {
555 return this;
556 }
557
558 ensureData();
559
560 ImageData typed = null;
561
562 boolean toComplex = ComplexValue.class.isAssignableFrom(type) && !ComplexValue.class.isAssignableFrom(getType());
563
564 if (toComplex && dataDescription.complexAxis == 0) {
565
566
567
568 Class<?> numType = ComplexValue.Float.class.isAssignableFrom(type) ? float.class : double.class;
569 Object[] t = (Object[]) ArrayFuncs.convertArray(dataArray, numType, getQuantizer());
570 ImageData f = new ImageData(ArrayFuncs.decimalsToComplex(t[0], t[1]));
571 f.dataDescription.quant = getQuantizer();
572
573
574 return f.convertTo(type);
575 }
576
577 typed = new ImageData(ArrayFuncs.convertArray(dataArray, type, getQuantizer()));
578 typed.dataDescription.quant = getQuantizer();
579 return typed;
580 }
581 }