001/***************************************************************************** 002 * Copyright by The HDF Group. * 003 * Copyright by the Board of Trustees of the University of Illinois. * 004 * All rights reserved. * 005 * * 006 * This file is part of the HDF Java Products distribution. * 007 * The full copyright notice, including terms governing use, modification, * 008 * and redistribution, is contained in the file COPYING. * 009 * COPYING can be found at the root of the source code distribution tree. * 010 * If you do not have access to this file, you may request a copy from * 011 * help@hdfgroup.org. * 012 ****************************************************************************/ 013 014package hdf.view; 015 016import java.awt.Image; 017import java.awt.Toolkit; 018import java.awt.image.BufferedImage; 019import java.awt.image.ColorModel; 020import java.awt.image.DataBufferInt; 021import java.awt.image.DirectColorModel; 022import java.awt.image.MemoryImageSource; 023import java.awt.image.PixelGrabber; 024import java.io.BufferedInputStream; 025import java.io.BufferedReader; 026import java.io.File; 027import java.io.FileInputStream; 028import java.io.FileReader; 029import java.lang.reflect.Array; 030import java.lang.reflect.Constructor; 031import java.lang.reflect.Method; 032import java.math.BigInteger; 033import java.util.BitSet; 034import java.util.List; 035import java.util.StringTokenizer; 036 037import javax.imageio.ImageIO; 038import javax.swing.tree.DefaultMutableTreeNode; 039 040import hdf.object.Datatype; 041import hdf.object.FileFormat; 042import hdf.object.Group; 043import hdf.object.ScalarDS; 044import hdf.view.ViewProperties.BITMASK_OP; 045 046/** 047 * The "Tools" class contains various tools for HDF files such as jpeg to HDF 048 * converter. 049 * 050 * @author Peter X. Cao 051 * @version 2.4 9/6/2007 052 */ 053public final class Tools { 054 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(Tools.class); 055 056 public static final long MAX_INT8 = 127; 057 public static final long MAX_UINT8 = 255; 058 public static final long MAX_INT16 = 32767; 059 public static final long MAX_UINT16 = 65535; 060 public static final long MAX_INT32 = 2147483647; 061 public static final long MAX_UINT32 = 4294967295L; 062 public static final long MAX_INT64 = 9223372036854775807L; 063 public static final BigInteger MAX_UINT64 = new BigInteger("18446744073709551615"); 064 065 /** Key for JPEG image file type. */ 066 public static final String FILE_TYPE_JPEG = "JPEG"; 067 068 /** Key for TIFF image file type. */ 069 public static final String FILE_TYPE_TIFF = "TIFF"; 070 071 /** Key for PNG image file type. */ 072 public static final String FILE_TYPE_PNG = "PNG"; 073 074 /** Key for GIF image file type. */ 075 public static final String FILE_TYPE_GIF = "GIF"; 076 077 /** Key for BMP image file type. */ 078 public static final String FILE_TYPE_BMP = "BMP"; 079 080 /** Key for all image file type. */ 081 public static final String FILE_TYPE_IMAGE = "IMG"; 082 083 /** Print out debug information 084 * @param caller 085 * the caller object. 086 * @param msg 087 * the message to be displayed. 088 */ 089 public static final void debug(Object caller, Object msg) { 090 if (caller != null) System.out.println("*** " + caller.getClass().getName() + ": " + msg); 091 } 092 093 /** 094 * Converts an image file into HDF4/5 file. 095 * 096 * @param imgFileName 097 * the input image file. 098 * @param hFileName 099 * the name of the HDF4/5 file. 100 * @param fromType 101 * the type of image. 102 * @param toType 103 * the type of file converted to. 104 * 105 * @throws Exception if a failure occurred 106 */ 107 public static void convertImageToHDF(String imgFileName, String hFileName, String fromType, String toType) 108 throws Exception { 109 File imgFile = null; 110 111 if (imgFileName == null) { 112 throw new NullPointerException("The source image file is null."); 113 } 114 else if (!(imgFile = new File(imgFileName)).exists()) { 115 throw new NullPointerException("The source image file does not exist."); 116 } 117 else if (hFileName == null) { 118 throw new NullPointerException("The target HDF file is null."); 119 } 120 121 if (!fromType.equals(FILE_TYPE_IMAGE)) { 122 throw new UnsupportedOperationException("Unsupported image type."); 123 } 124 else if (!(toType.equals(FileFormat.FILE_TYPE_HDF4) || toType.equals(FileFormat.FILE_TYPE_HDF5))) { 125 throw new UnsupportedOperationException("Unsupported destination file type."); 126 } 127 128 BufferedImage image = null; 129 try { 130 BufferedInputStream in = new BufferedInputStream(new FileInputStream(imgFileName)); 131 image = ImageIO.read(in); 132 in.close(); 133 } 134 catch (Throwable err) { 135 image = null; 136 } 137 138 if (image == null) throw new UnsupportedOperationException("Failed to read image: " + imgFileName); 139 140 int h = image.getHeight(); 141 int w = image.getWidth(); 142 byte[] data = null; 143 144 try { 145 data = new byte[3 * h * w]; 146 } 147 catch (OutOfMemoryError err) { 148 err.printStackTrace(); 149 throw new RuntimeException("Out of memory error."); 150 } 151 152 int idx = 0; 153 int rgb = 0; 154 for (int i = 0; i < h; i++) { 155 for (int j = 0; j < w; j++) { 156 rgb = image.getRGB(j, i); 157 data[idx++] = (byte) (rgb >> 16); 158 data[idx++] = (byte) (rgb >> 8); 159 data[idx++] = (byte) rgb; 160 } 161 } 162 163 long[] dims = null; 164 Datatype type = null; 165 Group pgroup = null; 166 String imgName = imgFile.getName(); 167 FileFormat newfile = null, thefile = null; 168 if (toType.equals(FileFormat.FILE_TYPE_HDF5)) { 169 thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF5); 170 long[] h5dims = { h, w, 3 }; // RGB pixel interlace 171 dims = h5dims; 172 } 173 else if (toType.equals(FileFormat.FILE_TYPE_HDF4)) { 174 thefile = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4); 175 long[] h4dims = { w, h, 3 }; // RGB pixel interlace 176 dims = h4dims; 177 } 178 else { 179 thefile = null; 180 } 181 182 if (thefile != null) { 183 newfile = thefile.createInstance(hFileName, FileFormat.CREATE); 184 newfile.open(); 185 pgroup = (Group) ((DefaultMutableTreeNode) newfile.getRootNode()).getUserObject(); 186 type = newfile.createDatatype(Datatype.CLASS_CHAR, 1, Datatype.NATIVE, Datatype.SIGN_NONE); 187 newfile.createImage(imgName, pgroup, type, dims, null, null, -1, 3, ScalarDS.INTERLACE_PIXEL, data); 188 newfile.close(); 189 } 190 191 // clean up memory 192 data = null; 193 image = null; 194 Runtime.getRuntime().gc(); 195 } 196 197 /** 198 * Save a BufferedImage into an image file. 199 * 200 * @param image 201 * the BufferedImage to save. 202 * @param file 203 * the image file. 204 * @param type 205 * the image type. 206 * 207 * @throws Exception if a failure occurred 208 */ 209 public static void saveImageAs(BufferedImage image, File file, String type) throws Exception { 210 if (image == null) { 211 throw new NullPointerException("The source image is null."); 212 } 213 214 ImageIO.write(image, type, file); 215 } 216 217 /** 218 * Creates the gray palette of the indexed 256-color table. 219 * <p> 220 * The palette values are stored in a two-dimensional byte array and arrange 221 * by color components of red, green and blue. palette[][] = byte[3][256], 222 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 223 * blue components respectively. 224 * 225 * @return the gray palette in the form of byte[3][256] 226 */ 227 public static final byte[][] createGrayPalette() { 228 byte[][] p = new byte[3][256]; 229 230 for (int i = 0; i < 256; i++) { 231 p[0][i] = p[1][i] = p[2][i] = (byte) (i); 232 } 233 234 return p; 235 } 236 237 /** 238 * Creates the reverse gray palette of the indexed 256-color table. 239 * <p> 240 * The palette values are stored in a two-dimensional byte array and arrange 241 * by color components of red, green and blue. palette[][] = byte[3][256], 242 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 243 * blue components respectively. 244 * 245 * @return the gray palette in the form of byte[3][256] 246 */ 247 public static final byte[][] createReverseGrayPalette() { 248 byte[][] p = new byte[3][256]; 249 250 for (int i = 0; i < 256; i++) { 251 p[0][i] = p[1][i] = p[2][i] = (byte) (255 - i); 252 } 253 254 return p; 255 } 256 257 /** 258 * Creates the gray wave palette of the indexed 256-color table. 259 * <p> 260 * The palette values are stored in a two-dimensional byte array and arrange 261 * by color components of red, green and blue. palette[][] = byte[3][256], 262 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 263 * blue components respectively. 264 * 265 * @return the gray palette in the form of byte[3][256] 266 */ 267 public static final byte[][] createGrayWavePalette() { 268 byte[][] p = new byte[3][256]; 269 270 for (int i = 0; i < 256; i++) { 271 p[0][i] = p[1][i] = p[2][i] = (byte) (255 / 2 + (255 / 2) * Math.sin((i - 32) / 20.3)); 272 } 273 274 return p; 275 } 276 277 /** 278 * Creates the rainbow palette of the indexed 256-color table. 279 * <p> 280 * The palette values are stored in a two-dimensional byte array and arrange 281 * by color components of red, green and blue. palette[][] = byte[3][256], 282 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 283 * blue components respectively. 284 * 285 * @return the rainbow palette in the form of byte[3][256] 286 */ 287 public static final byte[][] createRainbowPalette() { 288 byte r, g, b; 289 byte[][] p = new byte[3][256]; 290 291 for (int i = 1; i < 255; i++) { 292 if (i <= 29) { 293 r = (byte) (129.36 - i * 4.36); 294 g = 0; 295 b = (byte) 255; 296 } 297 else if (i <= 86) { 298 r = 0; 299 g = (byte) (-133.54 + i * 4.52); 300 b = (byte) 255; 301 } 302 else if (i <= 141) { 303 r = 0; 304 g = (byte) 255; 305 b = (byte) (665.83 - i * 4.72); 306 } 307 else if (i <= 199) { 308 r = (byte) (-635.26 + i * 4.47); 309 g = (byte) 255; 310 b = 0; 311 } 312 else { 313 r = (byte) 255; 314 g = (byte) (1166.81 - i * 4.57); 315 b = 0; 316 } 317 318 p[0][i] = r; 319 p[1][i] = g; 320 p[2][i] = b; 321 } 322 323 p[0][0] = p[1][0] = p[2][0] = 0; 324 p[0][255] = p[1][255] = p[2][255] = (byte) 255; 325 326 return p; 327 } 328 329 /** 330 * Creates the nature palette of the indexed 256-color table. 331 * <p> 332 * The palette values are stored in a two-dimensional byte array and arrange 333 * by color components of red, green and blue. palette[][] = byte[3][256], 334 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 335 * blue components respectively. 336 * 337 * @return the nature palette in the form of byte[3][256] 338 */ 339 public static final byte[][] createNaturePalette() { 340 byte[][] p = new byte[3][256]; 341 342 for (int i = 1; i < 210; i++) { 343 p[0][i] = (byte) ((Math.sin((double) (i - 5) / 16) + 1) * 90); 344 p[1][i] = (byte) ((1 - Math.sin((double) (i - 30) / 12)) * 64 * (1 - (double) i / 255) + 128 - i / 2); 345 p[2][i] = (byte) ((1 - Math.sin((double) (i - 8) / 9)) * 110 + 30); 346 } 347 348 for (int i = 210; i < 255; i++) { 349 p[0][i] = (byte) 80; 350 p[1][i] = (byte) 0; 351 p[2][i] = (byte) 200; 352 } 353 354 p[0][0] = p[1][0] = p[2][0] = 0; 355 p[0][255] = p[1][255] = p[2][255] = (byte) 255; 356 357 return p; 358 } 359 360 /** 361 * Creates the wave palette of the indexed 256-color table. 362 * <p> 363 * The palette values are stored in a two-dimensional byte array and arrange 364 * by color components of red, green and blue. palette[][] = byte[3][256], 365 * where, palette[0][], palette[1][] and palette[2][] are the red, green and 366 * blue components respectively. 367 * 368 * @return the wave palette in the form of byte[3][256] 369 */ 370 public static final byte[][] createWavePalette() { 371 byte[][] p = new byte[3][256]; 372 373 for (int i = 1; i < 255; i++) { 374 p[0][i] = (byte) ((Math.sin(((double) i / 40 - 3.2)) + 1) * 128); 375 p[1][i] = (byte) ((1 - Math.sin((i / 2.55 - 3.1))) * 70 + 30); 376 p[2][i] = (byte) ((1 - Math.sin(((double) i / 40 - 3.1))) * 128); 377 } 378 379 p[0][0] = p[1][0] = p[2][0] = 0; 380 p[0][255] = p[1][255] = p[2][255] = (byte) 255; 381 382 return p; 383 } 384 385 /** 386 * read an image palette from a file. 387 * 388 * A palette file has format of (value, red, green, blue). The color value 389 * in palette file can be either unsigned char [0..255] or float [0..1]. 390 * Float value will be converted to [0..255]. 391 * 392 * The color table in file can have any number of entries between 2 to 256. 393 * It will be converted to a color table of 256 entries. Any missing index 394 * will calculated by linear interpolation between the neighboring index 395 * values. For example, index 11 is missing in the following table 10 200 60 396 * 20 12 100 100 60 Index 11 will be calculated based on index 10 and index 397 * 12, i.e. 11 150 80 40 398 * 399 * @param filename 400 * the name of the palette file. 401 * 402 * @return the wave palette in the form of byte[3][256] 403 */ 404 public static final byte[][] readPalette(String filename) { 405 final int COLOR256 = 256; 406 BufferedReader in = null; 407 String line = null; 408 int nentries = 0, i, j, idx; 409 float v, r, g, b, ratio, max_v, min_v, max_color, min_color; 410 float[][] tbl = new float[COLOR256][4]; /* value, red, green, blue */ 411 412 if (filename == null) return null; 413 414 try { 415 in = new BufferedReader(new FileReader(filename)); 416 } 417 catch (Exception ex) { 418 log.debug("input file:", ex); 419 in = null; 420 } 421 422 if (in == null) return null; 423 424 idx = 0; 425 v = r = g = b = ratio = max_v = min_v = max_color = min_color = 0; 426 do { 427 try { 428 line = in.readLine(); 429 } 430 catch (Exception ex) { 431 log.debug("input file:", ex); 432 line = null; 433 } 434 435 if (line == null) continue; 436 437 StringTokenizer st = new StringTokenizer(line); 438 439 // invalid line 440 if (st.countTokens() != 4) { 441 continue; 442 } 443 444 try { 445 v = Float.valueOf(st.nextToken()); 446 r = Float.valueOf(st.nextToken()); 447 g = Float.valueOf(st.nextToken()); 448 b = Float.valueOf(st.nextToken()); 449 } 450 catch (NumberFormatException ex) { 451 log.debug("input file:", ex); 452 continue; 453 } 454 455 tbl[idx][0] = v; 456 tbl[idx][1] = r; 457 tbl[idx][2] = g; 458 tbl[idx][3] = b; 459 460 if (idx == 0) { 461 max_v = min_v = v; 462 max_color = min_color = r; 463 } 464 465 max_v = Math.max(max_v, v); 466 max_color = Math.max(max_color, r); 467 max_color = Math.max(max_color, g); 468 max_color = Math.max(max_color, b); 469 470 min_v = Math.min(min_v, v); 471 min_color = Math.min(min_color, r); 472 min_color = Math.min(min_color, g); 473 min_color = Math.min(min_color, b); 474 475 idx++; 476 if (idx >= COLOR256) break; /* only support to 256 colors */ 477 } while (line != null); 478 479 try { 480 in.close(); 481 } 482 catch (Exception ex) { 483 log.debug("input file:", ex); 484 } 485 486 nentries = idx; 487 if (nentries <= 1) // must have more than one entries 488 return null; 489 490 // convert color table to byte 491 nentries = idx; 492 if (max_color <= 1) { 493 ratio = (min_color == max_color) ? 1.0f : ((COLOR256 - 1.0f) / (max_color - min_color)); 494 495 for (i = 0; i < nentries; i++) { 496 for (j = 1; j < 4; j++) 497 tbl[i][j] = (tbl[i][j] - min_color) * ratio; 498 } 499 } 500 501 // convert table to 256 entries 502 idx = 0; 503 ratio = (min_v == max_v) ? 1.0f : ((COLOR256 - 1.0f) / (max_v - min_v)); 504 505 int[][] p = new int[3][COLOR256]; 506 for (i = 0; i < nentries; i++) { 507 idx = (int) ((tbl[i][0] - min_v) * ratio); 508 for (j = 0; j < 3; j++) 509 p[j][idx] = (int) tbl[i][j + 1]; 510 } 511 512 /* linear interpolating missing values in the color table */ 513 for (i = 1; i < COLOR256; i++) { 514 if ((p[0][i] + p[1][i] + p[2][i]) == 0) { 515 j = i + 1; 516 517 // figure out number of missing points between two given points 518 while (j < COLOR256 && (p[0][j] + p[1][j] + p[2][j]) == 0) 519 j++; 520 521 if (j >= COLOR256) break; // nothing in the table to interpolating 522 523 float d1 = (p[0][j] - p[0][i - 1]) / (j - i); 524 float d2 = (p[1][j] - p[1][i - 1]) / (j - i); 525 float d3 = (p[2][j] - p[2][i - 1]) / (j - i); 526 527 for (int k = i; k <= j; k++) { 528 p[0][k] = (int) (p[0][i - 1] + d1 * (k - i + 1)); 529 p[1][k] = (int) (p[1][i - 1] + d2 * (k - i + 1)); 530 p[2][k] = (int) (p[2][i - 1] + d3 * (k - i + 1)); 531 } 532 i = j + 1; 533 } // if ((p[0][i] + p[1][i] + p[2][i]) == 0) 534 } // for (i = 1; i < COLOR256; i++) { 535 536 byte[][] pal = new byte[3][COLOR256]; 537 for (i = 1; i < COLOR256; i++) { 538 for (j = 0; j < 3; j++) 539 pal[j][i] = (byte) (p[j][i]); 540 } 541 542 return pal; 543 } 544 545 /** 546 * This method returns true if the specified image has transparent pixels. 547 * 548 * @param image 549 * the image to be check if has alpha. 550 * 551 * @return true if the image has alpha setting. 552 */ 553 public static boolean hasAlpha(Image image) { 554 if (image == null) { 555 return false; 556 } 557 558 // If buffered image, the color model is readily available 559 if (image instanceof BufferedImage) { 560 BufferedImage bimage = (BufferedImage) image; 561 return bimage.getColorModel().hasAlpha(); 562 } 563 564 // Use a pixel grabber to retrieve the image's color model; 565 // grabbing a single pixel is usually sufficient 566 PixelGrabber pg = new PixelGrabber(image, 0, 0, 1, 1, false); 567 try { 568 pg.grabPixels(); 569 } 570 catch (InterruptedException e) { 571 log.debug("transparent pixels:", e); 572 } 573 ColorModel cm = pg.getColorModel(); 574 575 return cm.hasAlpha(); 576 } 577 578 /** 579 * Creates a RGB indexed image of 256 colors. 580 * 581 * @param bufferedImage 582 * the target image. 583 * @param imageData 584 * the byte array of the image data. 585 * @param palette 586 * the color lookup table. 587 * @param w 588 * the width of the image. 589 * @param h 590 * the height of the image. 591 * 592 * @return the image. 593 */ 594 public static Image createIndexedImage(BufferedImage bufferedImage, byte[] imageData, byte[][] palette, int w, int h) 595 { 596 if (imageData==null || w<=0 || h<=0) 597 return null; 598 599 if (palette==null) 600 palette = Tools.createGrayPalette(); 601 602 if (bufferedImage == null) 603 bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 604 605 final int[] pixels = ( (DataBufferInt) bufferedImage.getRaster().getDataBuffer() ).getData(); 606 int len = pixels.length; 607 608 for (int i=0; i<len; i++) { 609 int idx = imageData[i] & 0xff; 610 int r = ((int)(palette[0][idx] & 0xff))<<16; 611 int g = ((int)(palette[1][idx] & 0xff))<<8; 612 int b = palette[2][idx] & 0xff; 613 614 pixels[i] = 0xff000000 | r | g | b; 615 } 616 617 return bufferedImage; 618 } 619 620 /** 621 * Creates a true color image. 622 * <p> 623 * DirectColorModel is used to construct the image from raw data. The 624 * DirectColorModel model is similar to an X11 TrueColor visual, which has 625 * the following parameters: <br> 626 * 627 * <pre> 628 * Number of bits: 32 629 * Red mask: 0x00ff0000 630 * Green mask: 0x0000ff00 631 * Blue mask: 0x000000ff 632 * Alpha mask: 0xff000000 633 * Color space: sRGB 634 * isAlphaPremultiplied: False 635 * Transparency: Transparency.TRANSLUCENT 636 * transferType: DataBuffer.TYPE_INT 637 * </pre> 638 * <p> 639 * The data may be arranged in one of two ways: by pixel or by plane. In 640 * both cases, the dataset will have a dataspace with three dimensions, 641 * height, width, and components. 642 * <p> 643 * For HDF4, the interlace modes specify orders for the dimensions as: 644 * 645 * <pre> 646 * INTERLACE_PIXEL = [width][height][pixel components] 647 * INTERLACE_PLANE = [pixel components][width][height] 648 * </pre> 649 * <p> 650 * For HDF5, the interlace modes specify orders for the dimensions as: 651 * 652 * <pre> 653 * INTERLACE_PIXEL = [height][width][pixel components] 654 * INTERLACE_PLANE = [pixel components][height][width] 655 * </pre> 656 * 657 * @param imageData 658 * the byte array of the image data. 659 * @param planeInterlace 660 * flag if the image is plane intelace. 661 * @param w 662 * the width of the image. 663 * @param h 664 * the height of the image. 665 * 666 * @return the image. 667 */ 668 public static Image createTrueColorImage(byte[] imageData, boolean planeInterlace, int w, int h) { 669 Image theImage = null; 670 int imgSize = w * h; 671 int packedImageData[] = new int[imgSize]; 672 int pixel = 0, idx = 0, r = 0, g = 0, b = 0; 673 for (int i = 0; i < h; i++) { 674 for (int j = 0; j < w; j++) { 675 pixel = r = g = b = 0; 676 if (planeInterlace) { 677 r = imageData[idx]; 678 g = imageData[imgSize + idx]; 679 b = imageData[imgSize * 2 + idx]; 680 } 681 else { 682 r = imageData[idx * 3]; 683 g = imageData[idx * 3 + 1]; 684 b = imageData[idx * 3 + 2]; 685 } 686 687 r = (r << 16) & 0x00ff0000; 688 g = (g << 8) & 0x0000ff00; 689 b = b & 0x000000ff; 690 691 // bits packed into alpha (1), red (r), green (g) and blue (b) 692 // as 11111111rrrrrrrrggggggggbbbbbbbb 693 pixel = 0xff000000 | r | g | b; 694 packedImageData[idx++] = pixel; 695 } // for (int j=0; j<w; j++) 696 } // for (int i=0; i<h; i++) 697 698 DirectColorModel dcm = (DirectColorModel) ColorModel.getRGBdefault(); 699 theImage = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(w, h, dcm, packedImageData, 0, w)); 700 701 packedImageData = null; 702 703 return theImage; 704 } 705 706 /** 707 * Convert an array of raw data into array of a byte data. 708 * 709 * @param rawData 710 * The input raw data. 711 * @param minmax 712 * the range of the raw data. 713 * @param w 714 * the width of the raw data. 715 * @param h 716 * the height of the raw data. 717 * @param isTransposed 718 * if the data is transposed. 719 * @param byteData 720 * the data in. 721 * 722 * @return the byte array of pixel data. 723 */ 724 public static byte[] getBytes(Object rawData, double[] minmax, int w, int h, boolean isTransposed, byte[] byteData) { 725 return Tools.getBytes(rawData, minmax, w, h, isTransposed, null, false, byteData); 726 } 727 728 public static byte[] getBytes(Object rawData, double[] minmax, int w, int h, boolean isTransposed, 729 List<Number> invalidValues, byte[] byteData) { 730 return getBytes(rawData, minmax, w, h, isTransposed, invalidValues, false, byteData); 731 } 732 733 public static byte[] getBytes(Object rawData, double[] minmax, int w, int h, boolean isTransposed, 734 List<Number> invalidValues, boolean convertByteData, byte[] byteData) { 735 return getBytes(rawData, minmax, w, h, isTransposed,invalidValues, convertByteData, byteData, null); 736 } 737 738 /** 739 * Convert an array of raw data into array of a byte data. 740 * 741 * @param rawData 742 * The input raw data. 743 * @param minmax 744 * the range of the raw data. 745 * @param w 746 * the width of the raw data. 747 * @param h 748 * the height of the raw data. 749 * @param isTransposed 750 * if the data is transposed. 751 * @param invalidValues 752 * the list of invalid values. 753 * @param convertByteData 754 * the converted data out. 755 * @param byteData 756 * the data in. 757 * @param list 758 * the list of integers. 759 * 760 * @return the byte array of pixel data. 761 */ 762 public static byte[] getBytes(Object rawData, double[] minmax, int w, int h, boolean isTransposed, 763 List<Number> invalidValues, boolean convertByteData, byte[] byteData, List<Integer> list) 764 { 765 double fillValue[] = null; 766 767 // no input data 768 if (rawData == null || w<=0 || h<=0) { 769 return null; 770 } 771 772 // input data is not an array 773 if (!rawData.getClass().isArray()) { 774 return null; 775 } 776 777 double min = Double.MAX_VALUE, max = -Double.MAX_VALUE, ratio = 1.0d; 778 String cname = rawData.getClass().getName(); 779 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 780 int size = Array.getLength(rawData); 781 782 if (minmax == null) { 783 minmax = new double[2]; 784 minmax[0] = minmax[1] = 0; 785 } 786 787 if (dname == 'B') { 788 return convertByteData((byte[]) rawData, minmax, w, h, isTransposed, fillValue, convertByteData, byteData, list); 789 } 790 791 if ((byteData == null) || (size != byteData.length)) { 792 byteData = new byte[size]; // reuse the old buffer 793 } 794 795 if (minmax[0] == minmax[1]) { 796 Tools.findMinMax(rawData, minmax, fillValue); 797 } 798 799 min = minmax[0]; 800 max = minmax[1]; 801 802 if (invalidValues!=null && invalidValues.size()>0) { 803 int n = invalidValues.size(); 804 fillValue = new double[n]; 805 for (int i=0; i<n; i++) { 806 fillValue[i] = invalidValues.get(i).doubleValue(); 807 } 808 } 809 ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min)); 810 int idxSrc = 0, idxDst = 0; 811 switch (dname) { 812 case 'S': 813 short[] s = (short[]) rawData; 814 for (int i = 0; i < h; i++) { 815 for (int j = 0; j < w; j++) { 816 idxSrc = idxDst =j * h + i; 817 if (isTransposed) idxDst = i * w + j; 818 byteData[idxDst] = toByte(s[idxSrc], ratio, min, max, fillValue, idxSrc, list); 819 } 820 } 821 break; 822 823 case 'I': 824 int[] ia = (int[]) rawData; 825 for (int i = 0; i < h; i++) { 826 for (int j = 0; j < w; j++) { 827 idxSrc = idxDst =j * h + i; 828 if (isTransposed) idxDst = i * w + j; 829 byteData[idxDst] = toByte(ia[idxSrc], ratio, min, max, fillValue, idxSrc, list); 830 } 831 } 832 break; 833 834 case 'J': 835 long[] l = (long[]) rawData; 836 for (int i = 0; i < h; i++) { 837 for (int j = 0; j < w; j++) { 838 idxSrc = idxDst =j * h + i; 839 if (isTransposed) idxDst = i * w + j; 840 byteData[idxDst] = toByte(l[idxSrc], ratio, min, max, fillValue, idxSrc, list); 841 } 842 } 843 break; 844 845 case 'F': 846 float[] f = (float[]) rawData; 847 for (int i = 0; i < h; i++) { 848 for (int j = 0; j < w; j++) { 849 idxSrc = idxDst =j * h + i; 850 if (isTransposed) idxDst = i * w + j; 851 byteData[idxDst] = toByte(f[idxSrc], ratio, min, max, fillValue, idxSrc, list); 852 } 853 } 854 break; 855 856 case 'D': 857 double[] d = (double[]) rawData; 858 for (int i = 0; i < h; i++) { 859 for (int j = 0; j < w; j++) { 860 idxSrc = idxDst =j * h + i; 861 if (isTransposed) idxDst = i * w + j; 862 byteData[idxDst] = toByte(d[idxSrc], ratio, min, max, fillValue, idxSrc, list); 863 } 864 } 865 break; 866 867 default: 868 byteData = null; 869 break; 870 } // switch (dname) 871 872 return byteData; 873 } 874 875 private static byte toByte(double in, double ratio, double min, double max, double[] fill, int idx, List<Integer> list) 876 { 877 byte out = 0; 878 879 if (in < min || in > max || isFillValue(in, fill) || isNaNINF(in)) { 880 out = 0; 881 if (list!=null) 882 list.add(idx); 883 } 884 else 885 out = (byte) ((in-min)*ratio); 886 887 return out; 888 } 889 890 private static boolean isFillValue(double in, double[] fill) { 891 892 if (fill==null) 893 return false; 894 895 for (int i=0; i<fill.length; i++) { 896 if (fill[i] == in) 897 return true; 898 } 899 900 return false; 901 } 902 903 private static byte[] convertByteData(byte[] rawData, double[] minmax, int w, int h, boolean isTransposed, 904 Object fillValue, boolean convertByteData, byte[] byteData, List<Integer> list) { 905 double min = Double.MAX_VALUE, max = -Double.MAX_VALUE, ratio = 1.0d; 906 907 if (rawData == null) return null; 908 909 if (convertByteData) { 910 if (minmax[0] == minmax[1]) { 911 Tools.findMinMax(rawData, minmax, fillValue); 912 } 913 } 914 915 if (minmax[0] == 0 && minmax[1] == 255) convertByteData = false; // no need to convert data 916 917 // no conversion and no transpose 918 if (!convertByteData && !isTransposed) { 919 if (byteData != null && byteData.length == rawData.length) { 920 System.arraycopy(rawData, 0, byteData, 0, rawData.length); 921 return byteData; 922 } 923 924 return rawData; 925 } 926 927 // don't want to change the original raw data 928 if (byteData == null || rawData == byteData) byteData = new byte[rawData.length]; 929 930 if (!convertByteData) { 931 // do not convert data, just transpose the data 932 minmax[0] = 0; 933 minmax[1] = 255; 934 if (isTransposed) { 935 for (int i = 0; i < h; i++) { 936 for (int j = 0; j < w; j++) { 937 byteData[i * w + j] = rawData[j * h + i]; 938 } 939 } 940 } 941 return byteData; 942 } 943 944 // special data range used, must convert the data 945 min = minmax[0]; 946 max = minmax[1]; 947 ratio = (min == max) ? 1.00d : (double) (255.00 / (max - min)); 948 int idxSrc = 0, idxDst = 0; 949 for (int i = 0; i < h; i++) { 950 for (int j = 0; j < w; j++) { 951 idxSrc = idxDst =j * h + i; 952 if (isTransposed) idxDst = i * w + j; 953 954 if (rawData[idxSrc] > max || rawData[idxSrc] < min) { 955 byteData[idxDst] = (byte) 0; 956 if (list!=null) 957 list.add(idxSrc); 958 } 959 else 960 byteData[idxDst] = (byte) ((rawData[idxSrc] - min) * ratio); 961 } 962 } 963 964 return byteData; 965 } 966 967 /** 968 * Create and initialize a new instance of the given class. 969 * 970 * @param cls 971 * the class of the instance 972 * @param initargs 973 * array of objects to be passed as arguments. 974 * 975 * @return a new instance of the given class. 976 * 977 * @throws Exception if a failure occurred 978 */ 979 public static Object newInstance(Class<?> cls, Object[] initargs) throws Exception { 980 log.trace("newInstance(Class = {}): start", cls); 981 982 if (cls == null) { 983 return null; 984 } 985 986 Object instance = null; 987 988 if ((initargs == null) || (initargs.length == 0)) { 989 instance = cls.newInstance(); 990 } 991 else { 992 Constructor<?>[] constructors = cls.getConstructors(); 993 if ((constructors == null) || (constructors.length == 0)) { 994 return null; 995 } 996 997 boolean isConstructorMatched = false; 998 Constructor<?> constructor = null; 999 Class<?>[] params = null; 1000 int m = constructors.length; 1001 int n = initargs.length; 1002 for (int i = 0; i < m; i++) { 1003 constructor = constructors[i]; 1004 params = constructor.getParameterTypes(); 1005 if (params.length == n) { 1006 // check if all the parameters are matched 1007 isConstructorMatched = params[0].isInstance(initargs[0]); 1008 for (int j = 0; j < n; j++) { 1009 isConstructorMatched = isConstructorMatched && params[j].isInstance(initargs[j]); 1010 } 1011 1012 if (isConstructorMatched) { 1013 try { 1014 instance = constructor.newInstance(initargs); 1015 } catch (Exception ex) { 1016 log.debug("Error creating instance of {}: {}", cls, ex.getMessage()); 1017 ex.printStackTrace(); 1018 } 1019 break; 1020 } 1021 } 1022 } // for (int i=0; i<m; i++) { 1023 } 1024 log.trace("newInstance(Class = {}): finish", cls); 1025 1026 return instance; 1027 } 1028 1029 /** 1030 * Computes autocontrast parameters (gain equates to contrast and bias 1031 * equates to brightness) for integers. 1032 * <p> 1033 * The computation is based on the following scaling 1034 * 1035 * <pre> 1036 * int_8 [0, 127] 1037 * uint_8 [0, 255] 1038 * int_16 [0, 32767] 1039 * uint_16 [0, 65535] 1040 * int_32 [0, 2147483647] 1041 * uint_32 [0, 4294967295] 1042 * int_64 [0, 9223372036854775807] 1043 * uint_64 [0, 18446744073709551615] // Not supported. 1044 * </pre> 1045 * 1046 * @param data 1047 * the raw data array of signed/unsigned integers 1048 * @param params 1049 * the auto gain parameter. params[0]=gain, params[1]=bias, 1050 * @param isUnsigned 1051 * the flag to indicate if the data array is unsigned integer. 1052 * 1053 * @return non-negative if successful; otherwise, returns negative 1054 */ 1055 public static int autoContrastCompute(Object data, double[] params, boolean isUnsigned) { 1056 int retval = 1; 1057 long maxDataValue = 255; 1058 double[] minmax = new double[2]; 1059 1060 // check parameters 1061 if ((data == null) || (params == null) || (Array.getLength(data) <= 0) || (params.length < 2)) { 1062 return -1; 1063 } 1064 1065 retval = autoContrastComputeMinMax(data, minmax); 1066 1067 // force the min_max method so we can look at the target grids data sets 1068 if ((retval < 0) || (minmax[1] - minmax[0] < 10)) { 1069 retval = findMinMax(data, minmax, null); 1070 } 1071 1072 if (retval < 0) { 1073 return -1; 1074 } 1075 1076 String cname = data.getClass().getName(); 1077 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 1078 switch (dname) { 1079 case 'B': 1080 maxDataValue = MAX_INT8; 1081 break; 1082 case 'S': 1083 maxDataValue = MAX_INT16; 1084 if (isUnsigned) { 1085 maxDataValue = MAX_UINT8; // data was upgraded from unsigned byte 1086 } 1087 break; 1088 case 'I': 1089 maxDataValue = MAX_INT32; 1090 if (isUnsigned) { 1091 maxDataValue = MAX_UINT16; // data was upgraded from unsigned short 1092 } 1093 break; 1094 case 'J': 1095 maxDataValue = MAX_INT64; 1096 if (isUnsigned) { 1097 maxDataValue = MAX_UINT32; // data was upgraded from unsigned int 1098 } 1099 break; 1100 default: 1101 retval = -1; 1102 break; 1103 } // switch (dname) 1104 1105 if (minmax[0] == minmax[1]) { 1106 params[0] = 1.0; 1107 params[1] = 0.0; 1108 } 1109 else { 1110 // This histogram method has a tendency to stretch the 1111 // range of values to be a bit too big, so we can 1112 // account for this by adding and subtracting some percent 1113 // of the difference to the max/min values 1114 // to prevent the gain from going too high. 1115 double diff = minmax[1] - minmax[0]; 1116 double newmax = (minmax[1] + (diff * 0.1)); 1117 double newmin = (minmax[0] - (diff * 0.1)); 1118 1119 if (newmax <= maxDataValue) { 1120 minmax[1] = newmax; 1121 } 1122 1123 if (newmin >= 0) { 1124 minmax[0] = newmin; 1125 } 1126 1127 params[0] = maxDataValue / (minmax[1] - minmax[0]); 1128 params[1] = -minmax[0]; 1129 } 1130 1131 return retval; 1132 } 1133 1134 /** 1135 * Apply autocontrast parameters to the original data in place (destructive) 1136 * 1137 * @param data_in 1138 * the original data array of signed/unsigned integers 1139 * @param data_out 1140 * the converted data array of signed/unsigned integers 1141 * @param params 1142 * the auto gain parameter. params[0]=gain, params[1]=bias 1143 * @param minmax 1144 * the data range. minmax[0]=min, minmax[1]=max 1145 * @param isUnsigned 1146 * the flag to indicate if the data array is unsigned integer 1147 * 1148 * @return the data array with the auto contrast conversion; otherwise, 1149 * returns null 1150 */ 1151 public static Object autoContrastApply(Object data_in, Object data_out, double[] params, double[] minmax, 1152 boolean isUnsigned) { 1153 int size = 0; 1154 double min = -MAX_INT64, max = MAX_INT64; 1155 1156 if ((data_in == null) || (params == null) || (params.length < 2)) { 1157 return null; 1158 } 1159 1160 if (minmax != null) { 1161 min = minmax[0]; 1162 max = minmax[1]; 1163 } 1164 // input and output array must be the same size 1165 size = Array.getLength(data_in); 1166 if ((data_out != null) && (size != Array.getLength(data_out))) { 1167 return null; 1168 } 1169 1170 double gain = params[0]; 1171 double bias = params[1]; 1172 double value_out, value_in; 1173 String cname = data_in.getClass().getName(); 1174 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 1175 1176 switch (dname) { 1177 case 'B': 1178 byte[] b_in = (byte[]) data_in; 1179 if (data_out == null) { 1180 data_out = new byte[size]; 1181 } 1182 byte[] b_out = (byte[]) data_out; 1183 byte b_max = (byte) MAX_INT8; 1184 1185 for (int i = 0; i < size; i++) { 1186 value_in = Math.max(b_in[i], min); 1187 value_in = Math.min(b_in[i], max); 1188 value_out = (value_in + bias) * gain; 1189 value_out = Math.max(value_out, 0.0); 1190 value_out = Math.min(value_out, b_max); 1191 b_out[i] = (byte) value_out; 1192 } 1193 break; 1194 case 'S': 1195 short[] s_in = (short[]) data_in; 1196 if (data_out == null) { 1197 data_out = new short[size]; 1198 } 1199 short[] s_out = (short[]) data_out; 1200 short s_max = (short) MAX_INT16; 1201 1202 if (isUnsigned) { 1203 s_max = (short) MAX_UINT8; // data was upgraded from unsigned byte 1204 } 1205 1206 for (int i = 0; i < size; i++) { 1207 value_in = Math.max(s_in[i], min); 1208 value_in = Math.min(s_in[i], max); 1209 value_out = (value_in + bias) * gain; 1210 value_out = Math.max(value_out, 0.0); 1211 value_out = Math.min(value_out, s_max); 1212 s_out[i] = (byte) value_out; 1213 } 1214 break; 1215 case 'I': 1216 int[] i_in = (int[]) data_in; 1217 if (data_out == null) { 1218 data_out = new int[size]; 1219 } 1220 int[] i_out = (int[]) data_out; 1221 int i_max = (int) MAX_INT32; 1222 if (isUnsigned) { 1223 i_max = (int) MAX_UINT16; // data was upgraded from unsigned short 1224 } 1225 1226 for (int i = 0; i < size; i++) { 1227 value_in = Math.max(i_in[i], min); 1228 value_in = Math.min(i_in[i], max); 1229 value_out = (value_in + bias) * gain; 1230 value_out = Math.max(value_out, 0.0); 1231 value_out = Math.min(value_out, i_max); 1232 i_out[i] = (byte) value_out; 1233 } 1234 break; 1235 case 'J': 1236 long[] l_in = (long[]) data_in; 1237 if (data_out == null) { 1238 data_out = new long[size]; 1239 } 1240 long[] l_out = (long[]) data_out; 1241 long l_max = MAX_INT64; 1242 if (isUnsigned) { 1243 l_max = MAX_UINT32; // data was upgraded from unsigned int 1244 } 1245 1246 for (int i = 0; i < size; i++) { 1247 value_in = Math.max(l_in[i], min); 1248 value_in = Math.min(l_in[i], max); 1249 value_out = (value_in + bias) * gain; 1250 value_out = Math.max(value_out, 0.0); 1251 value_out = Math.min(value_out, l_max); 1252 l_out[i] = (byte) value_out; 1253 } 1254 break; 1255 default: 1256 break; 1257 } // switch (dname) 1258 1259 return data_out; 1260 } 1261 1262 /** 1263 * Converts image raw data to bytes. 1264 * 1265 * The integer data is converted to byte data based on the following rule 1266 * 1267 * <pre> 1268 * uint_8 x 1269 * int_8 (x & 0x7F) << 1 1270 * uint_16 (x >> 8) & 0xFF 1271 * int_16 (x >> 7) & 0xFF 1272 * uint_32 (x >> 24) & 0xFF 1273 * int_32 (x >> 23) & 0xFF 1274 * uint_64 (x >> 56) & 0xFF 1275 * int_64 (x >> 55) & 0xFF 1276 * </pre> 1277 * 1278 * @param src 1279 * the source data array of signed integers or unsigned shorts 1280 * @param dst 1281 * the destination data array of bytes 1282 * @param isUnsigned 1283 * the flag to indicate if the data array is unsigned integer. 1284 * 1285 * @return non-negative if successful; otherwise, returns negative 1286 */ 1287 public static int autoContrastConvertImageBuffer(Object src, byte[] dst, boolean isUnsigned) { 1288 int retval = 0; 1289 1290 if ((src == null) || (dst == null) || (dst.length != Array.getLength(src))) { 1291 return -1; 1292 } 1293 1294 int size = dst.length; 1295 String cname = src.getClass().getName(); 1296 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 1297 switch (dname) { 1298 case 'B': 1299 byte[] b_src = (byte[]) src; 1300 if (isUnsigned) { 1301 for (int i = 0; i < size; i++) { 1302 dst[i] = b_src[i]; 1303 } 1304 } 1305 else { 1306 for (int i = 0; i < size; i++) { 1307 dst[i] = (byte) ((b_src[i] & 0x7F) << 1); 1308 } 1309 } 1310 break; 1311 case 'S': 1312 short[] s_src = (short[]) src; 1313 if (isUnsigned) { // data was upgraded from unsigned byte 1314 for (int i = 0; i < size; i++) { 1315 dst[i] = (byte) s_src[i]; 1316 } 1317 } 1318 else { 1319 for (int i = 0; i < size; i++) { 1320 dst[i] = (byte) ((s_src[i] >> 7) & 0xFF); 1321 } 1322 } 1323 break; 1324 case 'I': 1325 int[] i_src = (int[]) src; 1326 if (isUnsigned) { // data was upgraded from unsigned short 1327 for (int i = 0; i < size; i++) { 1328 dst[i] = (byte) ((i_src[i] >> 8) & 0xFF); 1329 } 1330 } 1331 else { 1332 for (int i = 0; i < size; i++) { 1333 dst[i] = (byte) ((i_src[i] >> 23) & 0xFF); 1334 } 1335 } 1336 break; 1337 case 'J': 1338 long[] l_src = (long[]) src; 1339 if (isUnsigned) { // data was upgraded from unsigned int 1340 for (int i = 0; i < size; i++) { 1341 dst[i] = (byte) ((l_src[i] >> 24) & 0xFF); 1342 } 1343 } 1344 else { 1345 for (int i = 0; i < size; i++) { 1346 dst[i] = (byte) ((l_src[i] >> 55) & 0xFF); 1347 } 1348 } 1349 break; 1350 default: 1351 retval = -1; 1352 break; 1353 } // switch (dname) 1354 1355 return retval; 1356 } 1357 1358 /** 1359 * Computes autocontrast parameters by 1360 * 1361 * <pre> 1362 * min = mean - 3 * std.dev 1363 * max = mean + 3 * std.dev 1364 * </pre> 1365 * 1366 * @param data 1367 * the raw data array 1368 * @param minmax 1369 * the min and max values. 1370 * 1371 * @return non-negative if successful; otherwise, returns negative 1372 */ 1373 public static int autoContrastComputeMinMax(Object data, double[] minmax) { 1374 int retval = 1; 1375 1376 if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) { 1377 return -1; 1378 } 1379 1380 double[] avgstd = { 0, 0 }; 1381 retval = computeStatistics(data, avgstd, null); 1382 if (retval < 0) { 1383 return retval; 1384 } 1385 1386 minmax[0] = avgstd[0] - 3.0 * avgstd[1]; 1387 minmax[1] = avgstd[0] + 3.0 * avgstd[1]; 1388 1389 return retval; 1390 } 1391 1392 /** 1393 * Finds the min and max values of the data array 1394 * 1395 * @param data 1396 * the raw data array 1397 * @param minmax 1398 * the mmin and max values of the array. 1399 * @param fillValue 1400 * the missing value or fill value. Exclude this value when check 1401 * for min/max 1402 * 1403 * @return non-negative if successful; otherwise, returns negative 1404 */ 1405 public static int findMinMax(Object data, double[] minmax, Object fillValue) { 1406 int retval = 1; 1407 1408 if ((data == null) || (minmax == null) || (Array.getLength(data) <= 0) || (Array.getLength(minmax) < 2)) { 1409 return -1; 1410 } 1411 1412 int n = Array.getLength(data); 1413 double fill = 0.0; 1414 boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray()); 1415 1416 String cname = data.getClass().getName(); 1417 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 1418 log.trace("findMinMax() cname={} : dname={}", cname, dname); 1419 1420 minmax[0] = Float.MAX_VALUE; 1421 minmax[1] = -Float.MAX_VALUE; 1422 1423 switch (dname) { 1424 case 'B': 1425 byte[] b = (byte[]) data; 1426 minmax[0] = minmax[1] = b[0]; 1427 1428 if (hasFillValue) fill = ((byte[]) fillValue)[0]; 1429 for (int i = 0; i < n; i++) { 1430 if (hasFillValue && b[i] == fill) continue; 1431 if (minmax[0] > b[i]) { 1432 minmax[0] = b[i]; 1433 } 1434 if (minmax[1] < b[i]) { 1435 minmax[1] = b[i]; 1436 } 1437 } 1438 break; 1439 case 'S': 1440 short[] s = (short[]) data; 1441 minmax[0] = minmax[1] = s[0]; 1442 1443 if (hasFillValue) fill = ((short[]) fillValue)[0]; 1444 1445 for (int i = 0; i < n; i++) { 1446 if (hasFillValue && s[i] == fill) continue; 1447 if (minmax[0] > s[i]) { 1448 minmax[0] = s[i]; 1449 } 1450 if (minmax[1] < s[i]) { 1451 minmax[1] = s[i]; 1452 } 1453 } 1454 break; 1455 case 'I': 1456 int[] ia = (int[]) data; 1457 minmax[0] = minmax[1] = ia[0]; 1458 1459 if (hasFillValue) fill = ((int[]) fillValue)[0]; 1460 1461 for (int i = 0; i < n; i++) { 1462 if (hasFillValue && ia[i] == fill) continue; 1463 if (minmax[0] > ia[i]) { 1464 minmax[0] = ia[i]; 1465 } 1466 if (minmax[1] < ia[i]) { 1467 minmax[1] = ia[i]; 1468 } 1469 } 1470 break; 1471 case 'J': 1472 long[] l = (long[]) data; 1473 minmax[0] = minmax[1] = l[0]; 1474 1475 if (hasFillValue) fill = ((long[]) fillValue)[0]; 1476 for (int i = 0; i < n; i++) { 1477 if (hasFillValue && l[i] == fill) continue; 1478 if (minmax[0] > l[i]) { 1479 minmax[0] = l[i]; 1480 } 1481 if (minmax[1] < l[i]) { 1482 minmax[1] = l[i]; 1483 } 1484 } 1485 break; 1486 case 'F': 1487 float[] f = (float[]) data; 1488 minmax[0] = minmax[1] = f[0]; 1489 1490 if (hasFillValue) fill = ((float[]) fillValue)[0]; 1491 for (int i = 0; i < n; i++) { 1492 if ((hasFillValue && f[i] == fill) || isNaNINF((double) f[i])) continue; 1493 if (minmax[0] > f[i]) { 1494 minmax[0] = f[i]; 1495 } 1496 if (minmax[1] < f[i]) { 1497 minmax[1] = f[i]; 1498 } 1499 } 1500 1501 break; 1502 case 'D': 1503 double[] d = (double[]) data; 1504 minmax[0] = minmax[1] = d[0]; 1505 1506 if (hasFillValue) fill = ((double[]) fillValue)[0]; 1507 for (int i = 0; i < n; i++) { 1508 if ((hasFillValue && d[i] == fill) || isNaNINF(d[i])) continue; 1509 1510 if (minmax[0] > d[i]) { 1511 minmax[0] = d[i]; 1512 } 1513 if (minmax[1] < d[i]) { 1514 minmax[1] = d[i]; 1515 } 1516 } 1517 break; 1518 default: 1519 retval = -1; 1520 break; 1521 } // switch (dname) 1522 1523 return retval; 1524 } 1525 1526 /** 1527 * Finds the distribution of data values 1528 * 1529 * @param data 1530 * the raw data array 1531 * @param dataDist 1532 * the data distirbution. 1533 * @param minmax 1534 * the data range 1535 * 1536 * @return non-negative if successful; otherwise, returns negative 1537 */ 1538 public static int findDataDist(Object data, int[] dataDist, double[] minmax) { 1539 int retval = 0; 1540 double delt = 1; 1541 1542 if ((data == null) || (minmax == null) || dataDist == null) return -1; 1543 1544 int n = Array.getLength(data); 1545 1546 if (minmax[1] != minmax[0]) delt = (dataDist.length - 1) / (minmax[1] - minmax[0]); 1547 1548 for (int i = 0; i < dataDist.length; i++) 1549 dataDist[i] = 0; 1550 1551 int idx; 1552 double val; 1553 for (int i = 0; i < n; i++) { 1554 val = ((Number) Array.get(data, i)).doubleValue(); 1555 if (val>=minmax[0] && val <=minmax[1]) { 1556 idx = (int) ((val - minmax[0]) * delt); 1557 dataDist[idx]++; 1558 } // don't count invalid values 1559 } 1560 1561 return retval; 1562 } 1563 1564 /** 1565 * Computes mean and standard deviation of a data array 1566 * 1567 * @param data 1568 * the raw data array 1569 * @param avgstd 1570 * the statistics: avgstd[0]=mean and avgstd[1]=stdev. 1571 * @param fillValue 1572 * the missing value or fill value. Exclude this value when 1573 * compute statistics 1574 * 1575 * @return non-negative if successful; otherwise, returns negative 1576 */ 1577 public static int computeStatistics(Object data, double[] avgstd, Object fillValue) { 1578 int retval = 1, npoints = 0; 1579 double sum = 0, avg = 0.0, var = 0.0, diff = 0.0, fill = 0.0; 1580 1581 if ((data == null) || (avgstd == null) || (Array.getLength(data) <= 0) || (Array.getLength(avgstd) < 2)) { 1582 return -1; 1583 } 1584 1585 int n = Array.getLength(data); 1586 boolean hasFillValue = (fillValue != null && fillValue.getClass().isArray()); 1587 1588 String cname = data.getClass().getName(); 1589 char dname = cname.charAt(cname.lastIndexOf("[") + 1); 1590 log.trace("computeStatistics() cname={} : dname={}", cname, dname); 1591 1592 npoints = 0; 1593 switch (dname) { 1594 case 'B': 1595 byte[] b = (byte[]) data; 1596 if (hasFillValue) fill = ((byte[]) fillValue)[0]; 1597 for (int i = 0; i < n; i++) { 1598 if (hasFillValue && b[i] == fill) continue; 1599 sum += b[i]; 1600 npoints++; 1601 } 1602 avg = sum / npoints; 1603 for (int i = 0; i < n; i++) { 1604 if (hasFillValue && b[i] == fill) continue; 1605 diff = b[i] - avg; 1606 var += diff * diff; 1607 } 1608 break; 1609 case 'S': 1610 short[] s = (short[]) data; 1611 if (hasFillValue) fill = ((short[]) fillValue)[0]; 1612 for (int i = 0; i < n; i++) { 1613 if (hasFillValue && s[i] == fill) continue; 1614 sum += s[i]; 1615 npoints++; 1616 } 1617 avg = sum / npoints; 1618 for (int i = 0; i < n; i++) { 1619 if (hasFillValue && s[i] == fill) continue; 1620 diff = s[i] - avg; 1621 var += diff * diff; 1622 } 1623 break; 1624 case 'I': 1625 int[] ia = (int[]) data; 1626 if (hasFillValue) fill = ((int[]) fillValue)[0]; 1627 for (int i = 0; i < n; i++) { 1628 if (hasFillValue && ia[i] == fill) continue; 1629 sum += ia[i]; 1630 npoints++; 1631 } 1632 avg = sum / npoints; 1633 for (int i = 0; i < n; i++) { 1634 if (hasFillValue && ia[i] == fill) continue; 1635 diff = ia[i] - avg; 1636 var += diff * diff; 1637 } 1638 break; 1639 case 'J': 1640 long[] l = (long[]) data; 1641 if (hasFillValue) fill = ((long[]) fillValue)[0]; 1642 for (int i = 0; i < n; i++) { 1643 if (hasFillValue && l[i] == fill) continue; 1644 sum += l[i]; 1645 npoints++; 1646 } 1647 1648 avg = sum / npoints; 1649 for (int i = 0; i < n; i++) { 1650 if (hasFillValue && l[i] == fill) continue; 1651 diff = l[i] - avg; 1652 var += diff * diff; 1653 } 1654 break; 1655 case 'F': 1656 float[] f = (float[]) data; 1657 if (hasFillValue) fill = ((float[]) fillValue)[0]; 1658 for (int i = 0; i < n; i++) { 1659 if (hasFillValue && f[i] == fill) continue; 1660 sum += f[i]; 1661 npoints++; 1662 } 1663 1664 avg = sum / npoints; 1665 for (int i = 0; i < n; i++) { 1666 if (hasFillValue && f[i] == fill) continue; 1667 diff = f[i] - avg; 1668 var += diff * diff; 1669 } 1670 break; 1671 case 'D': 1672 double[] d = (double[]) data; 1673 if (hasFillValue) fill = ((double[]) fillValue)[0]; 1674 for (int i = 0; i < n; i++) { 1675 if (hasFillValue && d[i] == fill) continue; 1676 sum += d[i]; 1677 npoints++; 1678 } 1679 avg = sum / npoints; 1680 for (int i = 0; i < n; i++) { 1681 if (hasFillValue && d[i] == fill) continue; 1682 diff = d[i] - avg; 1683 var += diff * diff; 1684 } 1685 break; 1686 default: 1687 retval = -1; 1688 break; 1689 } // switch (dname) 1690 1691 if (npoints <= 1) { 1692 if (npoints < 1) avgstd[0] = fill; 1693 avgstd[1] = 0; 1694 } 1695 else { 1696 avgstd[0] = avg; 1697 avgstd[1] = Math.sqrt(var / (npoints - 1)); 1698 } 1699 1700 return retval; 1701 } 1702 1703 /** 1704 * Returns a string representation of the long argument as an unsigned 1705 * integer in base 2. This is different from Long.toBinaryString(long i). 1706 * This function add padding (0's) to the string based on the nbytes. For 1707 * example, if v=15, nbytes=1, the string will be "00001111". 1708 * 1709 * @param v 1710 * the long value 1711 * @param nbytes 1712 * number of bytes in the integer 1713 * 1714 * @return the string representation of the unsigned long value represented 1715 * by the argument in binary (base 2). 1716 */ 1717 public static final String toBinaryString(long v, int nbytes) { 1718 if (nbytes <= 0) return null; 1719 1720 int nhex = nbytes * 2; 1721 short[] hex = new short[nhex]; 1722 1723 for (int i = 0; i < nhex; i++) 1724 hex[i] = (short) (0x0F & (v >> (i * 4))); 1725 1726 StringBuffer sb = new StringBuffer(); 1727 boolean isEven = true; 1728 for (int i = nhex - 1; i >= 0; i--) { 1729 if (isEven && (i < nhex - 1)) sb.append(" "); 1730 isEven = !isEven; // toggle 1731 1732 switch (hex[i]) { 1733 case 0: 1734 sb.append("0000"); 1735 break; 1736 case 1: 1737 sb.append("0001"); 1738 break; 1739 case 2: 1740 sb.append("0010"); 1741 break; 1742 case 3: 1743 sb.append("0011"); 1744 break; 1745 case 4: 1746 sb.append("0100"); 1747 break; 1748 case 5: 1749 sb.append("0101"); 1750 break; 1751 case 6: 1752 sb.append("0110"); 1753 break; 1754 case 7: 1755 sb.append("0111"); 1756 break; 1757 case 8: 1758 sb.append("1000"); 1759 break; 1760 case 9: 1761 sb.append("1001"); 1762 break; 1763 case 10: 1764 sb.append("1010"); 1765 break; 1766 case 11: 1767 sb.append("1011"); 1768 break; 1769 case 12: 1770 sb.append("1100"); 1771 break; 1772 case 13: 1773 sb.append("1101"); 1774 break; 1775 case 14: 1776 sb.append("1110"); 1777 break; 1778 case 15: 1779 sb.append("1111"); 1780 break; 1781 } 1782 } 1783 1784 return sb.toString(); 1785 } 1786 1787 final static char[] HEXCHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 1788 1789 /** 1790 * Returns a string representation of the long argument as an unsigned integer in base 16. This 1791 * is different from Long.toHexString(long i). This function add padding (0's) to the string 1792 * based on the nbytes. For example, if v=42543, nbytes=4, the string will be "0000A62F". 1793 * 1794 * @param v 1795 * the long value 1796 * @param nbytes 1797 * number of bytes in the integer 1798 * @return the string representation of the unsigned long value represented by the argument in 1799 * hexadecimal (base 16). 1800 */ 1801 public static final String toHexString (long v, int nbytes) { 1802 if (nbytes <= 0) return null; 1803 1804 int nhex = nbytes * 2; 1805 short[] hex = new short[nhex]; 1806 1807 for (int i = 0; i < nhex; i++) { 1808 hex[i] = (short) (0x0F & (v >> (i * 4))); 1809 } 1810 1811 StringBuffer sb = new StringBuffer(); 1812 for (int i = nhex - 1; i >= 0; i--) { 1813 sb.append(HEXCHARS[hex[i]]); 1814 } 1815 1816 return sb.toString(); 1817 } 1818 1819 /** 1820 * Apply bitmask to a data array. 1821 * 1822 * @param theData 1823 * the data array which the bitmask is applied to. 1824 * @param theMask 1825 * the bitmask to be applied to the data array. 1826 * @param op 1827 * the bitmask op to be applied 1828 * 1829 * @return true if bitmask is applied successfuly; otherwise, false. 1830 */ 1831 public static final boolean applyBitmask(Object theData, BitSet theMask, ViewProperties.BITMASK_OP op) { 1832 if (theData == null || Array.getLength(theData) <= 0 || theMask == null) return false; 1833 1834 char nt = '0'; 1835 String cName = theData.getClass().getName(); 1836 int cIndex = cName.lastIndexOf("["); 1837 if (cIndex >= 0) { 1838 nt = cName.charAt(cIndex + 1); 1839 } 1840 1841 // only deal with 8/16/32/64 bit datasets 1842 if (!(nt == 'B' || nt == 'S' || nt == 'I' || nt == 'J')) return false; 1843 1844 long bmask = 0, theValue = 0, packedValue = 0, bitValue = 0; 1845 1846 int nbits = theMask.length(); 1847 int len = Array.getLength(theData); 1848 1849 for (int i = 0; i < nbits; i++) { 1850 if (theMask.get(i)) bmask += 1 << i; 1851 } 1852 1853 for (int i = 0; i < len; i++) { 1854 if (nt == 'B') 1855 theValue = ((byte[]) theData)[i] & bmask; 1856 else if (nt == 'S') 1857 theValue = ((short[]) theData)[i] & bmask; 1858 else if (nt == 'I') 1859 theValue = ((int[]) theData)[i] & bmask; 1860 else if (nt == 'J') 1861 theValue = ((long[]) theData)[i] & bmask; 1862 1863 // apply bitmask only 1864 if (op == BITMASK_OP.AND) 1865 packedValue = theValue; 1866 else { 1867 // extract bits 1868 packedValue = 0; 1869 int bitPosition = 0; 1870 bitValue = 0; 1871 1872 for (int j = 0; j < nbits; j++) { 1873 if (theMask.get(j)) { 1874 bitValue = (theValue & 1); 1875 packedValue += (bitValue << bitPosition); 1876 bitPosition++; 1877 } 1878 // move to the next bit 1879 theValue = theValue >> 1; 1880 } 1881 } 1882 1883 if (nt == 'B') 1884 ((byte[]) theData)[i] = (byte) packedValue; 1885 else if (nt == 'S') 1886 ((short[]) theData)[i] = (short) packedValue; 1887 else if (nt == 'I') 1888 ((int[]) theData)[i] = (int) packedValue; 1889 else if (nt == 'J') 1890 ((long[]) theData)[i] = packedValue; 1891 } /* for (int i = 0; i < len; i++) */ 1892 1893 return true; 1894 } /* public static final boolean applyBitmask() */ 1895 1896 /** 1897 * Launch default browser for a given URL. 1898 * 1899 * @param url 1900 * the URL to open. 1901 * 1902 * @throws Exception if a failure occurred 1903 */ 1904 public static final void launchBrowser(String url) throws Exception { 1905 String os = System.getProperty("os.name"); 1906 Runtime runtime = Runtime.getRuntime(); 1907 1908 // Block for Windows Platform 1909 if (os.startsWith("Windows")) { 1910 String cmd = "rundll32 url.dll,FileProtocolHandler " + url; 1911 1912 if (new File(url).exists()) cmd = "cmd /c start \"\" \"" + url + "\""; 1913 runtime.exec(cmd); 1914 } 1915 // Block for Mac OS 1916 else if (os.startsWith("Mac OS")) { 1917 Class<?> fileMgr = Class.forName("com.apple.eio.FileManager"); 1918 Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] { String.class }); 1919 1920 if (new File(url).exists()) { 1921 // local file 1922 url = "file://" + url; 1923 } 1924 openURL.invoke(null, new Object[] { url }); 1925 } 1926 // Block for UNIX Platform 1927 else { 1928 String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" }; 1929 String browser = null; 1930 for (int count = 0; count < browsers.length && browser == null; count++) 1931 if (runtime.exec(new String[] { "which", browsers[count] }).waitFor() == 0) browser = browsers[count]; 1932 if (browser == null) 1933 throw new Exception("Could not find web browser"); 1934 else 1935 runtime.exec(new String[] { browser, url }); 1936 } 1937 } /* public static final void launchBrowser(String url) */ 1938 1939 /** 1940 * Check and find a non-exist file. 1941 * 1942 * @param path 1943 * -- the path that the new file will be checked. 1944 * @param ext 1945 * -- the extention of the new file. 1946 * 1947 * @return -- the new file. 1948 */ 1949 public static final File checkNewFile(String path, String ext) { 1950 File file = new File(path + "new" + ext); 1951 int i = 1; 1952 1953 while (file.exists()) { 1954 file = new File(path + "new" + i + ext); 1955 i++; 1956 } 1957 1958 return file; 1959 } 1960 1961 /** 1962 * Check if a given number if NaN or INF. 1963 * 1964 * @param val 1965 * the nubmer to be checked 1966 * 1967 * @return true if the number is Nan or INF; otherwise, false. 1968 */ 1969 public static final boolean isNaNINF(double val) { 1970 if (Double.isNaN(val) || val == Float.NEGATIVE_INFINITY || val == Float.POSITIVE_INFINITY 1971 || val == Double.NEGATIVE_INFINITY || val == Double.POSITIVE_INFINITY) return true; 1972 1973 return false; 1974 } 1975}