如何将带有4个以上波段的像素数组保存到java中的Tiff / GeoTiff文件中?

时间:2017-01-18 19:53:09

标签: image-processing bufferedimage tiff javax.imageio geotools

假设我手动超过4个频段创建了像素值,我想将它们存储在 tiff 文件中。

这些波段可以是R,G,B,温度(温度值不在0到255的范围内,因此我使用int而不是pexils的字节)等等。任何可从卫星获取的信息

现在我想将这些像素保存到tiff文件中。在java中有一个BufferedImage类,它有很多类型,如:TYPE_4BYTE_ABGR,TYPE_BYTE_GRAY等。但它们都不适用于4个以上的多波段。有TYPE_CUSTOM,但是在指定它并试图保存时数据到Tiff文件它给你一个例外,因为它不支持写操作(仅用于读操作,即它可以读取文件并将类型设置为TYPE_CUSTOM,如果它不理解类型但它不能写文件在一个不理解的类型)。

以下代码适用于3个频段甚至不正确(它没有显示彩色图像,看起来像是一个缺失线条的扭曲图像)但是对于超过4个频段我该怎么做?

    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);

我正在使用GeoTools

编辑: 根据@iant我更改了代码,但它只给出了空白的透明背景,即使我保持相同数量的波段,即3个波段。 @iant你能查看下面的代码吗?

    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();
    }

}

}

更新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();
    }
}

}

注意:我使用了int类型和一个大小为3的数组,因为原始图像有RGB波段。

1 个答案:

答案 0 :(得分:2)

您需要使用其他工厂方法创建WritableRaster,该方法允许您设置所需的数据类型和波段数。

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();
    }

<强>更新

如果您使用以下代码:

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();
    }
  }

它创造了一个10公里的方形栅格(靠近我的房子)。它有4个乐队可以代表你喜欢的任何东西,在它上面运行gdalinfo会给出以下信息:

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

这清楚地显示了4个乐队。最后,如果您将其导入QGIS以查看您有机会将4个乐队中的任何一个分配给红色,绿色和蓝带。

enter image description here

这会产生各种通常称为假色的图像,或者如果您正在查看组合时没有意义的物理测量,则可以选择单个色带的灰度。