Ede,

Crop operation also uses floats, so I'm not sure it solves the problem. Difficult to guess the impact without a try though.

The change may not be trivial as the scale operation which is currently performed at first place is also used to cache a scaled image which can be re-used when scale does not change (pan).

Another track : during my test (commented code), I observed that displaying a very small part of the full image without scaling/cropping with jai first was efficient and precise (drawRenderedImage(RenderedImage,AffineTransform)). The problem is with the rendering of the full image. Then, graphics2D.drawRenderedImage is very slow (like the scaling operation with jai). The only way to interpolate the image efficiently when the full image has to be displayed seems to be the subsampling operation with jai. Mixing subsampling for certain scales and normal affine transform for others may do the trick, but it seems over complicated.

Another track to be explored is the capability to use subsampled overviews wrapped in geotiff images (as described in Gary's mail about COG geotiff format). May be after migration...

Michaël  

envoyé : 12 octobre 2020 à 21:29
de : edgar.sol...@web.de
à : OpenJump develop and use <jump-pilot-devel@lists.sourceforge.net>
Cc: jump-pilot-...@lists.sourceforge.net
objet : Re: [JPP-Devel] SVN: [6596] core/trunk/src/com/vividsolutions/jump/workbench/imagery/ geoimg/GeoImage.java


hey Mike,

how about cropping first and scaling only the part shown?.. ede

On October 12, 2020 9:19:38 PM GMT+02:00, jump-pilot-svn--- via Jump-pilot-devel <jump-pilot-devel@lists.sourceforge.net> wrote:
>Revision: 6596

http://sourceforge.net/p/jump-pilot/code/6596

>Author: michaudm>Date: 2020-10-12 19:19:38 +0000 (Mon, 12 Oct 2020)
>Log Message:
>-----------
>Keep double parameters as long as possible (does not solve the pb
>described in #507)

 

>Modified Paths:>--------------
>core/trunk/src/com/vividsolutions/jump/workbench/imagery/geoimg/GeoImage.java

 

>Modified:>core/trunk/src/com/vividsolutions/jump/workbench/imagery/geoimg/GeoImage.java
>===================================================================
>---
>core/trunk/src/com/vividsolutions/jump/workbench/imagery/geoimg/GeoImage.java 2020-10-12
>15:05:32 UTC (rev 6595)
>+++
>core/trunk/src/com/vividsolutions/jump/workbench/imagery/geoimg/GeoImage.java 2020-10-12
>19:19:38 UTC (rev 6596)
>@@ -37,6 +37,7 @@

import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;

>+import java.awt.geom.NoninvertibleTransformException;

import java.awt.image.BufferedImage;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.ParameterBlock;

>@@ -44,10 +45,15 @@

import javax.media.jai.JAI;
import javax.media.jai.RenderedOp;

>+import javax.media.jai.operator.AffineDescriptor;

 

>+import com.vividsolutions.jts.geom.Coordinate;

import com.vividsolutions.jts.geom.Envelope;

>+import com.vividsolutions.jts.geom.util.AffineTransformation;>+import com.vividsolutions.jts.geom.util.AffineTransformationBuilder;

import com.vividsolutions.jump.JUMPException;
import com.vividsolutions.jump.feature.Feature;

>+import com.vividsolutions.jump.util.Timer;

import com.vividsolutions.jump.workbench.imagery.ReferencedImage;

>import>com.vividsolutions.jump.workbench.imagery.ReferencedImageException;

import com.vividsolutions.jump.workbench.model.Disposable;

>@@ -57,7 +63,7 @@>public class GeoImage implements ReferencedImage, Disposable,
>AlphaSetting {

private GeoReferencedRaster gtr;
private int alpha = 255;

>- private float last_scale;>+ private double last_scale;

private RenderedOp last_scale_img;
private Envelope last_img_env;
private RenderedImage last_rendering;

>@@ -92,6 +98,7 @@>public synchronized void paint(Feature f, java.awt.Graphics2D g,
>Viewport viewport)

throws ReferencedImageException {

>+ //long t0 = Timer.now();

try {
// update image envelope, either use geometry's or image's
// this allows moving the image via geometry movement

>@@ -109,17 +116,17 @@

// + Integer.toHexString(img.hashCode()));

// get current scale

>- final float scale = (float) viewport.getScale();>+ final double scale = viewport.getScale();

// get current viewport area
Envelope envModel_viewport = viewport.getEnvelopeInModelCoordinates();

// if nothing changed, no reason to rerender the whole shebang
// this is mainly the case when OJ last and regained focus

>- if (last_scale == scale && last_img_env instanceof Envelope>- && last_img_env.equals(envImage) && last_vwp_env instanceof
>Envelope
>+ if (last_scale == scale && last_img_env != null
>+ && last_img_env.equals(envImage) && last_vwp_env != null

&& last_vwp_env.equals(envModel_viewport)

>- && last_rendering instanceof RenderedImage>- && last_transform instanceof AffineTransform) {
>+ && last_rendering != null
>+ && last_transform != null) {

draw(g, null);
return;
}

>@@ -132,7 +139,7 @@

// reuse a cached version if scale and img_envelope didn't changed
// speeds up panning, window resizing
if (last_scale == scale && last_scale_img != null

>- && last_img_env instanceof Envelope>+ && last_img_env != null

&& last_img_env.equals(envImage)) {
img = last_scale_img;
// System.out.println("GI: USE SCALE CACHE");

>@@ -140,8 +147,8 @@

// System.out.println("GI: NO SCALE CACHE");

// First, scale the original image

>- float scaleX = scale * (float)>gtr.getDblModelUnitsPerRasterUnit_X();
>- float scaleY = scale * (float)
>gtr.getDblModelUnitsPerRasterUnit_Y();
>+ double scaleX = scale *
>gtr.getDblModelUnitsPerRasterUnit_X();
>+ double scaleY = scale *
>gtr.getDblModelUnitsPerRasterUnit_Y();

// calculate predicted dimensions
double scaledW = scaleX * src_img.getWidth();

>@@ -157,7 +164,7 @@

// we cache an overview here for big pictures
// speeds up situations when the whole picture is shown

>- float scaleX_toUse, scaleY_toUse;>+ double scaleX_toUse, scaleY_toUse;

RenderedImage scale_src_img;

>if ((imgW > 2000 || imgH > 2000) && scaledW < 2000 && scaledH < 2000 )>{

// System.out.println("GI: USE FULL SCALE CACHE");

>@@ -165,9 +172,9 @@>// this is faster than having JAI create it from scratch from big
>datasets

if (full_scale_img == null) {
if (imgW > imgH) {

>- full_scale = 1 / (imgW / 2000d);>+ full_scale = 2000d / imgW;

} else {

>- full_scale = 1 / (imgH / 2000d);>+ full_scale = 2000d / imgH;

}
// subsample average gives a smoothly resized image
pb = new ParameterBlock();

>@@ -178,14 +185,14 @@

// System.out.println("GI full scale img: "
// + full_scale_img.getWidth());
}

>- scaleX_toUse = (float) scaleX / (float) full_scale;>- scaleY_toUse = (float) scaleY / (float) full_scale;
>+ scaleX_toUse = scaleX / full_scale;
>+ scaleY_toUse = scaleY / full_scale;

scale_src_img = full_scale_img;
}
// scale the original
else{

>- scaleX_toUse = (float) scaleX;>- scaleY_toUse = (float) scaleY;
>+ scaleX_toUse = scaleX;
>+ scaleY_toUse = scaleY;

scale_src_img = src_img;
}

>@@ -195,8 +202,8 @@

// so use slow and qualitative inferior bicubic instead
// or NOT, to f**g slow, use default interpolation
if (scaleX > 0.1 || scaleY > 0.1) {

>- pb.add(scaleX_toUse);>- pb.add(scaleY_toUse);
>+ pb.add((float)scaleX_toUse);
>+ pb.add((float)scaleY_toUse);

pb.add(0f);
pb.add(0f);
// Interpolation interp = Interpolation

>@@ -204,8 +211,8 @@

// pb.add(interp); // add interpolation method
img = JAI.create("scale", pb, hints);
} else {

>- pb.add((double) (scaleX_toUse));>- pb.add((double) (scaleY_toUse));
>+ pb.add(scaleX_toUse);
>+ pb.add(scaleY_toUse);

img = JAI.create("subsampleaverage", pb, hints);
}

>@@ -244,13 +251,13 @@>double ratio_cropW = envModel_viewport.getWidth() /
>envImage.getWidth();
>double ratio_cropH = envModel_viewport.getHeight() /
>envImage.getHeight();

 

>- float raster_cropX = (int) (ratio_cropX * img.getWidth());>- float raster_cropY = (int) (ratio_cropY * img.getHeight());
>- float raster_cropW = (int) (ratio_cropW * img.getWidth());
>- float raster_cropH = (int) (ratio_cropH * img.getHeight());
>+ double raster_cropX = ratio_cropX * img.getWidth();
>+ double raster_cropY = ratio_cropY * img.getHeight();
>+ double raster_cropW = ratio_cropW * img.getWidth();
>+ double raster_cropH = ratio_cropH * img.getHeight();

 

>- float raster_offsetX = 0;>- float raster_offsetY = 0;
>+ double raster_offsetX = 0;
>+ double raster_offsetY = 0;

if (raster_cropX < 0) {
raster_offsetX = -raster_cropX;

>@@ -260,24 +267,30 @@

raster_offsetY = -raster_cropY;
raster_cropY = 0;
}

>+ raster_cropX = Math.min((float)raster_cropX,>(float)img.getWidth());
>+ raster_cropY = Math.min((float)raster_cropY,
>(float)img.getHeight());

raster_cropW = Math

>- .min(raster_cropW, img.getWidth() - (int) raster_cropX);>- raster_cropH = Math.min(raster_cropH, img.getHeight()
>- - (int) raster_cropY);
>+ .min((float)raster_cropW, (float)img.getWidth() - /*(int)*/
>raster_cropX);
>+ raster_cropH = Math.min((float)raster_cropH,
>(float)img.getHeight()
>+ - /*(int)*/ raster_cropY);

pb = new ParameterBlock();
pb.addSource(img);

>- pb.add(raster_cropX);>- pb.add(raster_cropY);
>- pb.add(raster_cropW);
>- pb.add(raster_cropH);
>+ //System.out.println("cropx " + (float)raster_cropX);
>+ //System.out.println("cropy " + (float)raster_cropY);
>+ //System.out.println("cropw " + (float)raster_cropW + " " +
>(img.getWidth() - /*(int)*/ raster_cropX));
>+ //System.out.println("croph " + (float)raster_cropH + " " +
>(img.getHeight() - /*(int)*/ raster_cropY));
>+ pb.add((float)raster_cropX);
>+ pb.add((float)raster_cropY);
>+ pb.add((float)raster_cropW);
>+ pb.add((float)raster_cropH);

img = JAI.create("crop", pb, null);

// move the image to the model coordinates
pb = new ParameterBlock();
pb.addSource(img);

>- pb.add(raster_offsetX - img.getMinX());>- pb.add(raster_offsetY - img.getMinY());
>+ pb.add((float)(raster_offsetX - img.getMinX()));
>+ pb.add((float)(raster_offsetY - img.getMinY()));

img = JAI.create("translate", pb, null);

// cache the current rendering here as used in the

>@@ -288,6 +301,7 @@

// eventually draw the image, let g render the chain
draw(g, img);

>+ //System.out.printf("Display at %f in %d ms%n", scale,>Timer.milliSecondsSince(t0));

} catch (Exception ex) {
throw new ReferencedImageException(ex);

>@@ -294,6 +308,77 @@

}
}

>+ // [mmichaud 2020-10-12] try to make image display more precise>using double-based
>+ // affine transform, but the code does not take advanatage of jai
>subsample used in
>+ // the previous code and is much slower
>+ //RenderedImage cached2000px = null;
>+ //public synchronized void paint(Feature f, java.awt.Graphics2D g,
>Viewport viewport)
>+ // throws ReferencedImageException {
>+ // long t0 = Timer.now();
>+ // long t1 = 0L;
>+ // // Image enveloppe in model coordinates
>+ // Envelope imageEnv = gtr.getEnvelope(f);
>+ // RenderedOp op = gtr.getRenderedOp();
>+ // RenderedImage im = op;
>+ // RenderingHints hints = gtr.createCacheRenderingHints();
>+ //
>+ // // Size of an image pixel in the Model (ground coordinates)
>+ // double pixelSizeInModelX = imageEnv.getWidth()/op.getWidth();
>+ // double pixelSizeInModelY = imageEnv.getHeight()/op.getHeight();
>+ // double scale = viewport.getScale();
>+ // // Size of an image pixel in the screen model (number of screen
>pixel for one image pixel)
>+ // double pixelSizeInViewX = pixelSizeInModelX * scale;
>+ // double pixelSizeInViewY = pixelSizeInModelY * scale;
>+ // // Short circuit : if the full image is < 1 screen pixel, return
>+ // if (pixelSizeInViewX*im.getWidth() < 0.5 &&
>pixelSizeInViewY*im.getHeight() < 0.5) {
>+ // System.out.println("Full image < 1 px");
>+ // return;
>+ // }
>+ //
>+ // if ((im.getWidth() > 2000 || im.getHeight() > 2000) &&
>pixelSizeInViewX < 1 && pixelSizeInViewY < 1) {
>+ // if (cached2000px == null) {
>+ // full_scale = 2000d / Math.max(im.getWidth(),
>im.getHeight());
>+ // ParameterBlock pb = new ParameterBlock();
>+ // pb.addSource(im);
>+ // pb.add(full_scale); // x scale factor
>+ // pb.add(full_scale); // y scale factor
>+ // cached2000px = JAI.create("subsampleaverage", pb, null);
>+ // }
>+ // im = cached2000px;
>+ // }
>+ //
>+ // System.out.println("Image " + im.getWidth() + " x " +
>im.getHeight());
>+ //
>+ // try {
>+ // AffineTransform model2view =
>viewport.getModelToViewTransform();
>+ // AffineTransformation imageToModel = new
>AffineTransformationBuilder(
>+ // new Coordinate(0.0, 0.0),
>+ // new Coordinate(im.getWidth(), 0.0),
>+ // new Coordinate(0.0, im.getHeight()),
>+ // new Coordinate(imageEnv.getMinX(),
>imageEnv.getMaxY()),
>+ // new Coordinate(imageEnv.getMaxX(),
>imageEnv.getMaxY()),
>+ // new Coordinate(imageEnv.getMinX(), imageEnv.getMinY())
>+ // ).getTransformation();
>+ //
>+ // AffineTransform image2view = new AffineTransform(
>+ // imageToModel.getMatrixEntries()[0],
>+ // imageToModel.getMatrixEntries()[3],
>+ // imageToModel.getMatrixEntries()[1],
>+ // imageToModel.getMatrixEntries()[4],
>+ // imageToModel.getMatrixEntries()[2],
>+ // imageToModel.getMatrixEntries()[5]
>+ // );
>+ //
>+ // image2view.preConcatenate(model2view);
>+ // im = AffineDescriptor.create(im,image2view, null,null, hints);
>+ // g.drawRenderedImage(im, new AffineTransform());
>+ //
>+ // System.out.printf("Display at %f (jai %d) (%dx%d) in %d ms%n",
>scale, t1, im.getWidth(), im.getHeight(), Timer.milliSecondsSince(t0));
>+ // } catch (NoninvertibleTransformException ex) {
>+ // throw new ReferencedImageException(ex);
>+ // }
>+ //}
>+

private void draw(Graphics2D g, RenderedImage img) {
Composite composite = g.getComposite();
// setup transparency

>@@ -302,7 +387,7 @@

// The image has been translated and scaled by JAI
// already. Just draw it with an identity transformation.
AffineTransform aft;

>- if (img instanceof RenderedImage){>+ if (img != null) {

aft = new AffineTransform();
}
// no img given? paint cached last rendering again

>_______________________________________________>Jump-pilot-devel mailing list
>Jump-pilot-devel@lists.sourceforge.net
>https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel


_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

_______________________________________________
Jump-pilot-devel mailing list
Jump-pilot-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/jump-pilot-devel

Reply via email to