source: josm/trunk/src/org/openstreetmap/josm/tools/ImageWarp.java@ 12131

Last change on this file since 12131 was 11972, checked in by bastiK, 7 years ago

see #7427 - remove obsolete code

  • Property svn:eol-style set to native
File size: 7.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.tools;
3
4import java.awt.Dimension;
5import java.awt.geom.Point2D;
6import java.awt.geom.Rectangle2D;
7import java.awt.image.BufferedImage;
8import java.util.HashMap;
9import java.util.HashSet;
10import java.util.Map;
11import java.util.Set;
12
13import org.openstreetmap.josm.Main;
14
15/**
16 * Image warping algorithm.
17 *
18 * Deforms an image geometrically according to a given transformation formula.
19 * @since 11858
20 */
21public class ImageWarp {
22
23 /**
24 * Transformation that translates the pixel coordinates.
25 */
26 public interface PointTransform {
27 /**
28 * Translates pixel coordinates.
29 * @param pt pixel coordinates
30 * @return transformed pixel coordinates
31 */
32 Point2D transform(Point2D pt);
33 }
34
35 /**
36 * Wrapper that optimizes a given {@link ImageWarp.PointTransform}.
37 *
38 * It does so by spanning a grid with certain step size. It will invoke the
39 * potentially expensive master transform only at those grid points and use
40 * bilinear interpolation to approximate transformed values in between.
41 * <p>
42 * For memory optimization, this class assumes that rows are more or less scanned
43 * one-by-one as is done in {@link ImageWarp#warp}. I.e. this transform is <em>not</em>
44 * random access in the y coordinate.
45 */
46 public static class GridTransform implements ImageWarp.PointTransform {
47
48 private final double stride;
49 private final ImageWarp.PointTransform trfm;
50
51 private final Map<Integer, Map<Integer, Point2D>> cache;
52
53 private final boolean consistencyTest;
54 private final Set<Integer> deletedRows;
55
56 /**
57 * Create a new GridTransform.
58 * @param trfm the master transform, that needs to be optimized
59 * @param stride step size
60 */
61 public GridTransform(ImageWarp.PointTransform trfm, double stride) {
62 this.trfm = trfm;
63 this.stride = stride;
64 this.cache = new HashMap<>();
65 this.consistencyTest = Main.isDebugEnabled();
66 if (consistencyTest) {
67 deletedRows = new HashSet<>();
68 } else {
69 deletedRows = null;
70 }
71 }
72
73 @Override
74 public Point2D transform(Point2D pt) {
75 int xIdx = (int) Math.floor(pt.getX() / stride);
76 int yIdx = (int) Math.floor(pt.getY() / stride);
77 double dx = pt.getX() / stride - xIdx;
78 double dy = pt.getY() / stride - yIdx;
79 Point2D value00 = getValue(xIdx, yIdx);
80 Point2D value01 = getValue(xIdx, yIdx + 1);
81 Point2D value10 = getValue(xIdx + 1, yIdx);
82 Point2D value11 = getValue(xIdx + 1, yIdx + 1);
83 double valueX = (value00.getX() * (1-dx) + value10.getX() * dx) * (1-dy) +
84 (value01.getX() * (1-dx) + value11.getX() * dx) * dy;
85 double valueY = (value00.getY() * (1-dx) + value10.getY() * dx) * (1-dy) +
86 (value01.getY() * (1-dx) + value11.getY() * dx) * dy;
87 return new Point2D.Double(valueX, valueY);
88 }
89
90 private Point2D getValue(int xIdx, int yIdx) {
91 Map<Integer, Point2D> row = getRow(yIdx);
92 Point2D val = row.get(xIdx);
93 if (val == null) {
94 val = trfm.transform(new Point2D.Double(xIdx * stride, yIdx * stride));
95 row.put(xIdx, val);
96 }
97 return val;
98 }
99
100 private Map<Integer, Point2D> getRow(int yIdx) {
101 cleanUp(yIdx - 3);
102 Map<Integer, Point2D> row = cache.get(yIdx);
103 if (row == null) {
104 row = new HashMap<>();
105 cache.put(yIdx, row);
106 if (consistencyTest) {
107 // should not create a row that has been deleted before
108 if (deletedRows.contains(yIdx)) throw new AssertionError();
109 // only ever cache 3 rows at once
110 if (cache.size() > 3) throw new AssertionError();
111 }
112 }
113 return row;
114 }
115
116 // remove rows from cache that will no longer be used
117 private void cleanUp(int yIdx) {
118 Map<Integer, Point2D> del = cache.remove(yIdx);
119 if (consistencyTest && del != null) {
120 // should delete each row only once
121 if (deletedRows.contains(yIdx)) throw new AssertionError();
122 deletedRows.add(yIdx);
123 }
124 }
125 }
126
127 /**
128 * Interpolation method.
129 */
130 public enum Interpolation {
131 /**
132 * Nearest neighbor.
133 *
134 * Simplest possible method. Faster, but not very good quality.
135 */
136 NEAREST_NEIGHBOR,
137
138 /**
139 * Bilinear.
140 *
141 * Decent quality.
142 */
143 BILINEAR;
144 }
145
146 /**
147 * Warp an image.
148 * @param srcImg the original image
149 * @param targetDim dimension of the target image
150 * @param invTransform inverse transformation (translates pixel coordinates
151 * of the target image to pixel coordinates of the original image)
152 * @param interpolation the interpolation method
153 * @return the warped image
154 */
155 public static BufferedImage warp(BufferedImage srcImg, Dimension targetDim, PointTransform invTransform, Interpolation interpolation) {
156 BufferedImage imgTarget = new BufferedImage(targetDim.width, targetDim.height, BufferedImage.TYPE_INT_ARGB);
157 Rectangle2D srcRect = new Rectangle2D.Double(0, 0, srcImg.getWidth(), srcImg.getHeight());
158 for (int j = 0; j < imgTarget.getHeight(); j++) {
159 for (int i = 0; i < imgTarget.getWidth(); i++) {
160 Point2D srcCoord = invTransform.transform(new Point2D.Double(i, j));
161 if (srcRect.contains(srcCoord)) {
162 int rgba;
163 switch (interpolation) {
164 case NEAREST_NEIGHBOR:
165 rgba = getColor((int) Math.round(srcCoord.getX()), (int) Math.round(srcCoord.getY()), srcImg);
166 break;
167 case BILINEAR:
168 int x0 = (int) Math.floor(srcCoord.getX());
169 double dx = srcCoord.getX() - x0;
170 int y0 = (int) Math.floor(srcCoord.getY());
171 double dy = srcCoord.getY() - y0;
172 int c00 = getColor(x0, y0, srcImg);
173 int c01 = getColor(x0, y0 + 1, srcImg);
174 int c10 = getColor(x0 + 1, y0, srcImg);
175 int c11 = getColor(x0 + 1, y0 + 1, srcImg);
176 rgba = 0;
177 // loop over color components: blue, green, red, alpha
178 for (int ch = 0; ch <= 3; ch++) {
179 int shift = 8 * ch;
180 int chVal = (int) Math.round(
181 (((c00 >> shift) & 0xff) * (1-dx) + ((c10 >> shift) & 0xff) * dx) * (1-dy) +
182 (((c01 >> shift) & 0xff) * (1-dx) + ((c11 >> shift) & 0xff) * dx) * dy);
183 rgba |= chVal << shift;
184 }
185 break;
186 default:
187 throw new AssertionError();
188 }
189 imgTarget.setRGB(i, j, rgba);
190 }
191 }
192 }
193 return imgTarget;
194 }
195
196 private static int getColor(int x, int y, BufferedImage img) {
197 // border strategy: continue with the color of the outermost pixel,
198 return img.getRGB(
199 Utils.clamp(x, 0, img.getWidth() - 1),
200 Utils.clamp(y, 0, img.getHeight() - 1));
201 }
202}
Note: See TracBrowser for help on using the repository browser.