Changeset 11897 in josm


Ignore:
Timestamp:
2017-04-13T15:14:20+02:00 (10 days ago)
Author:
bastiK
Message:

see #7427 - optimize warp transformaion

performance problems should be resolved now

Location:
trunk/src/org/openstreetmap/josm
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/imagery/ReprojectionTile.java

    r11894 r11897  
    103103        double scaleMapView = Main.map.mapView.getScale();
    104104        ImageWarp.Interpolation interpolation;
    105         switch (Main.pref.get("imagery.warp.interpolation", "bilinear")) {
     105        switch (Main.pref.get("imagery.warp.pixel-interpolation", "bilinear")) {
    106106            case "nearest_neighbor":
    107107                interpolation = ImageWarp.Interpolation.NEAREST_NEIGHBOR;
     
    148148                (pbTargetAligned.maxNorth - en11Current.north()) / scale);
    149149
     150        ImageWarp.PointTransform transform;
     151        int stride = Main.pref.getInteger("imagery.warp.projection-interpolation.stride", 7);
     152        if (stride > 0) {
     153            transform = new ImageWarp.GridTransform(pointTransform, stride);
     154        } else {
     155            transform = pointTransform;
     156        }
    150157        BufferedImage imageOut = ImageWarp.warp(
    151                 imageIn, getDimension(pbTargetAligned, scale), pointTransform,
    152                 interpolation);
     158                imageIn, getDimension(pbTargetAligned, scale),
     159                transform, interpolation);
    153160        synchronized (this) {
    154161            this.image = imageOut;
  • trunk/src/org/openstreetmap/josm/tools/ImageWarp.java

    r11896 r11897  
    66import java.awt.geom.Rectangle2D;
    77import java.awt.image.BufferedImage;
     8import java.util.HashMap;
     9import java.util.HashSet;
     10import java.util.Map;
     11import java.util.Set;
    812
    913/**
     
    2024    public interface PointTransform {
    2125        Point2D transform(Point2D pt);
     26    }
     27
     28    /**
     29     * Wrapper that optimizes a given {@link ImageWarp.PointTransform}.
     30     *
     31     * It does so by spanning a grid with certain step size. It will invoke the
     32     * potentially expensive master transform only at those grid points and use
     33     * bilinear interpolation to approximate transformed values in between.
     34     * <p>
     35     * For memory optimization, this class assumes that rows are more or less scanned
     36     * one-by-one as is done in {@link ImageWarp#warp}. I.e. this transform is <em>not</em>
     37     * random access in the y coordinate.
     38     */
     39    public static class GridTransform implements ImageWarp.PointTransform {
     40
     41        private final double stride;
     42        private final ImageWarp.PointTransform trfm;
     43
     44        private final Map<Integer, Map<Integer, Point2D>> cache;
     45
     46        private static final boolean CONSISTENCY_TEST = false;
     47        private final Set<Integer> deletedRows;
     48
     49        /**
     50         * Create a new GridTransform.
     51         * @param trfm the master transform, that needs to be optimized
     52         * @param stride step size
     53         */
     54        public GridTransform(ImageWarp.PointTransform trfm, double stride) {
     55            this.trfm = trfm;
     56            this.stride = stride;
     57            this.cache = new HashMap<>();
     58            if (CONSISTENCY_TEST) {
     59                deletedRows = new HashSet<>();
     60            } else {
     61                deletedRows = null;
     62            }
     63        }
     64
     65        @Override
     66        public Point2D transform(Point2D pt) {
     67            int xIdx = (int) Math.floor(pt.getX() / stride);
     68            int yIdx = (int) Math.floor(pt.getY() / stride);
     69            double dx = pt.getX() / stride - xIdx;
     70            double dy = pt.getY() / stride - yIdx;
     71            Point2D value00 = getValue(xIdx, yIdx);
     72            Point2D value01 = getValue(xIdx, yIdx + 1);
     73            Point2D value10 = getValue(xIdx + 1, yIdx);
     74            Point2D value11 = getValue(xIdx + 1, yIdx + 1);
     75            double valueX = (value00.getX() * (1-dx) + value10.getX() * dx) * (1-dy) +
     76                    (value01.getX() * (1-dx) + value11.getX() * dx) * dy;
     77            double valueY = (value00.getY() * (1-dx) + value10.getY() * dx) * (1-dy) +
     78                    (value01.getY() * (1-dx) + value11.getY() * dx) * dy;
     79            return new Point2D.Double(valueX, valueY);
     80        }
     81
     82        private Point2D getValue(int xIdx, int yIdx) {
     83            Map<Integer, Point2D> row = getRow(yIdx);
     84            Point2D val = row.get(xIdx);
     85            if (val == null) {
     86                val = trfm.transform(new Point2D.Double(xIdx * stride, yIdx * stride));
     87                row.put(xIdx, val);
     88            }
     89            return val;
     90        }
     91
     92        private Map<Integer, Point2D> getRow(int yIdx) {
     93            cleanUp(yIdx - 2);
     94            Map<Integer, Point2D> row = cache.get(yIdx);
     95            if (row == null) {
     96                row = new HashMap<>();
     97                cache.put(yIdx, row);
     98                if (CONSISTENCY_TEST) {
     99                    // should not create a row that has been deleted before
     100                    if (deletedRows.contains(yIdx)) throw new AssertionError();
     101                    // only ever cache 2 rows at once
     102                    if (cache.size() > 2) throw new AssertionError();
     103                }
     104            }
     105            return row;
     106        }
     107
     108        // remove rows from cache that will no longer be used
     109        private void cleanUp(int yIdx) {
     110            Map<Integer, Point2D> del = cache.remove(yIdx);
     111            if (CONSISTENCY_TEST && del != null) {
     112                // should delete each row only once
     113                if (deletedRows.contains(yIdx)) throw new AssertionError();
     114                deletedRows.add(yIdx);
     115            }
     116        }
    22117    }
    23118
Note: See TracChangeset for help on using the changeset viewer.