1 /***************************************************************************
2 * This file is part of Tecorrec. *
3 * Copyright 2008 James Hogan <james@albanarts.com> *
5 * Tecorrec is free software: you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation, either version 2 of the License, or *
8 * (at your option) any later version. *
10 * Tecorrec is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
15 * You should have received a copy of the GNU General Public License *
16 * along with Tecorrec. If not, write to the Free Software Foundation, *
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
18 ***************************************************************************/
21 * @file tcElevationOptimization.cpp
22 * @brief Optimized elevation.
25 #include "tcElevationOptimization.h"
26 #include "tcChannelConfigWidget.h"
27 #include "tcSrtmModel.h"
28 #include "tcShadowClassifyingData.h"
29 #include <SplineDefinitions.h>
32 #include <qwt_plot_curve.h>
36 #include <QVBoxLayout>
37 #include <QHBoxLayout>
38 #include <QGridLayout>
39 #include <QPushButton>
43 #include <QMessageBox>
44 #include <QFileDialog>
45 #include <QTextStream>
52 /// Worker thread class.
53 class tcElevationOptimization::WorkerThread
: public QThread
57 * Constructors + destructor
61 WorkerThread(tcElevationOptimization
* self
)
68 virtual ~WorkerThread()
78 m_this
->m_stopProcessing
= false;
79 m_this
->m_btnOptimize
->setText(tr("Optimize Iteratively"));
80 m_this
->m_btnOptimize
->update();
81 m_this
->m_processingMutex
.unlock();
90 /// Main optimization object.
91 tcElevationOptimization
* m_this
;
95 * Constructors + destructor
98 /// Primary constructor.
99 tcElevationOptimization::tcElevationOptimization(const QList
<tcShadowClassifyingData
*>& datasets
, tcSrtmModel
* dem
, tcGeoImageData
* imagery
)
100 : tcChannelDem(dem
, imagery
,
101 tr("Elevation Optimization"),
102 tr("Optimizes elevation using image data."),
104 , m_numDatasets(datasets
.size())
105 , m_datasets(new tcGeoImageData
*[m_numDatasets
])
106 , m_texToDatasetTex(new tcAffineTransform2
<double>[m_numDatasets
])
107 , m_shadowChannels(new tcChannel
*[m_numDatasets
])
108 , m_shadingChannels(new tcChannel
*[m_numDatasets
])
110 , m_data(new tcTypedPixelData
<PerDatasetPixelCache
>*[m_numDatasets
])
112 , m_shadowData(new Reference
<tcPixelData
<float> >[m_numDatasets
])
113 , m_shadingData(new Reference
<tcPixelData
<float> >[m_numDatasets
])
115 , m_loadingFromDem(false)
117 , m_processingMutex()
118 , m_stopProcessing(false)
121 , m_spinIterations(0)
122 , m_plot(new QwtPlot())
123 , m_stdDevCurve(new QwtPlotCurve())
124 , m_stdDevVoidCurve(new QwtPlotCurve())
125 , m_stdDevNonVoidCurve(new QwtPlotCurve())
127 , m_snapshotReadFile()
128 , m_snapshotReading(false)
129 , m_snapshotSlider(0)
130 , m_snapshotCounter(0)
132 m_plot
->setWindowFlags(Qt::Tool
| Qt::WindowStaysOnTopHint
);
133 m_plot
->setTitle("Standard Deviations");
134 connect(this, SIGNAL(replotStdDeviations()), m_plot
, SLOT(replot()), Qt::QueuedConnection
);
135 connect(this, SIGNAL(invalidateAndRedraw()), this, SLOT(invalidateAndRedrawSlot()), Qt::BlockingQueuedConnection
);
136 m_stdDevVoidCurve
->setPen(QPen(Qt::black
));
137 m_stdDevVoidCurve
->attach(m_plot
);
138 m_stdDevNonVoidCurve
->setPen(QPen(Qt::green
));
139 m_stdDevNonVoidCurve
->attach(m_plot
);
140 m_stdDevCurve
->setPen(QPen(Qt::blue
));
141 m_stdDevCurve
->attach(m_plot
);
142 for (int i
= 0; i
< m_numDatasets
; ++i
)
144 tcShadowClassifyingData
* landsat
= datasets
[i
];
145 m_datasets
[i
] = landsat
;
146 m_texToDatasetTex
[i
] = imagery
->texToGeo() * landsat
->geoToTex();
147 m_shadowChannels
[i
] = landsat
->shadowClassification();
148 if (0 != m_shadowChannels
[i
])
150 m_shadowChannels
[i
]->addDerivitive(this);
152 m_shadingChannels
[i
] = landsat
->shading();
153 if (0 != m_shadingChannels
[i
])
155 m_shadingChannels
[i
]->addDerivitive(this);
162 tcElevationOptimization::~tcElevationOptimization()
164 if (!m_processingMutex
.tryLock())
166 m_stopProcessing
= true;
171 delete [] m_datasets
;
172 delete [] m_texToDatasetTex
;
173 for (int i
= 0; i
< m_numDatasets
; ++i
)
175 if (0 != m_shadowChannels
[i
])
177 m_shadowChannels
[i
]->removeDerivitive(this);
179 if (0 != m_shadingChannels
[i
])
181 m_shadingChannels
[i
]->removeDerivitive(this);
185 delete [] m_shadowChannels
;
186 delete [] m_shadingChannels
;
187 delete m_elevationData
;
190 delete [] m_shadowData
;
191 delete [] m_shadingData
;
192 delete m_configWidget
;
196 * Main image interface
199 tcChannelConfigWidget
* tcElevationOptimization::configWidget()
201 if (0 == m_configWidget
)
203 m_configWidget
= new tcChannelConfigWidget();
204 m_configWidget
->setWindowTitle(tr("%1 Configuration").arg(name()));
206 QVBoxLayout
* layout
= new QVBoxLayout(m_configWidget
);
208 QLabel
* stats
= new QLabel(m_configWidget
);
209 layout
->addWidget(stats
);
210 connect(this, SIGNAL(statistics(const QString
&)), stats
, SLOT(setText(const QString
&)));
212 // Row of optimisation controls
213 QWidget
* buttons1
= new QWidget(m_configWidget
);
214 layout
->addWidget(buttons1
);
215 QHBoxLayout
* buttons1Layout
= new QHBoxLayout(buttons1
);
217 // reset DEM (sets all corrections to the original (interpolated) values
218 QPushButton
* btnResetDem
= new QPushButton(tr("Reset DEM"), m_configWidget
);
219 buttons1Layout
->addWidget(btnResetDem
);
220 btnResetDem
->setToolTip(tr("Reset DEM corrected values to interpolated values"));
221 connect(btnResetDem
, SIGNAL(clicked(bool)), this, SLOT(resetDem()));
223 // load base from DEM (loads DEM elevations into elevation buffer)
224 QPushButton
* btnLoadDem
= new QPushButton(tr("Load DEM"), m_configWidget
);
225 buttons1Layout
->addWidget(btnLoadDem
);
226 btnLoadDem
->setToolTip(tr("Load corrected values from DEM into elevation field"));
227 connect(btnLoadDem
, SIGNAL(clicked(bool)), this, SLOT(loadFromDem()));
229 // number of iterations (select number of iterations to perform)
230 m_spinIterations
= new QSpinBox(m_configWidget
);
231 buttons1Layout
->addWidget(m_spinIterations
);
232 m_spinIterations
->setToolTip(tr("Number of iterations of optimization to perform"));
233 m_spinIterations
->setRange(1, 1000);
234 m_spinIterations
->setValue(10);
236 // optimize (iterates on elevation buffer and write back to DEM at intervals and end)
237 m_btnOptimize
= new QPushButton(tr("Optimize Iteratively"), m_configWidget
);
238 buttons1Layout
->addWidget(m_btnOptimize
);
239 m_btnOptimize
->setToolTip(tr("Iteratively optimize elevation field and write results back to DEM"));
240 connect(m_btnOptimize
, SIGNAL(clicked(bool)), this, SLOT(startStopOptimize()));
242 // Another row of optimisation controls
243 QWidget
* buttons2
= new QWidget(m_configWidget
);
244 layout
->addWidget(buttons2
);
245 QHBoxLayout
* buttons2Layout
= new QHBoxLayout(buttons2
);
247 // apply hard constraints, adjusting reliabilities
248 QPushButton
* btnHardConstrain
= new QPushButton(tr("Hard Constrain"), m_configWidget
);
249 buttons2Layout
->addWidget(btnHardConstrain
);
250 btnLoadDem
->setToolTip(tr("Apply hard shadow constraints"));
251 connect(btnHardConstrain
, SIGNAL(clicked(bool)), this, SLOT(applyHardConstraints()));
253 // bilinear interpolation of voids
254 QPushButton
* btnBilinear
= new QPushButton(tr("Bilinear"), m_configWidget
);
255 buttons2Layout
->addWidget(btnBilinear
);
256 btnLoadDem
->setToolTip(tr("Void-fill using bilinear interpolation"));
257 connect(btnBilinear
, SIGNAL(clicked(bool)), this, SLOT(voidFillBilinear()));
259 // bicubic interpolation of voids
260 QPushButton
* btnBicubic
= new QPushButton(tr("Bicubic"), m_configWidget
);
261 buttons2Layout
->addWidget(btnBicubic
);
262 btnLoadDem
->setToolTip(tr("Void-fill using bicubic interpolation"));
263 connect(btnBicubic
, SIGNAL(clicked(bool)), this, SLOT(voidFillBicubic()));
265 // Another row of controls, for graphs
266 QWidget
* buttons3
= new QWidget(m_configWidget
);
267 layout
->addWidget(buttons3
);
268 QHBoxLayout
* buttons3Layout
= new QHBoxLayout(buttons3
);
270 // Show standard deviation curve
271 QPushButton
* btnStdDev
= new QPushButton(tr("Std Dev"), m_configWidget
);
272 buttons3Layout
->addWidget(btnStdDev
);
273 btnStdDev
->setToolTip(tr("Show standard deviation graphs"));
274 connect(btnStdDev
, SIGNAL(clicked(bool)), m_plot
, SLOT(show()));
276 // Clear standard deviation
277 QPushButton
* btnStdDevClear
= new QPushButton(tr("Clear"), m_configWidget
);
278 buttons3Layout
->addWidget(btnStdDevClear
);
279 btnStdDevClear
->setToolTip(tr("Clear standard deviation graphs"));
280 connect(btnStdDevClear
, SIGNAL(clicked(bool)), this, SLOT(clearStdDeviations()));
282 // Sample standard deviation
283 QPushButton
* btnStdDevSample
= new QPushButton(tr("Sample"), m_configWidget
);
284 buttons3Layout
->addWidget(btnStdDevSample
);
285 btnStdDevSample
->setToolTip(tr("Sample standard deviation"));
286 connect(btnStdDevSample
, SIGNAL(clicked(bool)), this, SLOT(sampleStdDeviations()));
288 // Save standard deviation
289 QPushButton
* btnStdDevSave
= new QPushButton(tr("Save"), m_configWidget
);
290 buttons3Layout
->addWidget(btnStdDevSave
);
291 btnStdDevSave
->setToolTip(tr("Save standard deviation samples to a text file for external processing"));
292 connect(btnStdDevSave
, SIGNAL(clicked(bool)), this, SLOT(saveStdDeviations()));
294 // Another row of controls, for terrain snapshotting
295 QWidget
* buttons4
= new QWidget(m_configWidget
);
296 layout
->addWidget(buttons4
);
297 QHBoxLayout
* buttons4Layout
= new QHBoxLayout(buttons4
);
299 // Initialise snapshotting
300 QPushButton
* btnInitSnapshotting
= new QPushButton(tr("Start snapshotting"), m_configWidget
);
301 buttons4Layout
->addWidget(btnInitSnapshotting
);
302 btnInitSnapshotting
->setToolTip(tr("Start snapshotting the terrain at intervals for later analysis"));
303 connect(btnInitSnapshotting
, SIGNAL(clicked(bool)), this, SLOT(initSnapShotting()));
306 QPushButton
* btnLoadSnapshots
= new QPushButton(tr("Load snapshots"), m_configWidget
);
307 buttons4Layout
->addWidget(btnLoadSnapshots
);
308 btnLoadSnapshots
->setToolTip(tr("Load a sequence of snapshots for processing"));
309 connect(btnLoadSnapshots
, SIGNAL(clicked(bool)), this, SLOT(loadSnapShots()));
311 // Samples resolution
312 m_crossResolution
= new QSpinBox(m_configWidget
);
313 buttons4Layout
->addWidget(m_crossResolution
);
314 m_crossResolution
->setToolTip(tr("Cross sectioning resolution"));
315 m_crossResolution
->setRange(64, 1024);
316 m_crossResolution
->setValue(1024);
318 // Frequency of samples
319 m_crossFrequency
= new QSpinBox(m_configWidget
);
320 buttons4Layout
->addWidget(m_crossFrequency
);
321 m_crossFrequency
->setToolTip(tr("Cross sectioning frequency"));
322 m_crossFrequency
->setRange(1, 10);
323 m_crossFrequency
->setValue(1);
325 // Button to save cross sectioning data
326 QPushButton
* btnCrossSectionSave
= new QPushButton(tr("Save cross section data"), m_configWidget
);
327 buttons4Layout
->addWidget(btnCrossSectionSave
);
328 connect(btnCrossSectionSave
, SIGNAL(clicked(bool)), this, SLOT(saveCrossSections()));
331 m_snapshotCounter
= new QLabel("0", m_configWidget
);
332 buttons4Layout
->addWidget(m_snapshotCounter
);
335 m_snapshotSlider
= new QSlider(Qt::Horizontal
, m_configWidget
);
336 layout
->addWidget(m_snapshotSlider
);
337 m_snapshotSlider
->setEnabled(false);
338 connect(m_snapshotSlider
, SIGNAL(sliderMoved(int)), this, SLOT(loadSnapShot(int)));
340 // Rows of weight sliders
341 QString weightNames
[WeightsMax
] = {
345 tr("Lit facing sun"),
346 tr("Shape from shading"),
347 tr("Photometric Stereo"),
348 tr("Below shadow line"),
349 tr("Transition elevation (top)"),
350 tr("Transition elevation (bottom)"),
351 tr("Transition slope"),
352 tr("Transition curving down"),
353 tr("Elevation Step"),
356 float defaults
[WeightsMax
][3] = {
357 {0.0f
, 10.0f
, 1.0f
}, // variance
358 {0.0f
, 5.0f
, 0.1f
}, // roughenss
359 {0.0f
, 5.0f
, 0.1f
}, // steepness
360 {0.0f
, 1000.0f
, 255.0f
}, // lit facing
361 {0.0f
, 100.0f
, 0.0f
}, // shading
362 {0.0f
, 100.0f
, 0.0f
}, // photometric stereo
363 {0.0f
, 10.0f
, 1.0f
}, // belowShadowLine
364 {0.0f
, 10.0f
, 1.0f
}, // transitionElevTop
365 {0.0f
, 10.0f
, 1.0f
}, // transitionElevBottom
366 {0.0f
, 1000.0f
, 100.0f
}, // transitionTangential
367 {0.0f
, 10.0f
, 1.0f
}, // transitionCurvingDown
368 {0.0f
, 20.0f
, 4.0f
}, // elevation step
369 {0.0f
, 4.0f
, 0.0f
}, // range
371 QWidget
* sliders
= new QWidget(m_configWidget
);
372 layout
->addWidget(sliders
);
373 QGridLayout
* slidersLayout
= new QGridLayout(sliders
);
376 for (; i
< WeightsMax
; ++i
)
378 QLabel
* label
= new QLabel(weightNames
[i
], sliders
);
379 slidersLayout
->addWidget(label
, i
, 0);
382 m_weightLimits
[0][i
] = defaults
[i
][0];
383 m_weightLimits
[1][i
] = (defaults
[i
][1] - defaults
[i
][0])/steps
;
384 int val
= (int)((defaults
[i
][2] - m_weightLimits
[0][i
]) / m_weightLimits
[1][i
]);
386 for (int sl
= 0; sl
< 2; ++sl
)
388 m_weightSliders
[sl
][i
] = new QSlider(Qt::Horizontal
, sliders
);
389 slidersLayout
->addWidget(m_weightSliders
[sl
][i
], i
, sl
+1);
390 m_weightSliders
[sl
][i
]->setRange(0, steps
);
391 m_weightSliders
[sl
][i
]->setValue(val
);
392 connect(m_weightSliders
[sl
][i
], SIGNAL(sliderMoved(int)), this, SLOT(updateWeightLabels()));
393 connect(m_weightSliders
[sl
][i
], SIGNAL(valueChanged(int)), this, SLOT(updateWeightLabels()));
395 m_weightLabels
[i
] = new QLabel("-", sliders
);
396 slidersLayout
->addWidget(m_weightLabels
[i
], i
, 3);
398 updateWeightLabels();
400 // Another row of controls, for weight saving, loading
401 QWidget
* buttons5
= new QWidget(m_configWidget
);
402 layout
->addWidget(buttons5
);
403 QHBoxLayout
* buttons5Layout
= new QHBoxLayout(buttons5
);
406 QPushButton
* btnSaveWeights
= new QPushButton(tr("Save weights"), m_configWidget
);
407 buttons5Layout
->addWidget(btnSaveWeights
);
408 btnInitSnapshotting
->setToolTip(tr("Save the weights to a file"));
409 connect(btnSaveWeights
, SIGNAL(clicked(bool)), this, SLOT(saveWeights()));
412 QPushButton
* btnLoadWeights
= new QPushButton(tr("Load weights"), m_configWidget
);
413 buttons5Layout
->addWidget(btnLoadWeights
);
414 btnLoadWeights
->setToolTip(tr("Load the weights from a file"));
415 connect(btnLoadWeights
, SIGNAL(clicked(bool)), this, SLOT(loadWeights()));
417 return m_configWidget
;
425 void tcElevationOptimization::resetDem()
429 /// Load elevation data from DEM.
430 void tcElevationOptimization::loadFromDem()
432 delete m_elevationData
;
438 for (int i
= 0; i
< m_numDatasets
; ++i
)
444 m_loadingFromDem
= true;
446 m_configWidget
->requestRedraw();
447 m_loadingFromDem
= false;
450 /// Apply hard constraints to elevation data.
451 void tcElevationOptimization::applyHardConstraints()
453 if (0 != m_elevationData
)
455 const int width
= m_elevationData
->width();
456 const int height
= m_elevationData
->height();
457 // Now start optimising
458 startProcessing("Applying hard constraints");
459 for (int j
= 0; j
<= height
; ++j
)
461 progress((float)j
/(height
-1));
462 for (int i
= 0; i
<= width
; ++i
)
464 int index
= j
*width
+ i
;
466 // Only apply constraints to unreliable pixels
467 PerPixelCache
* cache
= &m_datum
->buffer()[index
];
468 if (cache
->reliability
!= 0)
473 int elevationConstraintCount
= 0;
476 for (int ds
= 0; ds
< m_numDatasets
; ++ds
)
478 PerDatasetPixelCache
* dsCache
= &m_data
[ds
]->buffer()[index
];
480 bool isShadowed
= (m_shadowData
[ds
]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1)) < 0.5f
);
481 bool isShadowEntrance
= isShadowed
&&
482 dsCache
->shadowTransition
[TransitionEntrance
][0] == i
&&
483 dsCache
->shadowTransition
[TransitionEntrance
][1] == j
;
484 bool isShadowExit
= isShadowed
&&
485 dsCache
->shadowTransition
[TransitionExit
][0] == i
&&
486 dsCache
->shadowTransition
[TransitionExit
][1] == j
;
490 bool exitReliable
= false;
491 float exitElevation
= 0.0f
;
492 float exitDistance
= 0.0f
;
493 if (dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
494 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
495 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
496 dsCache
->shadowTransition
[TransitionExit
][1] < height
)
498 int exitIndex
= width
*dsCache
->shadowTransition
[TransitionExit
][1] + dsCache
->shadowTransition
[TransitionExit
][0];
499 PerPixelCache
* exitCache
= &m_datum
->buffer()[exitIndex
];
500 // Great, we can say for definite the elevation of this pixel because the corresponding pixel is reliable
501 if (exitCache
->reliability
!= 0)
504 exitElevation
= m_elevationData
->buffer()[exitIndex
];
505 exitDistance
= dsCache
->shadowTransitionDistance
[TransitionExit
];
506 if (isShadowEntrance
)
508 float newElevation
= exitElevation
+ exitDistance
*dsCache
->dlz_dlxy
;
509 m_elevationData
->buffer()[index
] = m_elevationData
->buffer()[index
]*elevationConstraintCount
+ newElevation
;
510 m_elevationData
->buffer()[index
] /= ++elevationConstraintCount
;
511 cache
->originalElevation
= m_elevationData
->buffer()[index
];
512 cache
->reliability
= 1;
516 bool entranceReliable
= false;
517 float entranceElevation
= 0.0f
;
518 float entranceDistance
= 0.0f
;
519 if (dsCache
->shadowTransition
[TransitionEntrance
][0] >= 0 &&
520 dsCache
->shadowTransition
[TransitionEntrance
][0] < width
&&
521 dsCache
->shadowTransition
[TransitionEntrance
][1] >= 0 &&
522 dsCache
->shadowTransition
[TransitionEntrance
][1] < height
)
524 int entranceIndex
= width
*dsCache
->shadowTransition
[TransitionEntrance
][1] + dsCache
->shadowTransition
[TransitionEntrance
][0];
525 PerPixelCache
* entranceCache
= &m_datum
->buffer()[entranceIndex
];
526 // Great, we can say for definite the elevation of this pixel because the corresponding pixel is reliable
527 if (entranceCache
->reliability
!= 0)
529 entranceReliable
= true;
530 entranceElevation
= m_elevationData
->buffer()[entranceIndex
];
531 entranceDistance
= dsCache
->shadowTransitionDistance
[TransitionEntrance
];
534 float newElevation
= entranceElevation
- entranceDistance
*dsCache
->dlz_dlxy
;
535 m_elevationData
->buffer()[index
] = m_elevationData
->buffer()[index
]*elevationConstraintCount
+ newElevation
;
536 m_elevationData
->buffer()[index
] /= ++elevationConstraintCount
;
537 cache
->originalElevation
= m_elevationData
->buffer()[index
];
538 cache
->reliability
= 1;
543 if (exitReliable
&& entranceReliable
)
545 // maximum elevation is between elevation at entrance and elevation at exit
546 float maxElevation
= (exitElevation
*entranceDistance
+ entranceElevation
*exitDistance
) / (entranceDistance
+ exitDistance
);
547 if (m_elevationData
->buffer()[index
] > maxElevation
)
549 m_elevationData
->buffer()[index
] = maxElevation
;
559 m_configWidget
->requestRedraw();
563 QMessageBox::warning(m_configWidget
,
564 tr("Elevation field not initialised."),
565 tr("Please ensure the optimisation channel is displayed."));
569 /// Void-fill bilinear.
570 void tcElevationOptimization::voidFillBilinear()
572 if (0 != m_elevationData
)
574 const int width
= m_elevationData
->width();
575 const int height
= m_elevationData
->height();
576 // Now start optimising
577 startProcessing("Bilinear void filling");
578 for (int j
= 0; j
< height
; ++j
)
580 progress(0.5f
*j
/(height
-1));
581 int lastNonVoid
= -1;
582 float lastNonVoidValue
;
583 for (int i
= 0; i
< width
; ++i
)
585 int index
= j
*width
+ i
;
586 if (m_datum
->buffer()[index
].reliability
!= 0)
588 float value
= m_elevationData
->buffer()[index
];
589 if (lastNonVoid
!= -1)
591 int gap
= i
- lastNonVoid
;
592 float startAlt
= lastNonVoidValue
;
593 float dAlt
= (value
- lastNonVoidValue
) / gap
;
594 // Lovely, between lastNonVoid and i is a void
595 for (int ii
= lastNonVoid
+1; ii
< i
; ++ii
)
597 int iIndex
= j
*width
+ ii
;
598 float alt
= startAlt
+ dAlt
* (ii
- lastNonVoid
);
599 m_elevationData
->buffer()[iIndex
] = alt
;
600 // Keep track of the gap
601 m_datum
->buffer()[iIndex
].temporary
.i
= gap
;
606 m_datum
->buffer()[index
].temporary
.i
= -1;
609 lastNonVoidValue
= value
;
613 m_datum
->buffer()[index
].temporary
.i
= -1;
617 for (int i
= 0; i
< width
; ++i
)
619 progress(0.5f
+ 0.5f
*i
/(width
-1));
620 int lastNonVoid
= -1;
621 float lastNonVoidValue
;
622 for (int j
= 0; j
< height
; ++j
)
624 int index
= j
*width
+ i
;
625 float value
= m_elevationData
->buffer()[index
];
626 if (m_datum
->buffer()[index
].reliability
!= 0)
628 if (lastNonVoid
!= -1)
630 int gap
= j
- lastNonVoid
;
631 float startAlt
= lastNonVoidValue
;
632 float dAlt
= (value
- lastNonVoidValue
) / gap
;
633 // Lovely, between lastNonVoid and j is a void
634 for (int jj
= lastNonVoid
+1; jj
< j
; ++jj
)
636 // Average with previous value if non zero, otherwise overwrite
637 int iIndex
= jj
*width
+ i
;
638 float alt
= startAlt
+ dAlt
* (jj
- lastNonVoid
);
639 int otherGap
= m_datum
->buffer()[iIndex
].temporary
.i
;
642 float prevAlt
= m_elevationData
->buffer()[iIndex
];
643 // Prefer the value of the smaller gap
644 alt
= (alt
* otherGap
+ prevAlt
* gap
) / (gap
+otherGap
);
646 m_elevationData
->buffer()[iIndex
] = alt
;
650 lastNonVoidValue
= value
;
657 m_configWidget
->requestRedraw();
661 QMessageBox::warning(m_configWidget
,
662 tr("Elevation field not initialised."),
663 tr("Please ensure the optimisation channel is displayed."));
667 /// Void-fill bicubic.
668 void tcElevationOptimization::voidFillBicubic()
670 if (0 != m_elevationData
)
672 const int width
= m_elevationData
->width();
673 const int height
= m_elevationData
->height();
674 // Now start optimising
675 startProcessing("Bicubic void filling");
676 for (int j
= 0; j
< height
; ++j
)
678 progress(0.5f
*j
/(height
-1));
679 int lastNonVoid
= -1;
680 float lastNonVoidValue
;
681 for (int i
= 0; i
< width
; ++i
)
683 int index
= j
*width
+ i
;
684 if (m_datum
->buffer()[index
].reliability
!= 0)
686 float value
= m_elevationData
->buffer()[index
];
687 if (lastNonVoid
!= -1)
689 int gap
= i
- lastNonVoid
;
690 // Obtain some control points for the splines
691 // use the two samples either side of the gap as control points 1 and 2
692 // use the ones before and after these samples, extended to the size of
693 // the gap as control points 0 and 3
694 float startAlt
= lastNonVoidValue
;
695 float beforeStartAlt
= startAlt
;
698 if (0 != m_datum
->buffer()[j
*width
+ lastNonVoid
- 1].reliability
)
700 beforeStartAlt
= startAlt
+ (m_elevationData
->buffer()[j
*width
+ lastNonVoid
- 1] - startAlt
) * gap
;
703 float endAlt
= value
;
704 float afterEndAlt
= endAlt
;
707 if (0 != m_datum
->buffer()[j
*width
+ i
+ 1].reliability
)
709 afterEndAlt
= endAlt
+ (m_elevationData
->buffer()[j
*width
+ i
+ 1] - endAlt
) * gap
;
712 // Lovely, between lastNonVoid and i is a void
713 for (int ii
= lastNonVoid
+1; ii
< i
; ++ii
)
715 int iIndex
= j
*width
+ ii
;
716 float u
= (float)(ii
-lastNonVoid
) / gap
;
719 float alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
720 m_elevationData
->buffer()[iIndex
] = alt
;
721 // Keep track of the gap
722 m_datum
->buffer()[iIndex
].temporary
.i
= gap
;
727 m_datum
->buffer()[index
].temporary
.i
= -1;
730 lastNonVoidValue
= value
;
734 m_datum
->buffer()[index
].temporary
.i
= -1;
738 for (int i
= 0; i
< width
; ++i
)
740 progress(0.5f
+ 0.5f
*i
/(width
-1));
741 int lastNonVoid
= -1;
742 float lastNonVoidValue
;
743 for (int j
= 0; j
< height
; ++j
)
745 int index
= j
*width
+ i
;
746 float value
= m_elevationData
->buffer()[index
];
747 if (m_datum
->buffer()[index
].reliability
!= 0)
749 if (lastNonVoid
!= -1)
751 int gap
= j
- lastNonVoid
;
752 // Obtain some control points for the splines
753 // use the two samples either side of the gap as control points 1 and 2
754 // use the ones before and after these samples, extended to the size of
755 // the gap as control points 0 and 3
756 float startAlt
= lastNonVoidValue
;
757 float beforeStartAlt
= startAlt
;
760 if (0 != m_datum
->buffer()[(lastNonVoid
-1)*width
+ i
].reliability
)
762 beforeStartAlt
= startAlt
+ (m_elevationData
->buffer()[(lastNonVoid
-1)*width
+ i
] - startAlt
) * gap
;
765 float endAlt
= value
;
766 float afterEndAlt
= endAlt
;
769 if (0 != m_datum
->buffer()[(j
+1)*width
+ i
].reliability
)
771 afterEndAlt
= endAlt
+ (m_elevationData
->buffer()[(j
+1)*width
+ i
] - endAlt
) * gap
;
774 // Lovely, between lastNonVoid and j is a void
775 for (int jj
= lastNonVoid
+1; jj
< j
; ++jj
)
777 // Average with previous value if non zero, otherwise overwrite
778 int iIndex
= jj
*width
+ i
;
779 float u
= (float)(jj
-lastNonVoid
) / gap
;
782 float alt
= maths::catmullRomSpline
.Spline(u
, u2
, u3
, beforeStartAlt
, startAlt
, endAlt
, afterEndAlt
);
783 int otherGap
= m_datum
->buffer()[iIndex
].temporary
.i
;
786 float prevAlt
= m_elevationData
->buffer()[iIndex
];
787 // Prefer the value of the smaller gap
788 alt
= (alt
* otherGap
+ prevAlt
* gap
) / (gap
+otherGap
);
790 m_elevationData
->buffer()[iIndex
] = alt
;
794 lastNonVoidValue
= value
;
801 m_configWidget
->requestRedraw();
805 QMessageBox::warning(m_configWidget
,
806 tr("Elevation field not initialised."),
807 tr("Please ensure the optimisation channel is displayed."));
811 /// Start/stop optimization process.
812 void tcElevationOptimization::startStopOptimize()
814 if (m_processingMutex
.tryLock())
816 m_btnOptimize
->setText(tr("Stop Optimization"));
817 m_btnOptimize
->update();
820 m_thread
= new WorkerThread(this);
826 // Warn the worker thread that its time to stop.
827 m_stopProcessing
= true;
831 /// Optimize a number of times.
832 void tcElevationOptimization::optimize()
834 if (0 != m_elevationData
)
836 const int width
= m_elevationData
->width();
837 const int height
= m_elevationData
->height();
838 // Now start optimising
839 startProcessing("Optimizing elevation data");
840 int passes
= m_spinIterations
->value();
841 for (int i
= 0; i
< passes
; ++i
)
843 startProcessing("Optimizing elevation data");
844 float passage
= (float)i
/(passes
-1);
845 float passageNeg
= 1.0f
- passageNeg
;
847 for (int w
= 0; w
< WeightsMax
; ++w
)
849 float start
= m_weightLimits
[0][w
] + m_weightLimits
[1][w
]*m_weightSliders
[0][w
]->value();
850 float end
= m_weightLimits
[0][w
] + m_weightLimits
[1][w
]*m_weightSliders
[1][w
]->value();
851 m_weights
[w
] = passageNeg
*start
+ passage
*end
;
853 float deltaH
= m_weights
[ElevationStep
];
854 float range
= m_weights
[Range
];
855 optimizeElevations(0, 0, width
-1, height
-1, deltaH
, range
);
856 if (m_stopProcessing
)
858 emit
invalidateAndRedraw();
863 if ((i
+1) % 5 == 0 || i
== passes
-1)
865 emit
invalidateAndRedraw();
866 startProcessing("Optimizing elevation data");
867 progress((float)i
/passes
);
870 sampleStdDeviations();
876 QMessageBox::warning(m_configWidget
,
877 tr("Elevation field not initialised."),
878 tr("Please ensure the optimisation channel is displayed."));
882 /// Clear standard deviations.
883 void tcElevationOptimization::clearStdDeviations()
886 m_stdDevData
.clear();
887 m_stdDevCurve
->setData(m_plotX
, m_stdDevData
);
888 m_stdDevVoidData
.clear();
889 m_stdDevVoidCurve
->setData(m_plotX
, m_stdDevVoidData
);
890 m_stdDevNonVoidData
.clear();
891 m_stdDevNonVoidCurve
->setData(m_plotX
, m_stdDevNonVoidData
);
896 /// Sample standard deviations.
897 void tcElevationOptimization::sampleStdDeviations()
899 int width
= m_elevationData
->width();
900 int height
= m_elevationData
->height();
901 float stdDevVoid
= 0.0f
;
902 float stdDevNonVoid
= 0.0f
;
903 float stdDevAll
= 0.0f
;
906 int marginx
= height
>>3;
907 int marginy
= width
>>3;
908 for (int j
= marginy
; j
< height
-marginy
; ++j
)
910 for (int i
= marginx
; i
< width
-marginx
; ++i
)
912 int index
= j
*width
+ i
;
913 float altitude
= m_datum
->buffer()[index
].accurateElevation
;
915 float square
= m_elevationData
->buffer()[index
] - altitude
;
918 if (m_datum
->buffer()[index
].reliability
)
920 stdDevNonVoid
+= square
;
924 stdDevVoid
+= square
;
930 stdDevNonVoid
= sqrt(stdDevNonVoid
/ (total
-voids
));
931 stdDevVoid
= sqrt(stdDevVoid
/ voids
);
932 stdDevAll
= sqrt(stdDevAll
/ total
);
933 emit
statistics(tr("Overall std deviation: %1 m\n"
934 "Void std deviation: %2 (%4\%) m\n"
935 "Non-void std deviation: %3 (%5\%) m")
937 .arg(stdDevVoid
).arg(stdDevNonVoid
)
938 .arg(100.0f
*voids
/total
).arg(100.0f
*(total
-voids
)/total
));
939 //m_configWidget->update();
941 if (m_snapshotReadFile
.isEmpty() || m_snapshotReading
)
943 m_plotX
.append(m_plotX
.size()+1);
944 m_stdDevData
.append(stdDevAll
);
945 m_stdDevCurve
->setData(m_plotX
, m_stdDevData
);
946 m_stdDevVoidData
.append(stdDevVoid
);
947 m_stdDevVoidCurve
->setData(m_plotX
, m_stdDevVoidData
);
948 m_stdDevNonVoidData
.append(stdDevNonVoid
);
949 m_stdDevNonVoidCurve
->setData(m_plotX
, m_stdDevNonVoidData
);
950 // this will get queued for event loop
951 emit
replotStdDeviations();
954 if (!m_snapshotFile
.isEmpty())
956 QString fname
= QString("%1.%2").arg(m_snapshotFile
).arg(m_plotX
.size(), 4, 10, QLatin1Char('0'));
958 if (snap
.open(QIODevice::WriteOnly
))
960 QDataStream
stream(&snap
);
961 stream
.setByteOrder(QDataStream::LittleEndian
);
962 stream
<< width
<< height
;
964 for (int j
= 0; j
< height
; ++j
)
966 for (int i
= 0; i
< width
; ++i
)
968 uint16_t alt
= floor(0.5f
+ m_elevationData
->buffer()[index
]);
969 uint16_t orig
= floor(0.5f
+ m_datum
->buffer()[index
].originalElevation
);
970 if (!m_datum
->buffer()[index
].reliability
)
974 stream
<< alt
<< orig
;
983 /// Sample standard deviations.
984 void tcElevationOptimization::saveStdDeviations()
986 QString filename
= QFileDialog::getSaveFileName(m_configWidget
,
987 tr("Save standard deviation data"),
989 tr("ASCII Data Files (*.adf)"));
991 if (!filename
.isEmpty())
993 // Open the file ready to write the data
994 QFile
file(filename
);
995 if (!file
.open(QIODevice::WriteOnly
| QIODevice::Text
))
997 QMessageBox::warning(m_configWidget
,
999 tr("Could not open '%1' for writing.").arg(filename
));
1003 QTextStream
out(&file
);
1005 for (int i
= 0; i
< m_plotX
.size(); ++i
)
1007 out
<< m_plotX
[i
] << "\t"
1008 << m_stdDevData
[i
] << "\t"
1009 << m_stdDevVoidData
[i
] << "\t"
1010 << m_stdDevNonVoidData
[i
] << "\n";
1015 /// Save cross section data.
1016 void tcElevationOptimization::saveCrossSections()
1018 if (!m_crossSectioned
)
1020 QMessageBox::warning(m_configWidget
,
1021 tr("Cross section save failed."),
1022 tr("You need to select a cross section with middle mouse button drag."));
1026 QString filename
= QFileDialog::getSaveFileName(m_configWidget
,
1027 tr("Save cross section data"),
1029 tr("Crosssection Files (*.xsec)"));
1031 if (!filename
.isEmpty())
1033 // Open the file ready to write the data
1034 QFile
file(filename
);
1035 if (!file
.open(QIODevice::WriteOnly
| QIODevice::Text
))
1037 QMessageBox::warning(m_configWidget
,
1039 tr("Could not open '%1' for writing.").arg(filename
));
1043 QTextStream
out(&file
);
1045 int resolution
= m_crossResolution
->value();
1046 int frequency
= m_crossFrequency
->value();
1047 int snaps
= m_snapshotSlider
->maximum();
1048 tcGeo delta
= (m_crossSection
[1] - m_crossSection
[0]) / (resolution
-1);
1051 float deltaDist
= tcGeo::angleBetween(m_crossSection
[1], m_crossSection
[0])/(resolution
-1)*6378.137e3
;
1052 float distance
= 0.0f
;
1053 for (int p
= 0; p
< resolution
; ++p
)
1055 out
<< distance
<< ((p
== resolution
-1) ? "\n" : "\t");
1056 distance
+= deltaDist
;
1059 for (int snap
= 1; snap
<= snaps
; snap
+= frequency
)
1064 tcGeo tmp
= m_crossSection
[0];
1065 for (int p
= 0; p
< resolution
; ++p
)
1067 float elev
= m_elevationData
->elevationAt(tmp
);
1068 out
<< elev
<< ((p
== resolution
-1) ? "\n" : "\t");
1069 tmp
.setLon(tmp
.lon() + delta
.lon());
1070 tmp
.setLat(tmp
.lat() + delta
.lat());
1076 /// Invalidate and redraw.
1077 void tcElevationOptimization::invalidateAndRedrawSlot()
1080 m_configWidget
->requestRedraw();
1083 /// Initialise snapshotting.
1084 void tcElevationOptimization::initSnapShotting()
1086 m_snapshotReadFile
= QString();
1087 m_snapshotSlider
->setEnabled(false);
1088 m_snapshotFile
= QFileDialog::getSaveFileName(m_configWidget
,
1089 tr("Save snapshotting config file"),
1091 tr("Snapshot Config Files (*.sdf)"));
1093 if (!m_snapshotFile
.isEmpty())
1095 // Open the file ready to write the data
1096 QFile
file(m_snapshotFile
);
1097 if (!file
.open(QIODevice::WriteOnly
))
1099 QMessageBox::warning(m_configWidget
,
1101 tr("Could not open '%1' for writing.").arg(m_snapshotFile
));
1105 const double* portion
= portionPosition();
1106 tcGeo
p1(imagery()->texToGeo()*maths::Vector
<2,double>(portion
[0], portion
[1]));
1107 tcGeo
p2(imagery()->texToGeo()*maths::Vector
<2,double>(portion
[2], portion
[3]));
1109 QDataStream
stream(&file
);
1110 stream
.setByteOrder(QDataStream::LittleEndian
);
1111 stream
<< p1
.lon()*180.0/M_PI
1112 << p1
.lat()*180.0/M_PI
1113 << p2
.lon()*180.0/M_PI
1114 << p2
.lat()*180.0/M_PI
;
1119 /// Load a sequence of snapshots.
1120 void tcElevationOptimization::loadSnapShots()
1122 m_snapshotFile
= QString();
1123 m_snapshotReadFile
= QFileDialog::getOpenFileName(m_configWidget
,
1124 tr("Open snapshotting config file"),
1126 tr("Snapshot Config Files (*.sdf)"));
1128 if (!m_snapshotReadFile
.isEmpty())
1130 // Open the file ready to read the data
1131 QFile
file(m_snapshotReadFile
);
1132 if (!file
.open(QIODevice::ReadOnly
| QIODevice::Text
))
1134 QMessageBox::warning(m_configWidget
,
1136 tr("Could not open '%1' for reading.").arg(m_snapshotReadFile
));
1140 QDataStream
stream(&file
);
1141 stream
.setByteOrder(QDataStream::LittleEndian
);
1143 double portion
[2][2];
1144 stream
>> portion
[0][0]
1148 tcGeo
p1(portion
[0][0]*M_PI
/180.0, portion
[0][1]*M_PI
/180.0);
1149 tcGeo
p2(portion
[1][0]*M_PI
/180.0, portion
[1][1]*M_PI
/180.0);
1151 // force update of region
1152 m_configWidget
->requestSlice(p1
, p2
);
1154 // Find number of snaps
1161 QString fname
= QString("%1.%2").arg(m_snapshotReadFile
).arg(max
, 4, 10, QLatin1Char('0'));
1162 snap
.setFileName(fname
);
1163 exists
= snap
.exists();
1166 m_snapshotSlider
->setRange(1, max
);
1167 m_snapshotSlider
->setEnabled(true);
1169 // load first sample
1170 clearStdDeviations();
1171 m_snapshotReading
= true;
1172 for (int i
= 1; i
<= max
; ++i
)
1176 m_snapshotReading
= false;
1178 m_snapshotSlider
->setValue(max
);
1182 m_snapshotSlider
->setEnabled(false);
1186 /// Update weight labels.
1187 void tcElevationOptimization::updateWeightLabels()
1189 for (int i
= 0; i
< WeightsMax
; ++i
)
1191 m_weightLabels
[i
]->setText(QString("%1 -> %2")
1192 .arg(m_weightLimits
[0][i
] + m_weightLimits
[1][i
]*m_weightSliders
[0][i
]->value())
1193 .arg(m_weightLimits
[0][i
] + m_weightLimits
[1][i
]*m_weightSliders
[1][i
]->value())
1199 void tcElevationOptimization::saveWeights()
1201 QString filename
= QFileDialog::getSaveFileName(m_configWidget
,
1204 tr("Elevation optimization weights (*.eow)"));
1206 if (!filename
.isEmpty())
1208 // Open the file ready to write the data
1209 QFile
file(filename
);
1210 if (!file
.open(QIODevice::WriteOnly
| QIODevice::Text
))
1212 QMessageBox::warning(m_configWidget
,
1214 tr("Could not open '%1' for writing.").arg(filename
));
1218 QTextStream
stream(&file
);
1219 for (int w
= 0; w
< WeightsMax
; ++w
)
1221 float start
= m_weightLimits
[0][w
] + m_weightLimits
[1][w
]*m_weightSliders
[0][w
]->value();
1222 float end
= m_weightLimits
[0][w
] + m_weightLimits
[1][w
]*m_weightSliders
[1][w
]->value();
1223 stream
<< start
<< "\t" << end
<< "\n";
1229 void tcElevationOptimization::loadWeights()
1231 QString filename
= QFileDialog::getOpenFileName(m_configWidget
,
1234 tr("Elevation optimization weights (*.eow)"));
1236 if (!filename
.isEmpty())
1238 // Open the file ready to write the data
1239 QFile
file(filename
);
1240 if (!file
.open(QIODevice::ReadOnly
| QIODevice::Text
))
1242 QMessageBox::warning(m_configWidget
,
1244 tr("Could not open '%1' for reading.").arg(filename
));
1248 QTextStream
stream(&file
);
1249 float starts
[WeightsMax
], ends
[WeightsMax
];
1250 for (int w
= 0; w
< WeightsMax
; ++w
)
1252 stream
>> starts
[w
] >> ends
[w
];
1255 if (w
== WeightsMax
-1)
1257 // Go back to shading shifting the values
1258 while (w
> PhotometricStereo
)
1260 starts
[w
] = starts
[w
-1];
1261 ends
[w
] = ends
[w
-1];
1264 starts
[w
] = ends
[w
] = 0.0f
;
1269 starts
[w
] = ends
[w
] = 0.0f
;
1270 QMessageBox::warning(m_configWidget
,
1272 tr("Not enough weights"));
1276 for (int w
= 0; w
< WeightsMax
; ++w
)
1278 m_weightSliders
[0][w
]->setValue((starts
[w
] - m_weightLimits
[0][w
]) / m_weightLimits
[1][w
]);
1279 m_weightSliders
[1][w
]->setValue((ends
[w
] - m_weightLimits
[0][w
]) / m_weightLimits
[1][w
]);
1284 /// Load a single snapshot.
1285 void tcElevationOptimization::loadSnapShot(int id
)
1287 QString counter
= "-";
1289 if (!m_snapshotReadFile
.isEmpty())
1291 QString fname
= QString("%1.%2").arg(m_snapshotReadFile
).arg(id
, 4, 10, QLatin1Char('0'));
1293 if (snap
.open(QIODevice::ReadOnly
))
1295 QDataStream
stream(&snap
);
1296 stream
.setByteOrder(QDataStream::LittleEndian
);
1298 stream
>> width
>> height
;
1299 if (m_elevationData
->width() != width
|| m_elevationData
->height() != height
)
1301 QMessageBox::warning(m_configWidget
,
1302 tr("Snapshot dimentions do not match with current."),
1308 for (int j
= 0; j
< height
; ++j
)
1310 for (int i
= 0; i
< width
; ++i
)
1314 stream
>> alt
>> orig
;
1315 m_datum
->buffer()[index
].reliability
= !(alt
& 0x8000);
1316 m_datum
->buffer()[index
].originalElevation
= orig
;
1318 m_elevationData
->buffer()[index
] = alt
;
1322 sampleStdDeviations();
1323 if (!m_snapshotReading
)
1326 m_configWidget
->requestRedraw();
1328 counter
= QString("%1").arg(id
);
1332 m_snapshotCounter
->setText(counter
);
1336 * Interface for derived class to implement
1339 void tcElevationOptimization::roundPortion(double* x1
, double* y1
, double* x2
, double* y2
)
1341 //m_shadowChannel->roundPortion(x1,y1,x2,y2);
1344 tcAbstractPixelData
* tcElevationOptimization::loadPortion(double x1
, double y1
, double x2
, double y2
, bool changed
)
1349 for (int i
= 0; i
< m_numDatasets
; ++i
)
1351 maths::Vector
<2,double> xy1
= m_texToDatasetTex
[i
] * maths::Vector
<2,double>(x1
,y1
);
1352 maths::Vector
<2,double> xy2
= m_texToDatasetTex
[i
] * maths::Vector
<2,double>(x2
,y2
);
1353 Reference
<tcAbstractPixelData
> channelData
= m_shadowChannels
[i
]->portion(xy1
[0], xy1
[1], xy2
[0], xy2
[1]);
1356 width
= channelData
->width();
1357 height
= channelData
->height();
1359 m_shadowData
[i
] = dynamicCast
<tcPixelData
<float>*>(channelData
);
1360 if (0 == m_shadowData
[i
])
1364 if (0 != m_shadingChannels
[i
])
1366 Reference
<tcAbstractPixelData
> shadingData
= m_shadingChannels
[i
]->portion(xy1
[0], xy1
[1], xy2
[0], xy2
[1]);
1367 m_shadingData
[i
] = dynamicCast
<tcPixelData
<float>*>(shadingData
);
1371 m_shadingData
[i
] = 0;
1375 /// @todo What if its a different size because the portion has changed?
1376 if (0 == m_elevationData
|| 0 == m_datum
|| changed
)
1378 delete m_elevationData
;
1379 for (int i
= 0; i
< m_numDatasets
; ++i
)
1385 // Create a new pixel buffer, initialised to altitude
1387 m_elevationData
= new tcElevationData(width
, height
);
1388 // Find transform of elevation data
1389 tcGeo geoBase
= geoAt(maths::Vector
<2,float>(x1
, y1
));
1390 tcGeo geoDiagonal
= geoAt(maths::Vector
<2,float>(x1
+ (x2
-x1
)/(width
-1), y1
+ (y2
-y1
)/(height
-1))) - geoBase
;
1391 m_elevationData
->setGeoToPixels(tcAffineTransform2
<double>(geoBase
, geoDiagonal
).inverse());
1393 m_datum
= new tcTypedPixelData
<PerPixelCache
>(width
, height
);
1394 for (int i
= 0; i
< m_numDatasets
; ++i
)
1396 m_data
[i
] = new tcTypedPixelData
<PerDatasetPixelCache
>(width
, height
);
1399 int maxWidthHeight
= qMax(width
, height
);
1400 double minXY12
= qMin(x2
-x1
, y2
-y1
);
1402 startProcessing("Caching elevation characteristics");
1403 for (int j
= 0; j
< height
; ++j
)
1405 progress((float)j
/(height
-1));
1406 for (int i
= 0; i
< width
; ++i
)
1408 int index
= j
*width
+ i
;
1409 // Transform coordinates
1410 maths::Vector
<2,float> coord (x1
+ (x2
-x1
)*i
/ (width
- 1),
1411 y1
+ (y2
-y1
)*j
/ (height
- 1));
1412 tcGeo geoCoord
= geoAt(coord
);
1414 PerPixelCache
* cache
= &m_datum
->buffer()[index
];
1417 maths::Vector
<2,float> coordx (x1
+ (x2
-x1
)*(i
+1) / (width
- 1),
1418 y1
+ (y2
-y1
)*j
/ (height
- 1));
1419 maths::Vector
<2,float> coordy (x1
+ (x2
-x1
)*i
/ (width
- 1),
1420 y1
+ (y2
-y1
)*(j
+1) / (height
- 1));
1421 tcGeo geoCoordx
= geoAt(coordx
) - geoCoord
;
1422 tcGeo geoCoordy
= geoAt(coordy
) - geoCoord
;
1424 float res
= geoCoordx
.angle();
1425 cache
->resolution
[0] = res
*cos(geoCoord
.lat());
1426 cache
->resolution
[1] = geoCoordy
.angle();
1427 cache
->resolution
*= 6378.137e3
;
1429 // Get some elevation data
1431 float altitude
= altitudeAt(geoCoord
, true, &accurate
);
1432 m_elevationData
->buffer()[index
] = altitude
;
1433 cache
->originalElevation
= altitude
;
1434 cache
->reliability
= accurate
? 1 : 0;
1435 cache
->accurateElevation
= dem()[1].altitudeAt(geoCoord
, true, 0);
1437 // Per dataset cache
1438 for (int ds
= 0; ds
< m_numDatasets
; ++ds
)
1440 tcGeoImageData
* imagery
= m_datasets
[ds
];
1441 maths::Vector
<3,float> light
= lightDirectionAt(geoCoord
, imagery
);
1443 PerDatasetPixelCache
* dsCache
= &m_data
[ds
]->buffer()[index
];
1444 dsCache
->light
= light
;
1445 dsCache
->lxy
= light
.slice
<0,2>().mag();
1446 dsCache
->dlz_dlxy
= light
[2]/dsCache
->lxy
;
1448 tcGeo
lightScaled(light
[0]*res
/sin(geoCoord
.lat()),
1450 tcGeo
offset(geoCoord
+ lightScaled
);
1451 for (int depthDirection
= 0; depthDirection
< 2; ++depthDirection
)
1453 tcGeo pos
= geoCoord
;
1455 if (depthDirection
== TransitionEntrance
)
1457 delta
= offset
- geoCoord
;
1461 delta
= geoCoord
- offset
;
1465 maths::Vector
<2,float> tex
= coord
;
1466 int transitionI
= i
;
1467 int transitionJ
= j
;
1468 while (transitionI
>= 0 && transitionI
< width
&& transitionJ
>= 0 && transitionJ
< height
)
1470 if (m_shadowData
[ds
]->sampleFloat((tex
[0]-x1
)/(x2
-x1
), (tex
[1]-y1
)/(y2
-y1
)) > 0.5f
)
1474 transitionI
= 0.5f
+ (tex
[0]-x1
)/(x2
-x1
)*(width
-1);
1475 transitionJ
= 0.5f
+ (tex
[1]-y1
)/(y2
-y1
)*(height
-1);
1477 tex
= textureAt(pos
);
1479 dsCache
->shadowTransition
[depthDirection
][0] = transitionI
;
1480 dsCache
->shadowTransition
[depthDirection
][1] = transitionJ
;
1481 dsCache
->shadowTransitionDistance
[depthDirection
] = tcGeo::angleBetween(pos
,geoCoord
)*6378.137e3
;
1487 * matrix L={Light row vec of shading channel i;..}
1489 * multiply by shading values for shading channels
1490 * should be mostly about the same magnitude
1493 // Estimate a normal from the shading channels
1494 maths::Vector
<3,float> totalN(0.0f
, 0.0f
, 0.01f
);
1497 for (ds
[0] = 0; ds
[0] < m_numDatasets
; ++ds
[0])
1499 if (0 == m_shadingData
[ds
[0]])
1503 for (ds
[1] = ds
[0]+1; ds
[1] < m_numDatasets
; ++ds
[1])
1505 if (0 == m_shadingData
[ds
[1]])
1509 for (ds
[2] = ds
[1]+1; ds
[2] < m_numDatasets
; ++ds
[2])
1511 if (0 == m_shadingData
[ds
[2]])
1515 maths::Matrix
<3,float> L(m_data
[ds
[0]]->buffer()[index
].light
,
1516 m_data
[ds
[1]]->buffer()[index
].light
,
1517 m_data
[ds
[2]]->buffer()[index
].light
);
1518 maths::Vector
<3,float> S
;
1519 bool singular
= false;
1520 L
.invert(&singular
);
1525 for (int dsi
= 0; dsi
< 3; ++dsi
)
1527 S
[dsi
] = m_shadingData
[ds
[dsi
]]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1));
1529 maths::Vector
<3,float> N
= S
* L
;
1535 cache
->normalEstimate
= totalN
;
1536 cache
->normalEstimate
.normalize();
1542 if (!m_loadingFromDem
)
1544 // Update elevation model
1545 startProcessing("Updating elevation model");
1546 dem()->updateFromElevationData(m_elevationData
);
1549 // Don't bother outputting anything
1550 //return new tcTypedPixelData<unsigned char>(1,1);
1552 // Downscale the elevation for viewing
1553 startProcessing("Downscaling for viewing");
1554 tcPixelData
<GLubyte
>* result
= new tcPixelData
<GLubyte
>(width
, height
);
1555 //tcTypedPixelData<maths::Vector<3,float> >* result = new tcTypedPixelData<maths::Vector<3,float> >(width, height);
1556 Q_ASSERT(0 != result
);
1559 for (int j
= 0; j
< height
; ++j
)
1561 progress((float)j
/(height
-1));
1562 for (int i
= 0; i
< width
; ++i
)
1565 PerPixelCache
* cache
= &m_datum
->buffer()[index
];
1567 result
->buffer()[index
] = 255 * cache
->reliability
;
1576 if (i
> 0 && i
< width
-1)
1578 float hp1
= m_elevationData
->buffer()[index
+1];
1579 float hm1
= m_elevationData
->buffer()[index
-1];
1580 dh_dx
= (hp1
-hm1
)/2 / cache
->resolution
[0];
1582 if (j
> 0 && j
< height
-1)
1584 float hp1
= m_elevationData
->buffer()[index
+width
];
1585 float hm1
= m_elevationData
->buffer()[index
-width
];
1586 dh_dy
= (hp1
-hm1
)/2 / cache
->resolution
[1];
1588 // (0.0f, -1.0f, dh_dy) x (1.0f, 0.0f, dh_dx)
1589 maths::Vector
<3,float> norm(-dh_dx
, dh_dy
, 1.0f
);
1591 result
->buffer()[index
] = norm
;
1596 for (int ds
= 0; ds
< m_numDatasets
; ++ds
)
1598 result
->buffer()[index
] = 0;
1599 PerDatasetPixelCache
* dsCache
= &m_data
[ds
]->buffer()[index
];
1601 float hwm1
= m_elevationData
->sampleFloat(
1602 ((float)i
+ dsCache
->light
[0]/cache
->resolution
[0])/(width
-1),
1603 ((float)j
- dsCache
->light
[1]/cache
->resolution
[1])/(height
-1)
1605 float hwp1
= m_elevationData
->sampleFloat(
1606 ((float)i
- dsCache
->light
[0]/cache
->resolution
[0])/(width
-1),
1607 ((float)j
+ dsCache
->light
[1]/cache
->resolution
[1])/(height
-1)
1609 float dh_dw
= (hwm1
- hwp1
)/(2*dsCache
->lxy
);
1610 float facingness
= dsCache
->dlz_dlxy
-dh_dw
;
1611 // if (facingness < 0.0f)
1612 // result->buffer()[index] = 128.0f;
1614 result
->buffer()[index
] = 128.0f
* facingness
*facingness
;
1616 result
->buffer()[index
] = 0;
1618 bool isShadowed
= (m_shadowData
[ds
]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1)) < 0.5f
);
1619 bool isShadowEntrance
= isShadowed
&&
1620 dsCache
->shadowTransition
[TransitionEntrance
][0] == i
&&
1621 dsCache
->shadowTransition
[TransitionEntrance
][1] == j
;
1622 bool isShadowExit
= isShadowed
&&
1623 dsCache
->shadowTransition
[TransitionExit
][0] == i
&&
1624 dsCache
->shadowTransition
[TransitionExit
][1] == j
;
1625 float elevation
= m_elevationData
->buffer()[index
];
1626 result
->buffer()[index
] = (elevation
- 2500.0f
)/1500.0f
*255.0f
;
1628 if (false && isShadowed
)
1630 // maximum elevation is between elevation at entrance and elevation at exit
1631 if (m_weights
[BelowShadowLine
] != 0.0f
&&
1632 dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
1633 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
1634 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
1635 dsCache
->shadowTransition
[TransitionExit
][1] < height
&&
1636 dsCache
->shadowTransition
[TransitionEntrance
][0] >= 0 &&
1637 dsCache
->shadowTransition
[TransitionEntrance
][0] < width
&&
1638 dsCache
->shadowTransition
[TransitionEntrance
][1] >= 0 &&
1639 dsCache
->shadowTransition
[TransitionEntrance
][1] < height
)
1641 int exitIndex
= width
*dsCache
->shadowTransition
[TransitionExit
][1] + dsCache
->shadowTransition
[TransitionExit
][0];
1642 int entranceIndex
= width
*dsCache
->shadowTransition
[TransitionEntrance
][1] + dsCache
->shadowTransition
[TransitionEntrance
][0];
1643 float exitElevation
= m_elevationData
->buffer()[exitIndex
];
1644 float entranceElevation
= m_elevationData
->buffer()[entranceIndex
];
1646 result
->buffer()[index
] = qMax(0.0f
, elevation
- (exitElevation
* dsCache
->shadowTransitionDistance
[TransitionEntrance
]
1647 +entranceElevation
* dsCache
->shadowTransitionDistance
[TransitionExit
])
1648 / (dsCache
->shadowTransitionDistance
[TransitionEntrance
]
1649 +dsCache
->shadowTransitionDistance
[TransitionExit
]));
1653 result
->buffer()[index
] = 128.0f
*m_shadingData
[ds
]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1));
1656 int xdist
= abs(dsCache
->shadowTransition
[TransitionEntrance
][0] - i
);
1657 int ydist
= abs(dsCache
->shadowTransition
[TransitionEntrance
][1] - j
);
1658 bool bothIn
= isShadowed
&& (dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
1659 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
1660 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
1661 dsCache
->shadowTransition
[TransitionExit
][1] < height
&&
1662 dsCache
->shadowTransition
[TransitionEntrance
][0] >= 0 &&
1663 dsCache
->shadowTransition
[TransitionEntrance
][0] < width
&&
1664 dsCache
->shadowTransition
[TransitionEntrance
][1] >= 0 &&
1665 dsCache
->shadowTransition
[TransitionEntrance
][1] < height
);
1667 if (isShadowEntrance
&& (dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
1668 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
1669 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
1670 dsCache
->shadowTransition
[TransitionExit
][1] < height
))
1672 int exitIndex
= width
*dsCache
->shadowTransition
[TransitionExit
][1] + dsCache
->shadowTransition
[TransitionExit
][0];
1674 float exitElevation
= m_elevationData
->buffer()[exitIndex
];
1675 float diff
= exitElevation
+ dsCache
->shadowTransitionDistance
[TransitionExit
]*dsCache
->dlz_dlxy
- elevation
;
1676 result
->buffer()[index
] += diff
/5000.0f
*255.0f
;
1680 result
->buffer()[index
] = 0;
1686 //result->buffer()[index] = isShadowed ? qMin(255, 100 * (xdist + ydist)) : 255;
1687 /*result->buffer()[index] = (isShadowed && (dsCache->shadowTransition[TransitionExit][0] >= 0 &&
1688 dsCache->shadowTransition[TransitionExit][0] < width &&
1689 dsCache->shadowTransition[TransitionExit][1] >= 0 &&
1690 dsCache->shadowTransition[TransitionExit][1] < height))?255:0;*/
1691 //float mycostChange = 100000.0f*255.0f*costChange(i, j, +4.0f, 0);
1692 //result->buffer()[index] = 128.0f-mycostChange;
1693 //result->buffer()[index] = m_datum->buffer()[index].reliability*255;
1698 result
->buffer()[index
] = cache
->normalEstimate
;
1712 template <typename T
>
1723 /// Calculate the cost for a set of pixels.
1724 float tcElevationOptimization::cost(int x1
, int y1
, int x2
, int y2
) const
1726 int width
= m_elevationData
->width();
1727 int height
= m_elevationData
->height();
1745 int maxWidthHeight
= qMax(width
, height
);
1746 //double minXY12 = qMin(m_window.x2-m_window.x1, m_window.y2-m_window.y1);
1748 float costVariance
= 0.0f
;
1749 int countVariance
= 0;
1750 float costRoughness
= 0.0f
;
1751 int countRoughness
= 0;
1752 float costSteepness
= 0.0f
;
1753 int countSteepness
= 0;
1754 float costLitFacing
= 0.0f
;
1755 int countLitFacing
= 0;
1756 float costShading
= 0.0f
;
1757 int countShading
= 0;
1758 float costStereo
= 0.0f
;
1759 int countStereo
= 0;
1760 float costBelowShadowLine
= 0.0f
;
1761 int countBelowShadowLine
= 0;
1762 float costTransitionElevTop
= 0.0f
;
1763 int countTransitionElevTop
= 0;
1764 float costTransitionElevBottom
= 0.0f
;
1765 int countTransitionElevBottom
= 0;
1766 float costTransitionTangential
= 0.0f
;
1767 int countTransitionTangential
= 0;
1768 float costTransitionCurvingDown
= 0.0f
;
1769 int countTransitionCurvingDown
= 0;
1770 for (int j
= y1
; j
<= y2
; ++j
)
1772 for (int i
= x1
; i
<= x2
; ++i
)
1774 float* elevationPtr
= pixelElevation(i
, j
);
1775 if (0 == elevationPtr
)
1780 int index
= j
*width
+ i
;
1782 // Basic data retrieval
1783 float elevation
= *elevationPtr
;
1784 PerPixelCache
* cache
= &m_datum
->buffer()[index
];
1785 if (m_voidsOnly
&& cache
->reliability
== 0)
1791 // Derivitive of gradient
1794 float d2h_dx2
= 0.0f
;
1795 float d2h_dy2
= 0.0f
;
1796 if (i
> 0 && i
< width
-1)
1798 float hp1
= m_elevationData
->buffer()[index
+1];
1799 float hm1
= m_elevationData
->buffer()[index
-1];
1800 d2h_dx2
= (hp1
+hm1
)/2 - elevation
;
1801 dh_dx
= (hp1
-hm1
)/2 / cache
->resolution
[0];
1803 if (j
> 0 && j
< height
-1)
1805 float hp1
= m_elevationData
->buffer()[index
+width
];
1806 float hm1
= m_elevationData
->buffer()[index
-width
];
1807 d2h_dy2
= (hp1
+hm1
)/2 - elevation
;
1808 dh_dy
= (hp1
-hm1
)/2 / cache
->resolution
[1];
1811 // Minimise variance from original value
1812 if (m_weights
[Variance
] != 0.0f
)
1814 // treat borderline pixels as reliable
1815 float reliability
= cache
->reliability
;
1816 if (i
< 5 || i
>= width
-5 || j
< 5 || j
>= height
-5)
1818 reliability
= 10.0f
;
1820 costVariance
+= reliability
* (elevation
-cache
->originalElevation
)*(elevation
-cache
->originalElevation
);
1824 // Minimise roughness
1825 if (m_weights
[Roughness
] != 0.0f
)
1827 costRoughness
+= (d2h_dx2
*d2h_dx2
+ d2h_dy2
*d2h_dy2
);
1831 // Also minimise slope
1832 // This works really well at getting a nice slope
1833 if (m_weights
[Steepness
] != 0.0f
)
1835 costSteepness
+= (dh_dx
*dh_dx
+ dh_dy
*dh_dy
);
1839 // Photometric stereo
1840 if (cache
->normalEstimate
[2] > 0.2f
&& m_weights
[PhotometricStereo
] != 0.0f
)
1842 // (0.0f, -1.0f, dh_dy) x (1.0f, 0.0f, dh_dx)
1843 maths::Vector
<3,float> norm(-dh_dx
, dh_dy
, 1.0f
);
1845 costStereo
+= MySqr(acos(cache
->normalEstimate
*norm
));
1846 //costStereo += MySqr(dh_dx - cache->normalEstimate[0]/cache->normalEstimate[2]);
1847 //costStereo += MySqr(dh_dy - cache->normalEstimate[1]/cache->normalEstimate[2]);
1852 for (int ds
= 0; ds
< m_numDatasets
; ++ds
)
1854 PerDatasetPixelCache
* dsCache
= &m_data
[ds
]->buffer()[index
];
1856 bool isShadowed
= (m_shadowData
[ds
]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1)) < 0.5f
);
1857 bool isShadowEntrance
= isShadowed
&&
1858 dsCache
->shadowTransition
[TransitionEntrance
][0] == i
&&
1859 dsCache
->shadowTransition
[TransitionEntrance
][1] == j
;
1860 bool isShadowExit
= isShadowed
&&
1861 dsCache
->shadowTransition
[TransitionExit
][0] == i
&&
1862 dsCache
->shadowTransition
[TransitionExit
][1] == j
;
1864 // Calculate measures relating to the light direction
1866 // Two sample points are 2*dsCache->lxy apart
1867 float hwm1
= m_elevationData
->sampleFloat(
1868 ((float)i
+ dsCache
->light
[0]/cache
->resolution
[0])/(width
-1),
1869 ((float)j
- dsCache
->light
[1]/cache
->resolution
[1])/(height
-1)
1871 float hwp1
= m_elevationData
->sampleFloat(
1872 ((float)i
- dsCache
->light
[0]/cache
->resolution
[0])/(width
-1),
1873 ((float)j
+ dsCache
->light
[1]/cache
->resolution
[1])/(height
-1)
1875 float dh_dw
= (hwm1
- hwp1
)/(2*dsCache
->lxy
);
1876 float d2h_dw2
= (hwm1
+hwp1
)/2 - elevation
;
1877 float facingness
= dsCache
->dlz_dlxy
-dh_dw
;
1881 // maximum elevation is between elevation at entrance and elevation at exit
1882 if (m_weights
[BelowShadowLine
] != 0.0f
&&
1883 dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
1884 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
1885 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
1886 dsCache
->shadowTransition
[TransitionExit
][1] < height
&&
1887 dsCache
->shadowTransition
[TransitionEntrance
][0] >= 0 &&
1888 dsCache
->shadowTransition
[TransitionEntrance
][0] < width
&&
1889 dsCache
->shadowTransition
[TransitionEntrance
][1] >= 0 &&
1890 dsCache
->shadowTransition
[TransitionEntrance
][1] < height
)
1892 int exitIndex
= width
*dsCache
->shadowTransition
[TransitionExit
][1] + dsCache
->shadowTransition
[TransitionExit
][0];
1893 int entranceIndex
= width
*dsCache
->shadowTransition
[TransitionEntrance
][1] + dsCache
->shadowTransition
[TransitionEntrance
][0];
1894 float exitElevation
= m_elevationData
->buffer()[exitIndex
];
1895 float entranceElevation
= m_elevationData
->buffer()[entranceIndex
];
1897 costBelowShadowLine
+= MySqr(qMax(0.0f
, elevation
- (exitElevation
* dsCache
->shadowTransitionDistance
[TransitionEntrance
]
1898 +entranceElevation
* dsCache
->shadowTransitionDistance
[TransitionExit
])
1899 / (dsCache
->shadowTransitionDistance
[TransitionEntrance
]
1900 +dsCache
->shadowTransitionDistance
[TransitionExit
])));
1901 ++countBelowShadowLine
;
1906 if (facingness
< 0.0f
&& m_weights
[LitFacing
] != 0.0f
)
1908 // Lit pixels must face light
1909 costLitFacing
+= facingness
*facingness
;
1912 // Simple shape from shading
1913 if (0 != m_shadingData
[ds
] && m_weights
[Shading
] != 0.0f
)
1915 float intensity
= m_shadingData
[ds
]->sampleFloat((float)i
/(width
-1), (float)j
/(height
-1));
1916 // intensity = cos(theta) = L.N
1917 // (0.0f, -1.0f, dh_dy) x (1.0f, 0.0f, dh_dx)
1918 maths::Vector
<3,float> norm(-dh_dx
, dh_dy
, 1.0f
);
1920 costShading
+= MySqr(acos(qMin(1.0f
, intensity
)) - acos(qMin(1.0f
, dsCache
->light
*norm
)));
1924 if (isShadowEntrance
)
1926 if (m_weights
[TransitionElevTop
] != 0.0f
&&
1927 dsCache
->shadowTransition
[TransitionExit
][0] >= 0 &&
1928 dsCache
->shadowTransition
[TransitionExit
][0] < width
&&
1929 dsCache
->shadowTransition
[TransitionExit
][1] >= 0 &&
1930 dsCache
->shadowTransition
[TransitionExit
][1] < height
)
1932 int exitIndex
= width
*dsCache
->shadowTransition
[TransitionExit
][1] + dsCache
->shadowTransition
[TransitionExit
][0];
1933 float exitElevation
= m_elevationData
->buffer()[exitIndex
];
1934 //if (dsCache->shadowTransitionDistance[TransitionExit]*dsCache->dlz_dlxy > 100.0f)
1936 costTransitionElevTop
+= MySqr(exitElevation
+ dsCache
->shadowTransitionDistance
[TransitionExit
]*dsCache
->dlz_dlxy
- elevation
);
1937 ++countTransitionElevTop
;
1940 // gradient tangential to sun direction
1941 if (m_weights
[TransitionTangential
] != 0.0f
)
1943 costTransitionTangential
+= MySqr(facingness
);
1944 ++countTransitionTangential
;
1946 // second derivitive should be negative at entrance
1947 if (m_weights
[TransitionCurvingDown
] != 0.0f
)
1949 costTransitionCurvingDown
+= MySqr(qMax(0.0f
, d2h_dw2
));
1950 ++countTransitionCurvingDown
;
1955 if (m_weights
[TransitionElevBottom
] != 0.0f
&&
1956 dsCache
->shadowTransition
[TransitionEntrance
][0] >= 0 &&
1957 dsCache
->shadowTransition
[TransitionEntrance
][0] < width
&&
1958 dsCache
->shadowTransition
[TransitionEntrance
][1] >= 0 &&
1959 dsCache
->shadowTransition
[TransitionEntrance
][1] < height
)
1961 int entranceIndex
= width
*dsCache
->shadowTransition
[TransitionEntrance
][1] + dsCache
->shadowTransition
[TransitionEntrance
][0];
1962 float entranceElevation
= m_elevationData
->buffer()[entranceIndex
];
1963 // this has less effect, its usually "more" right
1964 /// @todo Smooth high reliability into low reliability
1965 costTransitionElevBottom
+= 1.0f
* MySqr(entranceElevation
- dsCache
->shadowTransitionDistance
[TransitionEntrance
]*dsCache
->dlz_dlxy
- elevation
);
1966 ++countTransitionElevBottom
;
1973 return m_weights
[Variance
]*costVariance
//countVariance
1974 + m_weights
[Roughness
]*costRoughness
//countRoughness
1975 + m_weights
[Steepness
]*costSteepness
//countSteepness
1976 + m_weights
[LitFacing
]*costLitFacing
//countLitFacing
1977 + m_weights
[Shading
]*costShading
//countShading
1978 + m_weights
[PhotometricStereo
]*costStereo
//countStereo
1979 + m_weights
[BelowShadowLine
]*costBelowShadowLine
//countBelowShadowLine
1980 + m_weights
[TransitionElevTop
]*costTransitionElevTop
//countTransitionElevTop
1981 + m_weights
[TransitionElevBottom
]*costTransitionElevBottom
//countTransitionElevBottom
1982 + m_weights
[TransitionTangential
]*costTransitionTangential
//countTransitionTangential
1983 + m_weights
[TransitionCurvingDown
]*costTransitionCurvingDown
//countTransitionCurvingDown
1990 * f(x) = exp[ - (2*x/range)^2 ]
1992 inline float elevationFalloffFunction(float xsq
)
1994 return qMax(0.0f
, 1.0f
- xsq
);
1995 //return exp(-4*xsq);
2000 /// Calculate the relative cost of changing a pixel's elevation.
2001 float tcElevationOptimization::costChange(int x
, int y
, float dh
, int range
)
2003 // Override range for now
2004 #define ELEV_RANGE 4
2005 range
= qMin(range
, ELEV_RANGE
);
2006 static const int effectiveRange
= 1 + range
;
2007 float oldElevations
[1+2*ELEV_RANGE
][1+2*ELEV_RANGE
];
2009 // Assume a single change in elevation only affects cost of pixels to effectiveRange from it.
2010 // Now technically this doesn't hold for shadow edges as all pixels across the shadow can be affected.
2011 // If the pixel is a shadow edge, take into account the cost of extra pixels.
2012 float currentCost
= cost(x
-effectiveRange
, y
-effectiveRange
, x
+effectiveRange
, y
+effectiveRange
);
2013 for (int i
= 0; i
< 1+range
*2; ++i
)
2016 for (int j
= 0; j
< 1+range
*2; ++j
)
2019 float* elev
= pixelElevation(x
+di
, y
+dj
);
2020 if (0 != elev
&& (!m_voidsOnly
|| 0 != m_datum
->buffer()[j
*m_datum
->width() + i
].reliability
))
2022 oldElevations
[i
][j
] = *elev
;
2023 *elev
+= dh
*elevationFalloffFunction(float(di
*di
+ dj
*dj
) / ((range
+1)*(range
+1)));
2027 float newCost
= cost(x
-effectiveRange
, y
-effectiveRange
, x
+effectiveRange
, y
+effectiveRange
);
2028 for (int i
= 0; i
< 1+range
*2; ++i
)
2031 for (int j
= 0; j
< 1+range
*2; ++j
)
2034 float* elev
= pixelElevation(x
+di
, y
+dj
);
2035 if (0 != elev
&& (!m_voidsOnly
|| 0 != m_datum
->buffer()[j
*m_datum
->width() + i
].reliability
))
2037 *elev
= oldElevations
[i
][j
];
2041 return newCost
- currentCost
;
2044 /// Optimize a whole range of elevations.
2045 void tcElevationOptimization::optimizeElevations(int x1
, int y1
, int x2
, int y2
, float dh
, int range
)
2047 for (int j
= y1
; j
<= y2
; ++j
)
2049 for (int i
= x1
; i
<= x2
; ++i
)
2051 if (m_stopProcessing
)
2055 int index
= j
*m_datum
->width() + i
;
2057 if (!m_voidsOnly
|| 0 != m_datum
->buffer()[j
*m_datum
->width() + i
].reliability
)
2059 // Find the cost of adjusting the elevation by dh in each direction
2060 float costInc
= costChange(i
,j
, dh
, range
);
2061 float costDec
= costChange(i
,j
,-dh
, range
);
2062 if (costInc
< 0.0f
&& costInc
< costDec
)
2064 adjustElevation(i
,j
,dh
, range
);
2066 else if (costDec
< 0.0f
)
2068 adjustElevation(i
,j
,-dh
, range
);
2079 /// Get pointer to elevation at a pixel.
2080 float* tcElevationOptimization::pixelElevation(int x
, int y
) const
2082 if (x
< 0 || x
>= m_elevationData
->width() ||
2083 y
< 0 || y
>= m_elevationData
->height())
2087 int index
= y
*m_elevationData
->width() + x
;
2088 return &(m_elevationData
->buffer()[index
]);
2091 /// Adjust elevation at a pixel.
2092 void tcElevationOptimization::adjustElevation(int x
, int y
, float dh
, int range
)
2094 range
= qMin(range
, ELEV_RANGE
);
2095 for (int i
= 0; i
< 1+range
*2; ++i
)
2098 for (int j
= 0; j
< 1+range
*2; ++j
)
2101 float* elev
= pixelElevation(x
+di
, y
+dj
);
2104 *elev
+= dh
*elevationFalloffFunction(float(di
*di
+ dj
*dj
) / ((range
+1)*(range
+1)));