Reputation: 1174
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.
Upvotes: 2
Views: 1918
Reputation: 10976
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
This clearly shows the 4 bands. Finally if you import it in to QGIS to view you are offered the chance to assign any of the 4 bands to the red, green & blue bands.
This produces a variety of images which are normally called false colour or you can opt for a grayscale of a single band if you are looking at physical measurements that don't happen to make sense when combined.
Upvotes: 2