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

Recording geo-tagged float Tiff

parent ff164bbf
......@@ -487,10 +487,10 @@ public class CLTParameters {
public int [] gmap_tex_pals = {0,1,2};
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 int gmap_tiff_pal = 0; // LWIR palette for TIFF
public boolean generate_bg = true; // Generate background image for 3dmodels
public boolean show_textures = true; // show generated textures
public boolean debug_filters = false;// show intermediate results of filtering
......@@ -1635,6 +1635,11 @@ public class CLTParameters {
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_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+"show_textures", this.show_textures+"");
properties.setProperty(prefix+"debug_filters", this.debug_filters+"");
......@@ -2649,10 +2654,15 @@ public class CLTParameters {
if (properties.getProperty(prefix+"gmap_discard_rdisp")!=null) this.gmap_discard_rdisp=Double.parseDouble(properties.getProperty(prefix+ "gmap_discard_rdisp"));
if (properties.getProperty(prefix+"gmap_pix_size")!=null) this.gmap_pix_size=Double.parseDouble(properties.getProperty(prefix+ "gmap_pix_size"));
if (properties.getProperty(prefix+"gmap_max_image_width")!=null) this.gmap_max_image_width=Integer.parseInt(properties.getProperty(prefix+ "gmap_max_image_width"));
if (properties.getProperty(prefix+"gmap_min_sfm")!=null) this.gmap_min_sfm=Double.parseDouble(properties.getProperty(prefix+ "gmap_min_sfm"));
if (properties.getProperty(prefix+"gmap_min_sfm")!=null) this.gmap_min_sfm=Double.parseDouble(properties.getProperty(prefix+ "gmap_min_sfm"));
if (properties.getProperty(prefix+"gmap_crop_empty")!=null) this.gmap_crop_empty=Boolean.parseBoolean(properties.getProperty(prefix+ "gmap_crop_empty"));
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_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+"show_textures")!=null) this.show_textures=Boolean.parseBoolean(properties.getProperty(prefix+"show_textures"));
......@@ -3922,6 +3932,15 @@ public class CLTParameters {
"Additionally crop map by this number of pixels from each of the four directions.");
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'");
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.addCheckbox ("Generate background (infinity) image for 3D (not needed for view down)", this.generate_bg);
......@@ -5137,7 +5156,13 @@ public class CLTParameters {
this.gmap_min_sfm= gd.getNextNumber();
this.gmap_crop_empty= gd.getNextBoolean();
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.show_textures= gd.getNextBoolean();
this.debug_filters= gd.getNextBoolean();
......
......@@ -114,7 +114,7 @@ public class DumpImageMetadata
System.out.println(">");
}
private static void dumpMetadata(IIOMetadata metadata)
static void dumpMetadata(IIOMetadata metadata)
{
String[] names = metadata.getMetadataFormatNames();
int length = names.length;
......
......@@ -23,11 +23,18 @@
*/
package com.elphel.imagej.readers;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferFloat;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.awt.image.WritableRaster;
import java.awt.image.PixelInterleavedSampleModel;
import java.io.File;
import java.io.IOException;
import java.time.LocalDateTime;
......@@ -68,7 +75,7 @@ public class ElphelTiffWriter {
public static String TIFF_FIELD_DESCRIPTION_ATTRIBUTE = "description";
public static void saveTiffARGB32(
public static void saveTiffARGBOrGray32(
ImagePlus imp,
String path, // full path to save image
double [] lla, // latitude, longitude, altitude (or null)
......@@ -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
// tiffWriteParam.setCompressionType("Exif JPEG");
tiffWriteParam.setCompressionMode(ImageWriteParam.MODE_DISABLED);
/* ImagePlus image removes alpha, so we need to create a new ARGB BufferedImage
* and copy data (it is correct in imp.getImage() */
BufferedImage bufferedImage = (BufferedImage) imp.getImage(); // properties=null
BufferedImage bufferedImageAlpha = new BufferedImage(
bufferedImage.getWidth(),
bufferedImage.getHeight(),
BufferedImage.TYPE_INT_ARGB);
int [] ipixels = (int[])imp.getProcessor().getPixels();
final int[] bia_as_arr = ( (DataBufferInt) bufferedImageAlpha.getRaster().getDataBuffer() ).getData();
System.arraycopy(ipixels, 0, bia_as_arr, 0, bia_as_arr.length);
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
* and copy data (it is correct in imp.getImage() */
bufferedImageAlpha = new BufferedImage(
bufferedImage.getWidth(),
bufferedImage.getHeight(),
BufferedImage.TYPE_INT_ARGB);
int [] ipixels = (int[])imp.getProcessor().getPixels();
final int[] bia_as_arr = ( (DataBufferInt) bufferedImageAlpha.getRaster().getDataBuffer() ).getData();
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);
IIOMetadata imageMetadata = tiffWriter.getDefaultImageMetadata(imageType, tiffWriteParam);
if (debugLevel > 0) {System.out.println("\n=== imageMetadata ===:");DumpImageMetadata.dumpMetadata(imageMetadata);}
// just for testing:
int cm_dig_after = 9;
// Here is a way adding tag sets through TIFFDirectory
......@@ -166,6 +184,82 @@ public class ElphelTiffWriter {
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
int denom = 1;
for (int i = 0; i < digits_after; i++) denom *= 10;
......
......@@ -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 (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,
path,
lla, // double [] lla, // latitude, longitude, altitude (or null)
......@@ -330,39 +330,53 @@ the type of pixel data in this file getPixelType()
return;
} else if (imp.getType()==ImagePlus.GRAY32) {
int lwir_palette = 0;
double [] minmax = null;
String title = imp.getTitle();
float [] fpixels = (float[]) imp.getProcessor().getPixels();
double [] dpixels = new double [fpixels.length];
for (int i = 0; i < fpixels.length; i++) {
dpixels[i] = fpixels[i];
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;
double [] minmax = null;
String title = imp.getTitle();
float [] fpixels = (float[]) imp.getProcessor().getPixels();
double [] dpixels = new double [fpixels.length];
for (int i = 0; i < fpixels.length; i++) {
dpixels[i] = fpixels[i];
}
ImagePlus imp_rgb = QuadCLTCPU.linearStackToColorLWIR(
Eyesis_Correction.CLT_PARAMETERS, // clt_parameters, // CLTParameters clt_parameters,
lwir_palette, // int lwir_palette, // <0 - do not convert
minmax, // minmax, // double [] minmax,
title, // String name,
"-rgba", // String suffix, // such as disparity=...
true, // boolean toRGB,
new double[][] {dpixels}, // faded_textures[nslice], // double [][] texture_data,
imp.getWidth(), // int width, // int tilesX,
imp.getHeight(), // int height, // int tilesY,
debugLevel); // int debugLevel )
// File test_dump = new File("/home/elphel/Documents/DoD/Ukraine/photos/test/1694531940_120446-02-gps.tiff");
// 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?
ElphelTiffWriter.saveTiffARGBOrGray32(
imp_rgb,
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);
// saveTiffARGB32(imp_rgb, path, imageJTags, debugLevel);
}
ImagePlus imp_rgb = QuadCLTCPU.linearStackToColorLWIR(
Eyesis_Correction.CLT_PARAMETERS, // clt_parameters, // CLTParameters clt_parameters,
lwir_palette, // int lwir_palette, // <0 - do not convert
minmax, // minmax, // double [] minmax,
title, // String name,
"-rgba", // String suffix, // such as disparity=...
true, // boolean toRGB,
new double[][] {dpixels}, // faded_textures[nslice], // double [][] texture_data,
imp.getWidth(), // int width, // int tilesX,
imp.getHeight(), // int height, // int tilesY,
debugLevel); // int debugLevel )
// File test_dump = new File("/home/elphel/Documents/DoD/Ukraine/photos/test/1694531940_120446-02-gps.tiff");
// 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?
ElphelTiffWriter.saveTiffARGB32(
imp_rgb,
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);
// saveTiffARGB32(imp_rgb, path, imageJTags, debugLevel);
return;
}
IJ.showMessage("Not yet implemented for this image type");
......
......@@ -16445,7 +16445,7 @@ public class QuadCLTCPU {
height, // int height, // int tilesY,
debugLevel); // int debugLevel )
String image_path = model_dir + Prefs.getFileSeparator() + title+".tiff";
ElphelTiffWriter.saveTiffARGB32(
ElphelTiffWriter.saveTiffARGBOrGray32(
imp,
image_path,
lla, // double [] lla, // latitude, longitude, altitude (or null)
......
......@@ -2466,7 +2466,7 @@ public class TexturedModel {
final double discard_rdisp = clt_parameters.gmap_discard_rdisp ; //0.02; // discard above/below this fraction of average height
final double pix_size = clt_parameters.gmap_pix_size ; //0.005; // hdr_x0y0, // in meters
final int max_image_width = clt_parameters.gmap_max_image_width ; //4000; // 3200; // increase pixel size as a power of 2 until image fits
final double min_sfm_gain = clt_parameters.gmap_min_sfm ; //10.0 minimal SfM gain to keep ground map
final double min_sfm_gain = clt_parameters.gmap_min_sfm ; //10.0 minimal SfM gain to keep ground map
final boolean crop_empty = clt_parameters.gmap_crop_empty ; //true;
final int crop_extra = clt_parameters.gmap_crop_extra ; //20;
final int [] tex_pals = clt_parameters.gmap_tex_pals ; //{0,1,2};
......
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