Commit 24d7049d authored by Andrey Filippov's avatar Andrey Filippov

Recording geo-tagged float Tiff

parent ff164bbf
...@@ -487,10 +487,10 @@ public class CLTParameters { ...@@ -487,10 +487,10 @@ public class CLTParameters {
public int [] gmap_tex_pals = {0,1,2}; public int [] gmap_tex_pals = {0,1,2};
public boolean gmap_save_alt = true; // save height map (meters ASL) public boolean gmap_save_alt = true; // save height map (meters ASL)
public boolean gmap_save_tiff32 = true; // save float TIFF (NaN for transparency) with geo metadata (top left corner)
public boolean gmap_save_tiff = true; // save transparent TIFF with geo metadata (top left corner) public boolean gmap_save_tiff = true; // save transparent TIFF with geo metadata (top left corner)
public int gmap_tiff_pal = 0; // LWIR palette for TIFF public int gmap_tiff_pal = 0; // LWIR palette for TIFF
public boolean generate_bg = true; // Generate background image for 3dmodels public boolean generate_bg = true; // Generate background image for 3dmodels
public boolean show_textures = true; // show generated textures public boolean show_textures = true; // show generated textures
public boolean debug_filters = false;// show intermediate results of filtering public boolean debug_filters = false;// show intermediate results of filtering
...@@ -1635,6 +1635,11 @@ public class CLTParameters { ...@@ -1635,6 +1635,11 @@ public class CLTParameters {
properties.setProperty(prefix+"gmap_crop_extra", this.gmap_crop_extra+""); // int properties.setProperty(prefix+"gmap_crop_extra", this.gmap_crop_extra+""); // int
properties.setProperty(prefix+"gmap_tex_pals", CLTParameters.arr_to_str(this.gmap_tex_pals)); // int[] properties.setProperty(prefix+"gmap_tex_pals", CLTParameters.arr_to_str(this.gmap_tex_pals)); // int[]
properties.setProperty(prefix+"gmap_save_alt", this.gmap_save_alt+""); // boolean
properties.setProperty(prefix+"gmap_save_tiff32", this.gmap_save_tiff32+""); // boolean
properties.setProperty(prefix+"gmap_save_tiff", this.gmap_save_tiff+""); // boolean
properties.setProperty(prefix+"gmap_tiff_pal", this.gmap_tiff_pal+""); // int
properties.setProperty(prefix+"generate_bg", this.generate_bg+""); properties.setProperty(prefix+"generate_bg", this.generate_bg+"");
properties.setProperty(prefix+"show_textures", this.show_textures+""); properties.setProperty(prefix+"show_textures", this.show_textures+"");
properties.setProperty(prefix+"debug_filters", this.debug_filters+""); properties.setProperty(prefix+"debug_filters", this.debug_filters+"");
...@@ -2654,6 +2659,11 @@ public class CLTParameters { ...@@ -2654,6 +2659,11 @@ public class CLTParameters {
if (properties.getProperty(prefix+"gmap_crop_extra")!=null) this.gmap_crop_extra=Integer.parseInt(properties.getProperty(prefix+ "gmap_crop_extra"));// int if (properties.getProperty(prefix+"gmap_crop_extra")!=null) this.gmap_crop_extra=Integer.parseInt(properties.getProperty(prefix+ "gmap_crop_extra"));// int
if (properties.getProperty(prefix+"gmap_tex_pals")!=null) this.gmap_tex_pals=CLTParameters.str_to_iarr(properties.getProperty(prefix+"gmap_tex_pals")); if (properties.getProperty(prefix+"gmap_tex_pals")!=null) this.gmap_tex_pals=CLTParameters.str_to_iarr(properties.getProperty(prefix+"gmap_tex_pals"));
if (properties.getProperty(prefix+"gmap_save_alt")!=null) this.gmap_save_alt=Boolean.parseBoolean(properties.getProperty(prefix+ "gmap_save_alt"));
if (properties.getProperty(prefix+"gmap_save_tiff32")!=null) this.gmap_save_tiff32=Boolean.parseBoolean(properties.getProperty(prefix+ "gmap_save_tiff32"));
if (properties.getProperty(prefix+"gmap_save_tiff")!=null) this.gmap_save_tiff=Boolean.parseBoolean(properties.getProperty(prefix+ "gmap_save_tiff"));
if (properties.getProperty(prefix+"gmap_tiff_pal")!=null) this.gmap_tiff_pal=Integer.parseInt(properties.getProperty(prefix+ "gmap_tiff_pal"));// int
if (properties.getProperty(prefix+"generate_bg")!=null) this.generate_bg=Boolean.parseBoolean(properties.getProperty(prefix+"generate_bg")); if (properties.getProperty(prefix+"generate_bg")!=null) this.generate_bg=Boolean.parseBoolean(properties.getProperty(prefix+"generate_bg"));
if (properties.getProperty(prefix+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures")); if (properties.getProperty(prefix+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures"));
if (properties.getProperty(prefix+"debug_filters")!=null) this.debug_filters=Boolean.parseBoolean(properties.getProperty(prefix+"debug_filters")); if (properties.getProperty(prefix+"debug_filters")!=null) this.debug_filters=Boolean.parseBoolean(properties.getProperty(prefix+"debug_filters"));
...@@ -3923,6 +3933,15 @@ public class CLTParameters { ...@@ -3923,6 +3933,15 @@ public class CLTParameters {
gd.addStringField ("LWIR palettes to render the maps", CLTParameters.arr_to_str(this.gmap_tex_pals), 40, gd.addStringField ("LWIR palettes to render the maps", CLTParameters.arr_to_str(this.gmap_tex_pals), 40,
"Enter space-separated list of the palette numbers, such as, such as '0 1 2'"); "Enter space-separated list of the palette numbers, such as, such as '0 1 2'");
gd.addCheckbox ("Save height map (meters ASL)", this.gmap_save_alt, // true; // enable change FG pixel to opaque from transparent
"Save height map (meters Above Seal Level) as a floating-point single-slice TIFF (NAN for transparency).");
gd.addCheckbox ("Save float Tiff with transparency and Geo",this.gmap_save_tiff32, // true; // enable change FG pixel to opaque from transparent
"Save float (32-bit) TIFF with geo and resolution metadata (coordinates correspond to the top left image corner), NAN for transparency.");
gd.addCheckbox ("Save ARGB Tiff with transparency and Geo",this.gmap_save_tiff, // true; // enable change FG pixel to opaque from transparent
"Save transparent TIFF with geo and resolution metadata (coordinates correspond to the top left image corner).");
gd.addNumericField("Crop maps extra", this.gmap_tiff_pal, 0,3,"", // minimal neighbors to keep alpha
"LWIR palette for Geo/transparency TIFF output.");
gd.addMessage ("Earlier 3D generation parameters"); gd.addMessage ("Earlier 3D generation parameters");
gd.addCheckbox ("Generate background (infinity) image for 3D (not needed for view down)", this.generate_bg); gd.addCheckbox ("Generate background (infinity) image for 3D (not needed for view down)", this.generate_bg);
gd.addCheckbox ("Show generated textures", this.show_textures); gd.addCheckbox ("Show generated textures", this.show_textures);
...@@ -5138,6 +5157,12 @@ public class CLTParameters { ...@@ -5138,6 +5157,12 @@ public class CLTParameters {
this.gmap_crop_empty= gd.getNextBoolean(); this.gmap_crop_empty= gd.getNextBoolean();
this.gmap_crop_extra= (int) gd.getNextNumber(); this.gmap_crop_extra= (int) gd.getNextNumber();
this.gmap_tex_pals = CLTParameters.str_to_iarr(gd.getNextString()); this.gmap_tex_pals = CLTParameters.str_to_iarr(gd.getNextString());
this.gmap_save_alt= gd.getNextBoolean();
this.gmap_save_tiff32= gd.getNextBoolean();
this.gmap_save_tiff= gd.getNextBoolean();
this.gmap_tiff_pal= (int) gd.getNextNumber();
this.generate_bg= gd.getNextBoolean(); this.generate_bg= gd.getNextBoolean();
this.show_textures= gd.getNextBoolean(); this.show_textures= gd.getNextBoolean();
this.debug_filters= gd.getNextBoolean(); this.debug_filters= gd.getNextBoolean();
......
...@@ -114,7 +114,7 @@ public class DumpImageMetadata ...@@ -114,7 +114,7 @@ public class DumpImageMetadata
System.out.println(">"); System.out.println(">");
} }
private static void dumpMetadata(IIOMetadata metadata) static void dumpMetadata(IIOMetadata metadata)
{ {
String[] names = metadata.getMetadataFormatNames(); String[] names = metadata.getMetadataFormatNames();
int length = names.length; int length = names.length;
......
...@@ -23,11 +23,18 @@ ...@@ -23,11 +23,18 @@
*/ */
package com.elphel.imagej.readers; package com.elphel.imagej.readers;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.awt.image.ColorModel; import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferFloat;
import java.awt.image.Raster; import java.awt.image.Raster;
import java.awt.image.SampleModel; import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.PixelInterleavedSampleModel;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.time.LocalDateTime; import java.time.LocalDateTime;
...@@ -68,7 +75,7 @@ public class ElphelTiffWriter { ...@@ -68,7 +75,7 @@ public class ElphelTiffWriter {
public static String TIFF_FIELD_DESCRIPTION_ATTRIBUTE = "description"; public static String TIFF_FIELD_DESCRIPTION_ATTRIBUTE = "description";
public static void saveTiffARGB32( public static void saveTiffARGBOrGray32(
ImagePlus imp, ImagePlus imp,
String path, // full path to save image String path, // full path to save image
double [] lla, // latitude, longitude, altitude (or null) double [] lla, // latitude, longitude, altitude (or null)
...@@ -87,19 +94,30 @@ public class ElphelTiffWriter { ...@@ -87,19 +94,30 @@ public class ElphelTiffWriter {
// https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html // https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/javax/imageio/metadata/doc-files/tiff_metadata.html
// tiffWriteParam.setCompressionType("Exif JPEG"); // tiffWriteParam.setCompressionType("Exif JPEG");
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED); tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
BufferedImage bufferedImageAlpha;
BufferedImage bufferedImage = (BufferedImage) imp.getImage(); // properties=null
if (imp.getType()==ImagePlus.COLOR_RGB) {
/* ImagePlus image removes alpha, so we need to create a new ARGB BufferedImage /* ImagePlus image removes alpha, so we need to create a new ARGB BufferedImage
* and copy data (it is correct in imp.getImage() */ * and copy data (it is correct in imp.getImage() */
BufferedImage bufferedImage = (BufferedImage) imp.getImage(); // properties=null bufferedImageAlpha = new BufferedImage(
BufferedImage bufferedImageAlpha = new BufferedImage(
bufferedImage.getWidth(), bufferedImage.getWidth(),
bufferedImage.getHeight(), bufferedImage.getHeight(),
BufferedImage.TYPE_INT_ARGB); BufferedImage.TYPE_INT_ARGB);
int [] ipixels = (int[])imp.getProcessor().getPixels(); int [] ipixels = (int[])imp.getProcessor().getPixels();
final int[] bia_as_arr = ( (DataBufferInt) bufferedImageAlpha.getRaster().getDataBuffer() ).getData(); final int[] bia_as_arr = ( (DataBufferInt) bufferedImageAlpha.getRaster().getDataBuffer() ).getData();
System.arraycopy(ipixels, 0, bia_as_arr, 0, bia_as_arr.length); System.arraycopy(ipixels, 0, bia_as_arr, 0, bia_as_arr.length);
} else {
bufferedImageAlpha = createFloatBufferedImage(
bufferedImage.getWidth(),
bufferedImage.getHeight()
);
float [] pixels = (float[]) imp.getProcessor().getPixels();
final float[] bia_as_arr = ( (DataBufferFloat) bufferedImageAlpha.getRaster().getDataBuffer() ).getData();
System.arraycopy(pixels, 0, bia_as_arr, 0, bia_as_arr.length);
}
ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(bufferedImageAlpha); ImageTypeSpecifier imageType = ImageTypeSpecifier.createFromRenderedImage(bufferedImageAlpha);
IIOMetadata imageMetadata = tiffWriter.getDefaultImageMetadata(imageType, tiffWriteParam); IIOMetadata imageMetadata = tiffWriter.getDefaultImageMetadata(imageType, tiffWriteParam);
if (debugLevel > 0) {System.out.println("\n=== imageMetadata ===:");DumpImageMetadata.dumpMetadata(imageMetadata);}
// just for testing: // just for testing:
int cm_dig_after = 9; int cm_dig_after = 9;
// Here is a way adding tag sets through TIFFDirectory // Here is a way adding tag sets through TIFFDirectory
...@@ -166,6 +184,82 @@ public class ElphelTiffWriter { ...@@ -166,6 +184,82 @@ public class ElphelTiffWriter {
System.out.println(); System.out.println();
} }
private static BufferedImage createFloatBufferedImage(
int width,
int height
) {
//https://stackoverflow.com/questions/26766751/java-create-bufferedimage-with-float-precision/26775056#26775056
// int bands = 4; // 4 bands for ARGB, 3 for RGB etc
// int[] bandOffsets = {0, 1, 2, 3}; // length == bands, 0 == R, 1 == G, 2 == B and 3 == A
int bands = 1; // 4 bands for ARGB, 3 for RGB etc
int[] bandOffsets = {0}; // length == bands, 0 == R, 1 == G, 2 == B and 3 == A
// Create a TYPE_FLOAT sample model (specifying how the pixels are stored)
SampleModel sampleModel0 = new PixelInterleavedSampleModel(
DataBuffer.TYPE_FLOAT,
width,
height,
bands,
width * bands,
bandOffsets);
// Create a color model compatible with this sample model/raster (TYPE_FLOAT)
// Note that the number of bands must equal the number of color components in the
// color space (3 for RGB) + 1 extra band if the color model contains alpha
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_GRAY); // CS_sRGB);
// ColorModel colorModel = new ComponentColorModel(colorSpace, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_FLOAT);
ColorModel colorModel = new ComponentColorModel(
colorSpace,
false, // true,
false,
Transparency.TRANSLUCENT, // ?
DataBuffer.TYPE_FLOAT);
SampleModel sampleModel = colorModel.createCompatibleSampleModel (width, height);
// ...and data buffer (where the pixels are stored)
DataBuffer buffer = new DataBufferFloat(width * height * bands);
// Wrap it in a writable raster
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
// And finally create an image with this raster
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
return image;
/*
* color space (ColorSpace.CS_GRAY) and no transparency may make more sense.
public class FloatImage {
public static void main(String[] args) {
// Define dimensions and layout of the image
int w = 300;
int h = 200;
int bands = 4; // 4 bands for ARGB, 3 for RGB etc
int[] bandOffsets = {0, 1, 2, 3}; // length == bands, 0 == R, 1 == G, 2 == B and 3 == A
// Create a TYPE_FLOAT sample model (specifying how the pixels are stored)
SampleModel sampleModel = new PixelInterleavedSampleModel(DataBuffer.TYPE_FLOAT, w, h, bands, w * bands, bandOffsets);
// ...and data buffer (where the pixels are stored)
DataBuffer buffer = new DataBufferFloat(w * h * bands);
// Wrap it in a writable raster
WritableRaster raster = Raster.createWritableRaster(sampleModel, buffer, null);
// Create a color model compatible with this sample model/raster (TYPE_FLOAT)
// Note that the number of bands must equal the number of color components in the
// color space (3 for RGB) + 1 extra band if the color model contains alpha
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ColorModel colorModel = new ComponentColorModel(colorSpace, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_FLOAT);
// And finally create an image with this raster
BufferedImage image = new BufferedImage(colorModel, raster, colorModel.isAlphaPremultiplied(), null);
System.out.println("image = " + image);
}
}
*/
}
private static IIOMetadataNode createRationalDegrees(double d, int digits_after) { // 8 private static IIOMetadataNode createRationalDegrees(double d, int digits_after) { // 8
int denom = 1; int denom = 1;
for (int i = 0; i < digits_after; i++) denom *= 10; for (int i = 0; i < digits_after; i++) denom *= 10;
......
...@@ -314,7 +314,7 @@ the type of pixel data in this file getPixelType() ...@@ -314,7 +314,7 @@ the type of pixel data in this file getPixelType()
if (imp.getType()==ImagePlus.COLOR_RGB) { // 4*8 bit - type = 0 if (imp.getType()==ImagePlus.COLOR_RGB) { // 4*8 bit - type = 0
if (debugLevel>1) System.out.println("Saving 8-bit TIFF with alpha-channel: "+path); // is it possible to just add alpha to high bytes? if (debugLevel>1) System.out.println("Saving 8-bit TIFF with alpha-channel: "+path); // is it possible to just add alpha to high bytes?
ElphelTiffWriter.saveTiffARGB32( ElphelTiffWriter.saveTiffARGBOrGray32(
imp, imp,
path, path,
lla, // double [] lla, // latitude, longitude, altitude (or null) lla, // double [] lla, // latitude, longitude, altitude (or null)
...@@ -330,6 +330,19 @@ the type of pixel data in this file getPixelType() ...@@ -330,6 +330,19 @@ the type of pixel data in this file getPixelType()
return; return;
} else if (imp.getType()==ImagePlus.GRAY32) { } else if (imp.getType()==ImagePlus.GRAY32) {
boolean save_float = true;
if (save_float) {
if (debugLevel>1) System.out.println("Saving GRAY32 TIFF (NaN for transparency): "+path); // is it possible to just add alpha to high bytes?
ElphelTiffWriter.saveTiffARGBOrGray32(
imp,
path,
lla, // double [] lla, // latitude, longitude, altitude (or null)
dt, // LocalDateTime dt, // local date/time or null
pix_in_meters, //double pix_in_meters, // resolution or Double.NaN
imageJTags,
debugLevel);
} else {
int lwir_palette = 0; int lwir_palette = 0;
double [] minmax = null; double [] minmax = null;
String title = imp.getTitle(); String title = imp.getTitle();
...@@ -349,12 +362,12 @@ the type of pixel data in this file getPixelType() ...@@ -349,12 +362,12 @@ the type of pixel data in this file getPixelType()
imp.getWidth(), // int width, // int tilesX, imp.getWidth(), // int width, // int tilesX,
imp.getHeight(), // int height, // int tilesY, imp.getHeight(), // int height, // int tilesY,
debugLevel); // int debugLevel ) debugLevel); // int debugLevel )
// File test_dump = new File("/home/elphel/Documents/DoD/Ukraine/photos/test/1694531940_120446-02-gps.tiff"); // File test_dump = new File("/home/elphel/Documents/DoD/Ukraine/photos/test/1694531940_120446-02-gps.tiff");
// DumpImageMetadata.processFile(test_dump); // DumpImageMetadata.processFile(test_dump);
if (debugLevel>1) System.out.println("Saving 8-bit RGBA TIFF with alpha-channel: "+path); // is it possible to just add alpha to high bytes? if (debugLevel>1) System.out.println("Saving 8-bit RGBA TIFF with alpha-channel: "+path); // is it possible to just add alpha to high bytes?
ElphelTiffWriter.saveTiffARGB32( ElphelTiffWriter.saveTiffARGBOrGray32(
imp_rgb, imp_rgb,
path, path,
lla, // double [] lla, // latitude, longitude, altitude (or null) lla, // double [] lla, // latitude, longitude, altitude (or null)
...@@ -362,7 +375,8 @@ the type of pixel data in this file getPixelType() ...@@ -362,7 +375,8 @@ the type of pixel data in this file getPixelType()
pix_in_meters, //double pix_in_meters, // resolution or Double.NaN pix_in_meters, //double pix_in_meters, // resolution or Double.NaN
imageJTags, imageJTags,
debugLevel); debugLevel);
// saveTiffARGB32(imp_rgb, path, imageJTags, debugLevel); // saveTiffARGB32(imp_rgb, path, imageJTags, debugLevel);
}
return; return;
} }
IJ.showMessage("Not yet implemented for this image type"); IJ.showMessage("Not yet implemented for this image type");
......
...@@ -16445,7 +16445,7 @@ public class QuadCLTCPU { ...@@ -16445,7 +16445,7 @@ public class QuadCLTCPU {
height, // int height, // int tilesY, height, // int height, // int tilesY,
debugLevel); // int debugLevel ) debugLevel); // int debugLevel )
String image_path = model_dir + Prefs.getFileSeparator() + title+".tiff"; String image_path = model_dir + Prefs.getFileSeparator() + title+".tiff";
ElphelTiffWriter.saveTiffARGB32( ElphelTiffWriter.saveTiffARGBOrGray32(
imp, imp,
image_path, image_path,
lla, // double [] lla, // latitude, longitude, altitude (or null) lla, // double [] lla, // latitude, longitude, altitude (or null)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment