• geotools:How to save a pixel array with more than 4 bands to a Tiff/GeoTiff file in java?(如何保存超过4个通道的像素Array到geotiff)


    https://stackoverflow.com/questions/41728277/how-to-save-a-pixel-array-with-more-than-4-bands-to-a-tiff-geotiff-file-in-java

    Suppose I manually created pixel values for more than 4 bands and I want to store them in a tiff file.

    Those bands can be for R, G, B, temperature (values of temperature are not in the range of 0 to 255 thus I am using int instead of byte for pexils), etc. i.e. any information that can be taken from the satellite

    Now I want to save those pixels to a tiff file. In java there is a BufferedImage class which has many types like: TYPE_4BYTE_ABGR, TYPE_BYTE_GRAY, etc. However none of them for multi bands more than 4 bands. There is TYPE_CUSTOM but when specifying it and trying to save the data to a Tiff file it gives you an exception because it is not supported for write operation (only for read operation i.e. it can read the file and set the type to TYPE_CUSTOM if it didn't understand the type but it can not write the file in a not understood type).

    The below code worked for 3 bands even not properly (it doesn't show colored image and it looked like a distorted image with missing lines) but for more than 4 bands how can I do that?

        ImageOutputStream ios = ImageIO.createImageOutputStream(os);
        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("tiff");
        ImageWriter writer = writers.next();
        writer.setOutput(ios);
            int index = 0;
            int[] pixels = new int[width*height*numberOfBands];
            for (int i = 0; i < width; i++) {
                for (int j = 0; j < height; j++) {
                    for (int k = 0; k < numberOfBands; k++) {
                        pixels[index++] = //any values;
                    }
                }
            }
    
        DataBuffer dataBuffer = new DataBufferInt(pixels, pixels.length);
    
        // Create Raster
        WritableRaster writableRaster = Raster.createBandedRaster
            (dataBuffer, width, height,
             width,                      // scanlineStride
             new int[numberOfBands],             // bankIndices,
             new int[numberOfBands],             // bandOffsets,
             null);                     // location
    
        // Create the image
        BufferedImage bufferedImage = new BufferedImage
                (width, height, BufferedImage.TYPE_BYTE_RGB);
        bufferedImage.setData(writableRaster);
        IIOImage iioImage = new IIOImage(bufferedImage, null, null);
        ImageWriteParam param = writer.getDefaultWriteParam();
        writer.write(null, iioImage, param);

    I am using GeoTools by the way

    Edited: According to @iant I changed the code but it is giving blank transparent background only, even I kept the same number of bands i.e. 3 bands. @iant Could you check the code below.

    package examples;
    
        import java.awt.image.WritableRaster;
        import java.io.File;
        import java.io.IOException;
    
        import javax.media.jai.RasterFactory;
    
        import org.geotools.coverage.CoverageFactoryFinder;
        import org.geotools.coverage.grid.GridCoordinates2D;
        import org.geotools.coverage.grid.GridCoverage2D;
        import org.geotools.coverage.grid.GridCoverageFactory;
        import org.geotools.coverage.grid.GridEnvelope2D;
        import org.geotools.coverage.grid.GridGeometry2D;
        import org.geotools.coverage.grid.io.AbstractGridFormat;
        import org.geotools.coverage.grid.io.OverviewPolicy;
        import org.geotools.gce.geotiff.GeoTiffFormat;
        import org.geotools.gce.geotiff.GeoTiffReader;
        import org.opengis.coverage.grid.GridCoverageWriter;
        import org.opengis.parameter.GeneralParameterValue;
        import org.opengis.parameter.ParameterValue;
    
        public class CreateTiffImageTest2 {
    
        public static void main(String[] args) throws IOException {
    
        File file = new File("/home/mosab/Desktop/input/tif.tif");
    
        ParameterValue<OverviewPolicy> policy = AbstractGridFormat.OVERVIEW_POLICY.createValue();
        policy.setValue(OverviewPolicy.IGNORE);
        ParameterValue<String> gridsize = AbstractGridFormat.SUGGESTED_TILE_SIZE.createValue();
        ParameterValue<Boolean> useJaiRead = AbstractGridFormat.USE_JAI_IMAGEREAD.createValue();
        useJaiRead.setValue(true);
    
        GeoTiffReader geoTiffReader = new GeoTiffReader(file);
        GridCoverage2D cov = geoTiffReader.read(new GeneralParameterValue[] { policy, gridsize, useJaiRead });
        GridGeometry2D geometry = cov.getGridGeometry();
        GridEnvelope2D gridEnvelope = geometry.getGridRange2D();
        int w = (int) gridEnvelope.getWidth();
        int h = (int) gridEnvelope.getHeight();
    
        WritableRaster writableRaster = RasterFactory.createBandedRaster(java.awt.image.DataBuffer.TYPE_DOUBLE, w, h, 3,
                null);
        double[] data = new double[3];
        double[] dest = new double[3];
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                GridCoordinates2D coord = new GridCoordinates2D(i, j);
                cov.evaluate(coord, dest);
    
                data[0] = dest[0];
                data[1] = dest[1];
                data[2] = dest[2];
                writableRaster.setPixel(i, j, data);
            }
            float perc = 100.0f * i / w;
            if (i % 100 == 0) {
                System.out.println("done " + perc);
            }
        }
        // Wrap the raster as a Coverage
        GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
        GridCoverage2D gc = factory.create("name", writableRaster, cov.getEnvelope());
        File out = new File("/home/mosab/Desktop/input/tifgen.tif");
        GeoTiffFormat format = new GeoTiffFormat();
        GridCoverageWriter writer = format.getWriter(out);
        try {
            writer.write(gc, null);
            writer.dispose();
        } catch (IllegalArgumentException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    
    }

    }

    Update 2:

    import java.awt.image.BufferedImage;
    import java.awt.image.WritableRaster;
    import java.io.File;
    import java.io.IOException;
    
    import javax.imageio.ImageIO;
    import javax.media.jai.RasterFactory;
    
    import org.geotools.coverage.CoverageFactoryFinder;
    import org.geotools.coverage.grid.GridCoverage2D;
    import org.geotools.coverage.grid.GridCoverageFactory;
    import org.geotools.coverage.grid.io.AbstractGridFormat;
    import org.geotools.coverage.grid.io.GridFormatFinder;
    import org.geotools.factory.Hints;
    import org.geotools.gce.geotiff.GeoTiffFormat;
    import org.geotools.geometry.jts.ReferencedEnvelope;
    import org.geotools.referencing.CRS;
    import org.opengis.coverage.grid.GridCoverageWriter;
    import org.opengis.geometry.MismatchedDimensionException;
    import org.opengis.referencing.FactoryException;
    import org.opengis.referencing.NoSuchAuthorityCodeException;
    import org.opengis.referencing.crs.CoordinateReferenceSystem;
    
    public class Test2 {
    
    public static void print(Object o) {
        System.out.println(o);
    }
    
    public static void main(String[] args)
            throws MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException, IOException {
        File out = new File("/home/mosab/Desktop/input/1.tif");
        BufferedImage img = ImageIO.read(out);
    
        // ColorModel colorModel = img.getColorModel(
        WritableRaster raster = img.getRaster();
    
        int w = img.getWidth();
        int h = img.getHeight();
        print("width = " + w);
        print("heigh = " + h);
        int numBands = raster.getNumBands();
    
        WritableRaster writableRaster = RasterFactory.createBandedRaster(java.awt.image.DataBuffer.TYPE_INT, w, h, 3,
                null);
        //as I said pixels are created manually but I used here pixels from an image to check the approach
        int[] data = new int[3];
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                for (int k = 0; k < numBands; k++) {
                    data[k] = raster.getSample(i, j, k);
                }
                writableRaster.setPixel(i, j, data);
            }
        }
    
        GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
        CoordinateReferenceSystem crs = CRS.decode("EPSG:27700");
        int llx = 500000;
        int lly = 105000;
        ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(llx, llx + (w * 10), lly, lly + (h * 10), crs);
        GridCoverage2D gc = factory.create("name", writableRaster, referencedEnvelope);
    
        AbstractGridFormat format = GridFormatFinder.findFormat(out);
        Hints hints = null;
        if (format instanceof GeoTiffFormat) {
            hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
        }
    
        File out1 = new File("/home/mosab/Desktop/input/tifgen.tif");
        GridCoverageWriter writer = format.getWriter(out1);
        try {
            writer.write(gc, null);
            writer.dispose();
        } catch (IllegalArgumentException | IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    }

    Notice: I used int type and an array of size 3 because the original image has RGB bands.

    if you have such special demands you might consider having your own file format. –Piglet. Jan 19, 2017 at 9:12
    I would work on getting it to work with 3 bands before trying 4 –Ian Turton.Jan 19, 2017 at 16:10
    @Piglet it can be done using the TIff/Geotiff format, because it is intended for that. And by the way I mentioned earlier more than 4 bands, because in case of 4 some software will consider it ARGB i.e. +aplha . –Mosab Shaheen.Jan 21, 2017 at 9:48
    @iant The problem here I am not able to find a type for BufferedImage to support more than 4 bands –Mosab Shaheen.Jan 21, 2017 at 9:50
    that is why you need to use a writable raster which can have as many as you need. GeoTiff is technically limited to INT_MAX. –Ian Turton.Jan 21, 2017 at 11:19

    1 Answer

    You need to create a WritableRaster using the other factory method which allows you to set the required data type and number of bands.

    WritableRaster writableRaster = RasterFactory.createBandedRaster
            (java.awt.image.DataBuffer.TYPE_DOUBLE,width,height,4,null);
        double[] data = new double[4];
        double[] dest = new double[3];
        for(int i=0;i<width;i++) {
          for(int j=0;j<height;j++) {
    //basically anything you like to create the bands
            GridCoordinates2D coord = new GridCoordinates2D(i, j);
    
    //here I just grab the values of my base image and add them together
            cov.evaluate(coord, dest);
    
            data[0]=dest[0];
            data[1] = dest[1];
            data[2] = dest[2];
            data[3] = (dest[0]+dest[1]+dest[2]);
            // write them to the new raster
            writableRaster.setPixel(i, j, data);
          }
          float perc = 100.0f*i/width;
          if(i%100==0) {
            System.out.println("done "+perc);
          }
        }
        //Wrap the raster as a Coverage
        GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
        GridCoverage2D gc = factory.create("name", writableRaster, cov.getEnvelope());
        //write it out
        File out = new File(outFile);
        GridCoverageWriter writer = format.getWriter(out);
        try {
          writer.write(gc , null);
          writer.dispose();
        } catch (IllegalArgumentException | IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }

    UPDATE

    If you take the following code:

     
    public void makeTestRaster() throws MismatchedDimensionException, NoSuchAuthorityCodeException, FactoryException {
        int width = 1000;
        int height = 1000;
    
        WritableRaster writableRaster = RasterFactory.createBandedRaster(java.awt.image.DataBuffer.TYPE_DOUBLE, width,
            height, 4, null);
        double[] data = new double[4];
    
        for (int i = 0; i < width; i++) {
          for (int j = 0; j < height; j++) {
            data[0] = i * 100.0;
            data[1] = j * 100.0;
            data[2] = (width - i) * 100.0;
            data[3] = (height - j) * 100.0;
            System.out.println(i + "," + j + ":" + data[0] + " " + data[1] + " " + data[2] + " " + data[3] + " ");
            writableRaster.setPixel(i, j, data);
          }
          float perc = 100.0f * i / width;
          if (i % 100 == 0) {
            System.out.println("done " + perc);
          }
        }
        File out = new File("test.tif");
        GridCoverageFactory factory = CoverageFactoryFinder.getGridCoverageFactory(null);
        CoordinateReferenceSystem crs = CRS.decode("EPSG:27700");
        int llx = 500000;
        int lly = 105000;
        ReferencedEnvelope referencedEnvelope = new ReferencedEnvelope(llx, llx + (width * 10), lly, lly + (height * 10),
            crs);
        GridCoverage2D gc = factory.create("name", writableRaster, referencedEnvelope);
        AbstractGridFormat format = GridFormatFinder.findFormat(out);
        Hints hints = null;
        if (format instanceof GeoTiffFormat) {
          hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE);
        }
    
        GridCoverageWriter writer = format.getWriter(out);
        try {
          writer.write(gc, null);
          writer.dispose();
        } catch (IllegalArgumentException | IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }

    It creates a 10 KM square raster (near my house). It has 4 bands which could represent anything you like, running gdalinfo on it gives the following information:

    gdalinfo test.tif
    Driver: GTiff/GeoTIFF
    Files: test.tif
           test.tif.aux.xml
    Size is 1000, 1000
    Coordinate System is:
    PROJCS["OSGB 1936 / British National Grid",
        GEOGCS["OSGB 1936",
            DATUM["OSGB_1936",
                SPHEROID["Airy 1830",6377563.396,299.3249646,
                    AUTHORITY["EPSG","7001"]],
                TOWGS84[446.448,-125.157,542.06,0.15,0.247,0.842,-20.489],
                AUTHORITY["EPSG","6277"]],
            PRIMEM["Greenwich",0,
                AUTHORITY["EPSG","8901"]],
            UNIT["degree",0.0174532925199433,
                AUTHORITY["EPSG","9122"]],
            AUTHORITY["EPSG","4277"]],
        PROJECTION["Transverse_Mercator"],
        PARAMETER["latitude_of_origin",49],
        PARAMETER["central_meridian",-2],
        PARAMETER["scale_factor",0.9996012717],
        PARAMETER["false_easting",400000],
        PARAMETER["false_northing",-100000],
        UNIT["metre",1,
            AUTHORITY["EPSG","9001"]],
        AXIS["Easting",EAST],
        AXIS["Northing",NORTH],
        AUTHORITY["EPSG","27700"]]
    Origin = (500000.000000000000000,115000.000000000000000)
    Pixel Size = (10.000000000000000,-10.000000000000000)
    Metadata:
      AREA_OR_POINT=Area
      TIFFTAG_RESOLUTIONUNIT=1 (unitless)
      TIFFTAG_XRESOLUTION=1
      TIFFTAG_YRESOLUTION=1
    Image Structure Metadata:
      INTERLEAVE=PIXEL
    Corner Coordinates:
    Upper Left  (  500000.000,  115000.000) (  0d34'37.20"W, 50d55'30.82"N)
    Lower Left  (  500000.000,  105000.000) (  0d34'47.05"W, 50d50' 7.16"N)
    Upper Right (  510000.000,  115000.000) (  0d26' 5.12"W, 50d55'24.27"N)
    Lower Right (  510000.000,  105000.000) (  0d26'15.95"W, 50d50' 0.62"N)
    Center      (  505000.000,  110000.000) (  0d30'26.33"W, 50d52'45.79"N)
    Band 1 Block=1000x8 Type=Float64, ColorInterp=Gray
      Min=0.000 Max=99900.000 
      Minimum=0.000, Maximum=99900.000, Mean=49950.000, StdDev=28867.499
      Metadata:
        STATISTICS_MAXIMUM=99900
        STATISTICS_MEAN=49950
        STATISTICS_MINIMUM=0
        STATISTICS_STDDEV=28867.499025721
    Band 2 Block=1000x8 Type=Float64, ColorInterp=Undefined
      Min=0.000 Max=97500.000 
      Minimum=0.000, Maximum=97500.000, Mean=48750.000, StdDev=30378.926
      Metadata:
        STATISTICS_MAXIMUM=97500
        STATISTICS_MEAN=48750
        STATISTICS_MINIMUM=0
        STATISTICS_STDDEV=30378.926358031
    Band 3 Block=1000x8 Type=Float64, ColorInterp=Undefined
      Min=0.000 Max=97402500.000 
      Minimum=0.000, Maximum=97402500.000, Mean=24350625.000, StdDev=22476916.605
      Metadata:
        STATISTICS_MAXIMUM=97402500
        STATISTICS_MEAN=24350625
        STATISTICS_MINIMUM=0
        STATISTICS_STDDEV=22476916.605084
    Band 4 Block=1000x8 Type=Float64, ColorInterp=Undefined
      Min=2500.000 Max=100000.000 
      Minimum=2500.000, Maximum=100000.000, Mean=51250.000, StdDev=30378.926
      Metadata:
        STATISTICS_MAXIMUM=100000
        STATISTICS_MEAN=51249.999999999
        STATISTICS_MINIMUM=2500
        STATISTICS_STDDEV=30378.926358031

    Dear, in the code above there is the "cov" variable and you used it two times, first time in the origional image you took the pixels from: cov.evaluate(coord, dest); and second time in the generated image: GridCoverage2D gc = factory.create("name", writableRaster, cov.getEnvelope()); –
    Mosab Shaheen
    Jan 21, 2017 at 14:34
    but as I said before I don't want to depend on the original image I want to set the pixels "manually" as if there is no original image before. So I need the code to either create a separate "cov" variable, not depending on the original one, or trying different approach. So Could you rewrite the code to reflect that. –
    Mosab Shaheen
    Jan 21, 2017 at 14:35
    you can set your pixels any way you want, I happened to copy mine so I could check the output image was correct. You presumably know what the envelope of your output is so you can use that. –
    Ian Turton
    Jan 21, 2017 at 14:37
    Thanks I am trying it is giving me a blank transparent background only. Could you check the complete code I added above in the "Edit Post" –
    Mosab Shaheen
    Jan 21, 2017 at 14:55
    The code is ready to execute just run it and tell me please why it is giving only a blank transparent background( or if you code post the full code of yours to try it) –
    Mosab Shaheen
    Jan 21, 2017 at 14:57

  • 相关阅读:
    锁和监视器之间的区别 – Java并发
    实现Runnable接口和继承Thread类之间的区别
    如何使用wait(), notify() and notifyAll() – Java
    HashMap如何工作
    使用hashCode()和equals()方法
    Compare and Swap [CAS] 算法
    对象级别锁 vs 类级别锁 – Java
    solr的访问权限管理及ubuntu下iptables的设置
    mysql 字符串字段中查找非ascii字符
    tensorflow学习——调试ctc的两个bug
  • 原文地址:https://www.cnblogs.com/2008nmj/p/16270728.html
Copyright © 2020-2023  润新知