root / org.gvsig.toolbox / trunk / org.gvsig.toolbox / org.gvsig.toolbox.algorithm / src / main / java / es / unex / sextante / imageAnalysis / vectorizeTrees / VectorizeTreesAlgorithm.java @ 59
History | View | Annotate | Download (17 KB)
1 |
package es.unex.sextante.imageAnalysis.vectorizeTrees; |
---|---|
2 |
|
3 |
import java.awt.Point; |
4 |
import java.awt.geom.Point2D; |
5 |
import java.awt.geom.Rectangle2D; |
6 |
import java.util.ArrayList; |
7 |
import java.util.Arrays; |
8 |
|
9 |
import com.vividsolutions.jts.geom.Coordinate; |
10 |
import com.vividsolutions.jts.geom.Envelope; |
11 |
import com.vividsolutions.jts.geom.Geometry; |
12 |
import com.vividsolutions.jts.geom.GeometryFactory; |
13 |
|
14 |
import es.unex.sextante.additionalInfo.AdditionalInfoMultipleInput; |
15 |
import es.unex.sextante.additionalInfo.AdditionalInfoNumericalValue; |
16 |
import es.unex.sextante.additionalInfo.AdditionalInfoVectorLayer; |
17 |
import es.unex.sextante.core.AnalysisExtent; |
18 |
import es.unex.sextante.core.GeoAlgorithm; |
19 |
import es.unex.sextante.core.Sextante; |
20 |
import es.unex.sextante.dataObjects.IFeature; |
21 |
import es.unex.sextante.dataObjects.IFeatureIterator; |
22 |
import es.unex.sextante.dataObjects.IRasterLayer; |
23 |
import es.unex.sextante.dataObjects.IVectorLayer; |
24 |
import es.unex.sextante.exceptions.GeoAlgorithmExecutionException; |
25 |
import es.unex.sextante.exceptions.IteratorException; |
26 |
import es.unex.sextante.exceptions.RepeatedParameterNameException; |
27 |
import es.unex.sextante.math.simpleStats.SimpleStats; |
28 |
import es.unex.sextante.outputs.OutputVectorLayer; |
29 |
import es.unex.sextante.parameters.RasterLayerAndBand; |
30 |
import es.unex.sextante.rasterWrappers.GridCell; |
31 |
|
32 |
public class VectorizeTreesAlgorithm |
33 |
extends
|
34 |
GeoAlgorithm { |
35 |
|
36 |
private final static int m_iOffsetX[] = { 0, 1, 1, 1, 0, -1, -1, -1 }; |
37 |
private final static int m_iOffsetY[] = { 1, 1, 0, -1, -1, -1, 0, 1 }; |
38 |
|
39 |
public static final String INPUT = "INPUT"; |
40 |
public static final String POLYGONS = "POLYGONS"; |
41 |
public static final String TOLERANCE_SPECTRAL = "TOLERANCE_SPECTRAL"; |
42 |
public static final String TOLERANCE_SIZE = "TOLERANCE_SIZE"; |
43 |
public static final String MINSIZE = "MINSIZE"; |
44 |
public static final String MAXSIZE = "MAXSIZE"; |
45 |
public static final String TREES = "TREES"; |
46 |
|
47 |
private static final int ANALIZED = 2; |
48 |
|
49 |
private int m_iMinX, m_iMinY; |
50 |
private IRasterLayer[] m_Window; |
51 |
private IRasterLayer m_BinaryImage;
|
52 |
private ArrayList m_Bands; |
53 |
private IVectorLayer m_Polygons;
|
54 |
private ArrayList m_Stats; |
55 |
private SimpleStats m_SizeStats;
|
56 |
private IVectorLayer m_Trees;
|
57 |
private ArrayList m_TreesArray; |
58 |
private double m_dTolerance; |
59 |
private double m_dToleranceSize; |
60 |
private double m_dMinSize; |
61 |
private double m_dMaxSize; |
62 |
private GeometryFactory m_GeometryFactory;
|
63 |
private int m_iBand[]; |
64 |
|
65 |
|
66 |
@Override
|
67 |
public void defineCharacteristics() { |
68 |
|
69 |
setName(Sextante.getText("Detect_and_vectorize_individual_trees"));
|
70 |
setGroup(Sextante.getText("Image_processing"));
|
71 |
setUserCanDefineAnalysisExtent(true);
|
72 |
|
73 |
try {
|
74 |
m_Parameters.addMultipleInput(INPUT, Sextante.getText("Bands"), AdditionalInfoMultipleInput.DATA_TYPE_BAND, true); |
75 |
m_Parameters.addInputVectorLayer(POLYGONS, Sextante.getText("Sample_trees"),
|
76 |
AdditionalInfoVectorLayer.SHAPE_TYPE_POLYGON, true);
|
77 |
m_Parameters.addNumericalValue(TOLERANCE_SPECTRAL, Sextante.getText("Tolerance"),
|
78 |
AdditionalInfoNumericalValue.NUMERICAL_VALUE_DOUBLE, 1, 0, Double.MAX_VALUE); |
79 |
m_Parameters.addNumericalValue(TOLERANCE_SIZE, Sextante.getText("Tolerance_[size]"),
|
80 |
AdditionalInfoNumericalValue.NUMERICAL_VALUE_DOUBLE, 1, 0, Double.MAX_VALUE); |
81 |
// m_Parameters.addNumericalValue(MINSIZE,
|
82 |
// Sextante.getText( "Tamano_min"),
|
83 |
// AdditionalInfoNumericalValue.NUMERICAL_VALUE_DOUBLE,
|
84 |
// 0,0, Double.MAX_VALUE);
|
85 |
// m_Parameters.addNumericalValue(MAXSIZE,
|
86 |
// Sextante.getText( "Tamano_max"),
|
87 |
// AdditionalInfoNumericalValue.NUMERICAL_VALUE_DOUBLE,
|
88 |
// 100,0, Double.MAX_VALUE);
|
89 |
addOutputVectorLayer(TREES, Sextante.getText("Trees"), OutputVectorLayer.SHAPE_TYPE_POINT);
|
90 |
} |
91 |
catch (final RepeatedParameterNameException e) { |
92 |
Sextante.addErrorToLog(e); |
93 |
} |
94 |
|
95 |
} |
96 |
|
97 |
|
98 |
@Override
|
99 |
public boolean processAlgorithm() throws GeoAlgorithmExecutionException { |
100 |
|
101 |
int i;
|
102 |
|
103 |
m_GeometryFactory = new GeometryFactory();
|
104 |
|
105 |
m_Bands = m_Parameters.getParameterValueAsArrayList(INPUT); |
106 |
m_Polygons = m_Parameters.getParameterValueAsVectorLayer(POLYGONS); |
107 |
m_dTolerance = m_Parameters.getParameterValueAsDouble(TOLERANCE_SPECTRAL); |
108 |
m_dToleranceSize = m_Parameters.getParameterValueAsDouble(TOLERANCE_SIZE); |
109 |
// m_dMinSize = m_Parameters.getParameterValueAsDouble(MINSIZE);
|
110 |
// m_dMaxSize = m_Parameters.getParameterValueAsDouble(MAXSIZE);
|
111 |
|
112 |
m_TreesArray = new ArrayList(); |
113 |
|
114 |
m_Trees = getNewVectorLayer(TREES, Sextante.getText("Trees"), IVectorLayer.SHAPE_TYPE_POINT, new Class[] { Integer.class, |
115 |
Double.class, Double.class }, |
116 |
new String[] { "ID", Sextante.getText("Superficie"), Sextante.getText("coef_forma") }); |
117 |
|
118 |
m_SizeStats = new SimpleStats();
|
119 |
|
120 |
getClassInformation(); |
121 |
|
122 |
m_BinaryImage = this.getTempRasterLayer(IRasterLayer.RASTER_DATA_TYPE_BYTE, m_AnalysisExtent, 1); |
123 |
m_BinaryImage.setNoDataValue(0.);
|
124 |
|
125 |
for (i = 0; i < m_Window.length; i++) { |
126 |
m_Window[i].setWindowExtent(m_AnalysisExtent); |
127 |
} |
128 |
|
129 |
doParalellpiped(); |
130 |
detectAndVectorizeTrees(); |
131 |
|
132 |
//addOutputObject("out", "out", null, m_BinaryImage);
|
133 |
|
134 |
return !m_Task.isCanceled();
|
135 |
|
136 |
|
137 |
} |
138 |
|
139 |
|
140 |
private void detectAndVectorizeTrees() { |
141 |
|
142 |
int x, y;
|
143 |
int iNX, iNY;
|
144 |
double dValue;
|
145 |
|
146 |
iNX = m_BinaryImage.getWindowGridExtent().getNX(); |
147 |
iNY = m_BinaryImage.getWindowGridExtent().getNY(); |
148 |
|
149 |
setProgressText(Sextante.getText("Vectorizing"));
|
150 |
for (y = 0; (y < iNY) && setProgress(y, iNY); y++) { |
151 |
for (x = 0; x < iNX; x++) { |
152 |
dValue = m_BinaryImage.getCellValueAsDouble(x, y); |
153 |
if (!m_BinaryImage.isNoDataValue(dValue) && (dValue != ANALIZED)) {
|
154 |
detectTree(x, y); |
155 |
} |
156 |
} |
157 |
} |
158 |
|
159 |
vectorize(); |
160 |
|
161 |
} |
162 |
|
163 |
|
164 |
private void detectTree(int x, |
165 |
int y) {
|
166 |
|
167 |
int x2, y2;
|
168 |
int iPt;
|
169 |
int n;
|
170 |
int iCells = 0; |
171 |
int xMax = Integer.MIN_VALUE; |
172 |
int yMax = Integer.MIN_VALUE; |
173 |
int xMin = Integer.MAX_VALUE; |
174 |
int yMin = Integer.MAX_VALUE; |
175 |
double xCenter, yCenter;
|
176 |
double dValue;
|
177 |
ArrayList centralPoints = new ArrayList(); |
178 |
ArrayList adjPoints = new ArrayList(); |
179 |
Point point;
|
180 |
|
181 |
xMin = Math.min(xMin, x);
|
182 |
yMin = Math.min(yMin, y);
|
183 |
xMax = Math.max(xMax, x);
|
184 |
yMax = Math.max(yMax, y);
|
185 |
iCells++; |
186 |
|
187 |
centralPoints.add(new Point(x, y)); |
188 |
//m_BinaryImage.setNoData(x,y);
|
189 |
m_BinaryImage.setCellValue(x, y, ANALIZED); |
190 |
while (centralPoints.size() != 0) { |
191 |
for (iPt = 0; iPt < centralPoints.size(); iPt++) { |
192 |
point = (Point) centralPoints.get(iPt);
|
193 |
x = point.x; |
194 |
y = point.y; |
195 |
for (n = 0; n < 8; n++) { |
196 |
x2 = x + m_iOffsetX[n]; |
197 |
y2 = y + m_iOffsetY[n]; |
198 |
dValue = m_BinaryImage.getCellValueAsDouble(x2, y2); |
199 |
if (!m_BinaryImage.isNoDataValue(dValue) && (dValue != ANALIZED)) {
|
200 |
//m_BinaryImage.setNoData(x2,y2);
|
201 |
m_BinaryImage.setCellValue(x2, y2, ANALIZED); |
202 |
adjPoints.add(new Point(x2, y2)); |
203 |
xMin = Math.min(xMin, x2);
|
204 |
yMin = Math.min(yMin, y2);
|
205 |
xMax = Math.max(xMax, x2);
|
206 |
yMax = Math.max(yMax, y2);
|
207 |
iCells++; |
208 |
} |
209 |
} |
210 |
} |
211 |
|
212 |
centralPoints = adjPoints; |
213 |
adjPoints = new ArrayList(); |
214 |
|
215 |
if (m_Task.isCanceled()) {
|
216 |
return;
|
217 |
} |
218 |
|
219 |
} |
220 |
|
221 |
if (Math.abs(iCells - m_SizeStats.getMean()) < m_SizeStats.getStdDev() * m_dToleranceSize) { |
222 |
final Tree tree = new Tree(); |
223 |
final GridCell cellMin = new GridCell(xMin, yMin, 0); |
224 |
final GridCell cellMax = new GridCell(xMax, yMax, 0); |
225 |
final Point2D ptMin = m_AnalysisExtent.getWorldCoordsFromGridCoords(cellMin); |
226 |
final Point2D ptMax = m_AnalysisExtent.getWorldCoordsFromGridCoords(cellMax); |
227 |
xCenter = (ptMax.getX() + ptMin.getX()) / 2.;
|
228 |
yCenter = (ptMax.getY() + ptMin.getY()) / 2.;
|
229 |
|
230 |
tree.center = new Point2D.Double(xCenter, yCenter); |
231 |
tree.dArea = iCells; |
232 |
tree.dPerimeter = ((xMax - xMin + 1) * (yMax - yMin + 1)); |
233 |
m_TreesArray.add(tree); |
234 |
} |
235 |
|
236 |
} |
237 |
|
238 |
|
239 |
private void vectorize() { |
240 |
|
241 |
int iTree = 1; |
242 |
Tree tree; |
243 |
|
244 |
for (int i = 0; i < m_TreesArray.size(); i++) { |
245 |
tree = (Tree) m_TreesArray.get(i); |
246 |
final Object[] value = new Object[3]; |
247 |
value[0] = new Integer(iTree); |
248 |
value[1] = new Double(tree.dArea); |
249 |
value[2] = new Double(tree.getAreaPerimeterRatio()); |
250 |
final com.vividsolutions.jts.geom.Point pt = m_GeometryFactory.createPoint(new Coordinate(tree.center.getX(), |
251 |
tree.center.getY())); |
252 |
m_Trees.addFeature(pt, value); |
253 |
iTree++; |
254 |
} |
255 |
|
256 |
} |
257 |
|
258 |
|
259 |
private void getClassInformation() { |
260 |
|
261 |
int i;
|
262 |
final RasterLayerAndBand band;
|
263 |
m_Stats = new ArrayList(); |
264 |
|
265 |
m_Window = new IRasterLayer[m_Bands.size()];
|
266 |
m_iBand = new int[m_Bands.size()]; |
267 |
for (i = 0; i < m_Bands.size(); i++) { |
268 |
final RasterLayerAndBand rab = (RasterLayerAndBand) m_Bands.get(i);
|
269 |
m_iBand[i] = rab.getBand(); |
270 |
m_Window[i] = rab.getRasterLayer(); |
271 |
final AnalysisExtent extent = getAdjustedGridExtent(i);
|
272 |
m_Stats.add(new SimpleStats());
|
273 |
m_Window[i].setWindowExtent(extent); |
274 |
} |
275 |
|
276 |
setProgressText(Sextante.getText("Calculating_spectral_signatures"));
|
277 |
i = 0;
|
278 |
final int iShapeCount = m_Polygons.getShapesCount(); |
279 |
final IFeatureIterator featureIter = m_Polygons.iterator();
|
280 |
while (featureIter.hasNext() && setProgress(i, iShapeCount)) {
|
281 |
IFeature feature; |
282 |
try {
|
283 |
feature = featureIter.next(); |
284 |
final Geometry geometry = feature.getGeometry();
|
285 |
doPolygon(geometry); |
286 |
} |
287 |
catch (final IteratorException e) { |
288 |
//we ignore wrong features
|
289 |
} |
290 |
|
291 |
} |
292 |
featureIter.close(); |
293 |
|
294 |
} |
295 |
|
296 |
|
297 |
private void doPolygon(final Geometry geom) { |
298 |
|
299 |
for (int i = 0; i < geom.getNumGeometries(); i++) { |
300 |
final Geometry part = geom.getGeometryN(i);
|
301 |
doPolygonPart(part); |
302 |
} |
303 |
|
304 |
} |
305 |
|
306 |
|
307 |
private void doPolygonPart(final Geometry geom) { |
308 |
|
309 |
boolean bFill;
|
310 |
boolean bCrossing[]; |
311 |
int iNX, iNY;
|
312 |
int i;
|
313 |
int x, y, ix, xStart, xStop;
|
314 |
int iSize = 0; |
315 |
int iPoint;
|
316 |
double yPos;
|
317 |
double dValue;
|
318 |
AnalysisExtent ge; |
319 |
SimpleStats substats; |
320 |
Coordinate pLeft, pRight, pa, pb, p; |
321 |
|
322 |
final Envelope extent = geom.getEnvelopeInternal();
|
323 |
|
324 |
final Coordinate[] points = geom.getCoordinates(); |
325 |
|
326 |
for (i = 0; i < m_Window.length; i++) { |
327 |
p = new Coordinate();
|
328 |
substats = (SimpleStats) m_Stats.get(i); |
329 |
ge = m_Window[i].getWindowGridExtent(); |
330 |
iNX = ge.getNX(); |
331 |
iNY = ge.getNY(); |
332 |
bCrossing = new boolean[iNX]; |
333 |
|
334 |
xStart = (int) ((extent.getMinX() - ge.getXMin()) / ge.getCellSize()) - 1; |
335 |
if (xStart < 0) { |
336 |
xStart = 0;
|
337 |
} |
338 |
|
339 |
xStop = (int) ((extent.getMaxX() - ge.getXMin()) / ge.getCellSize()) + 1; |
340 |
if (xStop >= iNX) {
|
341 |
xStop = iNX - 1;
|
342 |
} |
343 |
|
344 |
for (y = 0, yPos = ge.getYMax(); y < iNY; y++, yPos -= ge.getCellSize()) { |
345 |
if ((yPos >= extent.getMinY()) && (yPos <= extent.getMaxY())) {
|
346 |
Arrays.fill(bCrossing, false); |
347 |
pLeft = new Coordinate(ge.getXMin() - 1.0, yPos); |
348 |
pRight = new Coordinate(ge.getXMax() + 1.0, yPos); |
349 |
|
350 |
pb = points[points.length - 1];
|
351 |
|
352 |
for (iPoint = 0; iPoint < points.length; iPoint++) { |
353 |
pa = pb; |
354 |
pb = points[iPoint]; |
355 |
|
356 |
if ((((pa.y <= yPos) && (yPos < pb.y)) || ((pa.y > yPos) && (yPos >= pb.y)))) {
|
357 |
getCrossing(p, pa, pb, pLeft, pRight); |
358 |
|
359 |
ix = (int) ((p.x - ge.getXMin()) / ge.getCellSize() + 1.0); |
360 |
|
361 |
if (ix < 0) { |
362 |
ix = 0;
|
363 |
} |
364 |
else if (ix >= iNX) { |
365 |
ix = iNX - 1;
|
366 |
} |
367 |
|
368 |
bCrossing[ix] = !bCrossing[ix]; |
369 |
} |
370 |
} |
371 |
|
372 |
for (x = xStart, bFill = false; x <= xStop; x++) { |
373 |
if (bCrossing[x]) {
|
374 |
bFill = !bFill; |
375 |
} |
376 |
if (bFill) {
|
377 |
iSize++; |
378 |
dValue = m_Window[i].getCellValueAsDouble(x /*+ m_iMinX - 1*/, y /*+ m_iMinY - 1*/, m_iBand[i]); |
379 |
if (!m_Window[i].isNoDataValue(dValue)) {
|
380 |
substats.addValue(dValue); |
381 |
} |
382 |
} |
383 |
} |
384 |
} |
385 |
} |
386 |
|
387 |
if (i == 0) { |
388 |
m_SizeStats.addValue(iSize); |
389 |
} |
390 |
} |
391 |
|
392 |
|
393 |
} |
394 |
|
395 |
|
396 |
private boolean getCrossing(final Coordinate crossing, |
397 |
final Coordinate a1,
|
398 |
final Coordinate a2,
|
399 |
final Coordinate b1,
|
400 |
final Coordinate b2) {
|
401 |
|
402 |
double lambda, div, a_dx, a_dy, b_dx, b_dy;
|
403 |
|
404 |
a_dx = a2.x - a1.x; |
405 |
a_dy = a2.y - a1.y; |
406 |
|
407 |
b_dx = b2.x - b1.x; |
408 |
b_dy = b2.y - b1.y; |
409 |
|
410 |
if ((div = a_dx * b_dy - b_dx * a_dy) != 0.0) { |
411 |
lambda = ((b1.x - a1.x) * b_dy - b_dx * (b1.y - a1.y)) / div; |
412 |
|
413 |
crossing.x = a1.x + lambda * a_dx; |
414 |
crossing.y = a1.y + lambda * a_dy; |
415 |
|
416 |
return true; |
417 |
|
418 |
} |
419 |
|
420 |
return false; |
421 |
|
422 |
} |
423 |
|
424 |
|
425 |
private AnalysisExtent getAdjustedGridExtent(final int iLayer) { |
426 |
|
427 |
double iMaxX, iMaxY;
|
428 |
double dMinX, dMinY;
|
429 |
double dMinX2, dMinY2, dMaxX2, dMaxY2;
|
430 |
double dCellSize;
|
431 |
final AnalysisExtent ge = new AnalysisExtent(); |
432 |
|
433 |
final Rectangle2D rect = m_Polygons.getFullExtent(); |
434 |
dMinX = m_Window[iLayer].getLayerGridExtent().getXMin(); |
435 |
dMinY = m_Window[iLayer].getLayerGridExtent().getYMin(); |
436 |
dCellSize = m_Window[iLayer].getLayerGridExtent().getCellSize(); |
437 |
|
438 |
m_iMinX = (int) Math.floor((rect.getMinX() - dMinX) / dCellSize); |
439 |
iMaxX = Math.ceil((rect.getMaxX() - dMinX) / dCellSize);
|
440 |
m_iMinY = (int) Math.floor((rect.getMinY() - dMinY) / dCellSize); |
441 |
iMaxY = Math.ceil((rect.getMaxY() - dMinY) / dCellSize);
|
442 |
|
443 |
dMinX2 = dMinX + m_iMinX * dCellSize; |
444 |
dMinY2 = dMinY + m_iMinY * dCellSize; |
445 |
dMaxX2 = dMinX + iMaxX * dCellSize; |
446 |
dMaxY2 = dMinY + iMaxY * dCellSize; |
447 |
|
448 |
ge.setCellSize(dCellSize); |
449 |
ge.setXRange(dMinX2, dMaxX2, true);
|
450 |
ge.setYRange(dMinY2, dMaxY2, true);
|
451 |
|
452 |
return ge;
|
453 |
|
454 |
} |
455 |
|
456 |
|
457 |
private boolean doParalellpiped() { |
458 |
|
459 |
boolean bIsInParalellpiped;
|
460 |
int iNX, iNY;
|
461 |
int x, y;
|
462 |
int iGrid;
|
463 |
final double dMean[] = new double[m_Window.length]; |
464 |
final double dStdDev[] = new double[m_Window.length]; |
465 |
double dValue;
|
466 |
SimpleStats substats; |
467 |
|
468 |
iNX = m_BinaryImage.getWindowGridExtent().getNX(); |
469 |
iNY = m_BinaryImage.getWindowGridExtent().getNY(); |
470 |
|
471 |
for (iGrid = 0; iGrid < m_Window.length; iGrid++) { |
472 |
substats = ((SimpleStats) m_Stats.get(iGrid)); |
473 |
dMean[iGrid] = substats.getMean(); |
474 |
dStdDev[iGrid] = Math.sqrt(substats.getVariance());
|
475 |
} |
476 |
|
477 |
setProgressText(Sextante.getText("Classifying"));
|
478 |
for (y = 0; (y < iNY) && setProgress(y, iNY); y++) { |
479 |
for (x = 0; x < iNX; x++) { |
480 |
bIsInParalellpiped = true;
|
481 |
for (iGrid = 0; iGrid < m_Window.length; iGrid++) { |
482 |
dValue = m_Window[iGrid].getCellValueAsDouble(x, y, m_iBand[iGrid]); |
483 |
if (!m_Window[iGrid].isNoDataValue(dValue)) {
|
484 |
if (Math.abs(dValue - dMean[iGrid]) > dStdDev[iGrid] * m_dTolerance) { |
485 |
bIsInParalellpiped = false;
|
486 |
break;
|
487 |
} |
488 |
} |
489 |
else {
|
490 |
bIsInParalellpiped = false;
|
491 |
break;
|
492 |
} |
493 |
} |
494 |
|
495 |
if (bIsInParalellpiped) {
|
496 |
m_BinaryImage.setCellValue(x, y, 1);
|
497 |
} |
498 |
else {
|
499 |
m_BinaryImage.setNoData(x, y); |
500 |
} |
501 |
} |
502 |
} |
503 |
|
504 |
return true; |
505 |
|
506 |
} |
507 |
|
508 |
} |