pktools  2.6.4
Processing Kernel for geospatial data
Filter2d.cc
1 /**********************************************************************
2 Filter2d.cc: class for filtering images
3 Copyright (C) 2008-2012 Pieter Kempeneers
4 
5 This file is part of pktools
6 
7 pktools is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
11 
12 pktools is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with pktools. If not, see <http://www.gnu.org/licenses/>.
19 ***********************************************************************/
20 #include <sstream>
21 #include <iomanip>
22 #include <iostream>
23 #include <cmath>
24 #include "Filter2d.h"
25 #include "StatFactory.h"
26 // #include "imageclasses/ImgUtils.h"
27 
28 filter2d::Filter2d::Filter2d(void)
29 {
30 }
31 
32 filter2d::Filter2d::Filter2d(const Vector2d<double> &taps)
33  : m_taps(taps)
34 {
35 }
36 
37 int filter2d::Filter2d::pushNoDataValue(double noDataValue)
38 {
39  if(find(m_noDataValues.begin(),m_noDataValues.end(),noDataValue)==m_noDataValues.end())
40  m_noDataValues.push_back(noDataValue);
41  return(m_noDataValues.size());
42 }
43 
44 void filter2d::Filter2d::setTaps(const Vector2d<double> &taps)
45 {
46  m_taps=taps;
47 }
48 
49 void filter2d::Filter2d::smoothNoData(const ImgReaderGdal& input, ImgWriterGdal& output, int dim)
50 {
51  smoothNoData(input, output,dim,dim);
52 }
53 
54 void filter2d::Filter2d::smooth(const ImgReaderGdal& input, ImgWriterGdal& output, int dim)
55 {
56  smooth(input, output,dim,dim);
57 }
58 
59 void filter2d::Filter2d::smoothNoData(const ImgReaderGdal& input, ImgWriterGdal& output, int dimX, int dimY)
60 {
61  m_taps.resize(dimY);
62  for(int j=0;j<dimY;++j){
63  m_taps[j].resize(dimX);
64  for(int i=0;i<dimX;++i)
65  m_taps[j][i]=1.0;
66  }
67  filter(input,output,false,true,true);
68 }
69 
70 void filter2d::Filter2d::smooth(const ImgReaderGdal& input, ImgWriterGdal& output, int dimX, int dimY)
71 {
72  m_taps.resize(dimY);
73  for(int j=0;j<dimY;++j){
74  m_taps[j].resize(dimX);
75  for(int i=0;i<dimX;++i)
76  m_taps[j][i]=1.0;
77  }
78  filter(input,output,false,true,false);
79 }
80 
81 
82 void filter2d::Filter2d::filter(const ImgReaderGdal& input, ImgWriterGdal& output, bool absolute, bool normalize, bool noData)
83 {
84  int dimX=m_taps[0].size();//horizontal!!!
85  int dimY=m_taps.size();//vertical!!!
86  // byte* tmpbuf=new byte[input.rowSize()];
87  const char* pszMessage;
88  void* pProgressArg=NULL;
89  GDALProgressFunc pfnProgress=GDALTermProgress;
90  double progress=0;
91  pfnProgress(progress,pszMessage,pProgressArg);
92  for(int iband=0;iband<input.nrOfBand();++iband){
93  Vector2d<double> inBuffer(dimY,input.nrOfCol());
94  std::vector<double> outBuffer(input.nrOfCol());
95  int indexI=0;
96  int indexJ=0;
97  //initialize last half of inBuffer
98  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
99  try{
100  input.readData(inBuffer[indexJ],GDT_Float64,abs(j),iband);
101  }
102  catch(std::string errorstring){
103  std::cerr << errorstring << "in line " << indexJ << std::endl;
104  }
105  ++indexJ;
106  }
107 
108  for(int y=0;y<input.nrOfRow();++y){
109  if(y){//inBuffer already initialized for y=0
110  //erase first line from inBuffer
111  if(dimY>1)
112  inBuffer.erase(inBuffer.begin());
113  //read extra line and push back to inBuffer if not out of bounds
114  if(y+dimY/2<input.nrOfRow()){
115  //allocate buffer
116  if(dimY>1)
117  inBuffer.push_back(inBuffer.back());
118  try{
119  input.readData(inBuffer[inBuffer.size()-1],GDT_Float64,y+dimY/2,iband);
120  }
121  catch(std::string errorstring){
122  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
123  }
124  }
125  else{
126  int over=y+dimY/2-input.nrOfRow();
127  int index=(inBuffer.size()-1)-over;
128  assert(index>=0);
129  assert(index<inBuffer.size());
130  inBuffer.push_back(inBuffer[index]);
131  }
132  }
133  for(int x=0;x<input.nrOfCol();++x){
134  outBuffer[x]=0;
135  double norm=0;
136  bool masked=false;
137  if(noData){//only filter noData values
138  for(int imask=0;imask<m_noDataValues.size();++imask){
139  if(inBuffer[(dimY-1)/2][x]==m_noDataValues[imask]){
140  masked=true;
141  break;
142  }
143  }
144  if(!masked){
145  outBuffer[x]=inBuffer[(dimY-1)/2][x];
146  continue;
147  }
148  }
149  assert(!noData||masked);
150  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
151  for(int i=-(dimX-1)/2;i<=dimX/2;++i){
152  indexI=x+i;
153  indexJ=(dimY-1)/2+j;
154  //check if out of bounds
155  if(x<(dimX-1)/2)
156  indexI=x+abs(i);
157  else if(x>=input.nrOfCol()-(dimX-1)/2)
158  indexI=x-abs(i);
159  if(y<(dimY-1)/2)
160  indexJ=(dimY-1)/2+abs(j);
161  else if(y>=input.nrOfRow()-(dimY-1)/2)
162  indexJ=(dimY-1)/2-abs(j);
163  //do not take masked values into account
164  masked=false;
165  for(int imask=0;imask<m_noDataValues.size();++imask){
166  if(inBuffer[indexJ][indexI]==m_noDataValues[imask]){
167  masked=true;
168  break;
169  }
170  }
171  if(!masked){
172  outBuffer[x]+=(m_taps[(dimY-1)/2+j][(dimX-1)/2+i]*inBuffer[indexJ][indexI]);
173  norm+=m_taps[(dimY-1)/2+j][(dimX-1)/2+i];
174  }
175  }
176  }
177  if(absolute)
178  outBuffer[x]=(normalize&&norm)? abs(outBuffer[x])/norm : abs(outBuffer[x]);
179  else if(normalize&&norm!=0)
180  outBuffer[x]=outBuffer[x]/norm;
181  }
182  //write outBuffer to file
183  try{
184  output.writeData(outBuffer,GDT_Float64,y,iband);
185  }
186  catch(std::string errorstring){
187  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
188  }
189  progress=(1.0+y);
190  progress+=(output.nrOfRow()*iband);
191  progress/=output.nrOfBand()*output.nrOfRow();
192  pfnProgress(progress,pszMessage,pProgressArg);
193  }
194  }
195 }
196 
197 
198 void filter2d::Filter2d::majorVoting(const std::string& inputFilename, const std::string& outputFilename,int dim,const std::vector<int> &prior)
199 {
200  const char* pszMessage;
201  void* pProgressArg=NULL;
202  GDALProgressFunc pfnProgress=GDALTermProgress;
203  double progress=0;
204  pfnProgress(progress,pszMessage,pProgressArg);
205 
206  bool usePriors=true;
207  if(prior.empty()){
208  std::cout << "no prior information" << std::endl;
209  usePriors=false;
210  }
211  else{
212  std::cout << "using priors ";
213  for(int iclass=0;iclass<prior.size();++iclass)
214  std::cout << " " << static_cast<short>(prior[iclass]);
215  std::cout << std::endl;
216  }
217 
218  ImgReaderGdal input;
219  ImgWriterGdal output;
220  input.open(inputFilename);
221  output.open(outputFilename,input);
222  int dimX=0;//horizontal!!!
223  int dimY=0;//vertical!!!
224  if(dim){
225  dimX=dim;
226  dimY=dim;
227  }
228  else{
229  dimX=m_taps[0].size();
230  dimY=m_taps.size();
231  }
232 
233  assert(dimX);
234  assert(dimY);
235 
236  Vector2d<double> inBuffer(dimY,input.nrOfCol());
237  std::vector<double> outBuffer(input.nrOfCol());
238  int indexI=0;
239  int indexJ=0;
240  //initialize last half of inBuffer
241  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
242  try{
243  input.readData(inBuffer[indexJ],GDT_Float64,abs(j));
244  }
245  catch(std::string errorstring){
246  std::cerr << errorstring << "in line " << indexJ << std::endl;
247  }
248  ++indexJ;
249  }
250 
251  for(int y=0;y<input.nrOfRow();++y){
252  if(y){//inBuffer already initialized for y=0
253  //erase first line from inBuffer
254  if(dimY>1)
255  inBuffer.erase(inBuffer.begin());
256  //read extra line and push back to inBuffer if not out of bounds
257  if(y+dimY/2<input.nrOfRow()){
258  //allocate buffer
259  if(dimY>1)
260  inBuffer.push_back(inBuffer.back());
261  try{
262  input.readData(inBuffer[inBuffer.size()-1],GDT_Float64,y+dimY/2);
263  }
264  catch(std::string errorstring){
265  std::cerr << errorstring << "in line" << y << std::endl;
266  }
267  }
268  else{
269  int over=y+dimY/2-input.nrOfRow();
270  int index=(inBuffer.size()-1)-over;
271  assert(index>=0);
272  assert(index<inBuffer.size());
273  inBuffer.push_back(inBuffer[index]);
274  }
275  }
276  for(int x=0;x<input.nrOfCol();++x){
277  outBuffer[x]=0;
278  std::map<int,int> occurrence;
279  int centre=dimX*(dimY-1)/2+(dimX-1)/2;
280  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
281  for(int i=-(dimX-1)/2;i<=dimX/2;++i){
282  indexI=x+i;
283  //check if out of bounds
284  if(indexI<0)
285  indexI=-indexI;
286  else if(indexI>=input.nrOfCol())
287  indexI=input.nrOfCol()-i;
288  if(y+j<0)
289  indexJ=-j;
290  else if(y+j>=input.nrOfRow())
291  indexJ=(dimY>2) ? (dimY-1)/2-j : 0;
292  else
293  indexJ=(dimY-1)/2+j;
294 
295  // if(x<dimX/2)
296  // indexI=x+abs(i);
297  // else if(x>=input.nrOfCol()-dimX/2)
298  // indexI=x-abs(i);
299  // if(y<dimY/2)
300  // indexJ=dimY/2+abs(j);
301  // else if(y>=input.nrOfRow()-dimY/2)
302  // indexJ=dimY/2-abs(j);
303  if(usePriors){
304  occurrence[inBuffer[indexJ][indexI]]+=prior[inBuffer[indexJ][indexI]-1];
305  }
306  else
307  ++occurrence[inBuffer[indexJ][indexI]];
308  }
309  }
310  std::map<int,int>::const_iterator maxit=occurrence.begin();
311  for(std::map<int,int>::const_iterator mit=occurrence.begin();mit!=occurrence.end();++mit){
312  if(mit->second>maxit->second)
313  maxit=mit;
314  }
315  if(occurrence[inBuffer[(dimY-1)/2][x]]<maxit->second)//
316  outBuffer[x]=maxit->first;
317  else//favorize original value in case of ties
318  outBuffer[x]=inBuffer[(dimY-1)/2][x];
319  }
320  //write outBuffer to file
321  try{
322  output.writeData(outBuffer,GDT_Float64,y);
323  }
324  catch(std::string errorstring){
325  std::cerr << errorstring << "in line" << y << std::endl;
326  }
327  progress=(1.0+y)/output.nrOfRow();
328  pfnProgress(progress,pszMessage,pProgressArg);
329  }
330  input.close();
331  output.close();
332 }
333 
334 void filter2d::Filter2d::median(const std::string& inputFilename, const std::string& outputFilename,int dim, bool disc)
335 {
336  ImgReaderGdal input;
337  ImgWriterGdal output;
338  input.open(inputFilename);
339  output.open(outputFilename,input);
340  doit(input,output,"median",dim,disc);
341 }
342 
343 void filter2d::Filter2d::var(const std::string& inputFilename, const std::string& outputFilename,int dim, bool disc)
344 {
345  ImgReaderGdal input;
346  ImgWriterGdal output;
347  input.open(inputFilename);
348  output.open(outputFilename,input);
349  doit(input,output,"var",dim,disc);
350 }
351 
352 void filter2d::Filter2d::doit(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& method, int dim, short down, bool disc)
353 {
354  doit(input,output,method,dim,dim,down,disc);
355 }
356 
357 void filter2d::Filter2d::doit(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& method, int dimX, int dimY, short down, bool disc)
358 {
359  const char* pszMessage;
360  void* pProgressArg=NULL;
361  GDALProgressFunc pfnProgress=GDALTermProgress;
362  double progress=0;
363  pfnProgress(progress,pszMessage,pProgressArg);
364 
365  assert(dimX);
366  assert(dimY);
367 
369  for(int iband=0;iband<input.nrOfBand();++iband){
370  Vector2d<double> inBuffer(dimY,input.nrOfCol());
371  std::vector<double> outBuffer((input.nrOfCol()+down-1)/down);
372  int indexI=0;
373  int indexJ=0;
374  //initialize last half of inBuffer
375  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
376  try{
377  input.readData(inBuffer[indexJ],GDT_Float64,abs(j),iband);
378  }
379  catch(std::string errorstring){
380  std::cerr << errorstring << "in line " << indexJ << std::endl;
381  }
382  ++indexJ;
383  }
384  for(int y=0;y<input.nrOfRow();++y){
385  if(y){//inBuffer already initialized for y=0
386  //erase first line from inBuffer
387  if(dimY>1)
388  inBuffer.erase(inBuffer.begin());
389  //read extra line and push back to inBuffer if not out of bounds
390  if(y+dimY/2<input.nrOfRow()){
391  //allocate buffer
392  if(dimY>1)
393  inBuffer.push_back(inBuffer.back());
394  try{
395  input.readData(inBuffer[inBuffer.size()-1],GDT_Float64,y+dimY/2,iband);
396  }
397  catch(std::string errorstring){
398  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
399  }
400  }
401  else{
402  int over=y+dimY/2-input.nrOfRow();
403  int index=(inBuffer.size()-1)-over;
404  assert(index>=0);
405  assert(index<inBuffer.size());
406  inBuffer.push_back(inBuffer[index]);
407  }
408  }
409  if((y+1+down/2)%down)
410  continue;
411  for(int x=0;x<input.nrOfCol();++x){
412  if((x+1+down/2)%down)
413  continue;
414  outBuffer[x/down]=0;
415  std::vector<double> windowBuffer;
416  std::map<long int,int> occurrence;
417  int centre=dimX*(dimY-1)/2+(dimX-1)/2;
418  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
419  for(int i=-(dimX-1)/2;i<=dimX/2;++i){
420  double d2=i*i+j*j;//square distance
421  if(disc&&(d2>(dimX/2)*(dimY/2)))
422  continue;
423  indexI=x+i;
424  //check if out of bounds
425  if(indexI<0)
426  indexI=-indexI;
427  else if(indexI>=input.nrOfCol())
428  indexI=input.nrOfCol()-i;
429  if(y+j<0)
430  indexJ=-j;
431  else if(y+j>=input.nrOfRow())
432  indexJ=(dimY>2) ? (dimY-1)/2-j : 0;
433  else
434  indexJ=(dimY-1)/2+j;
435  bool masked=false;
436  for(int imask=0;imask<m_noDataValues.size();++imask){
437  if(inBuffer[indexJ][indexI]==m_noDataValues[imask]){
438  masked=true;
439  break;
440  }
441  }
442  if(!masked){
443  std::vector<short>::const_iterator vit=m_class.begin();
444  if(!m_class.size())
445  ++occurrence[inBuffer[indexJ][indexI]];
446  else{
447  while(vit!=m_class.end()){
448  if(inBuffer[indexJ][indexI]==*(vit++))
449  ++occurrence[inBuffer[indexJ][indexI]];
450  }
451  }
452  windowBuffer.push_back(inBuffer[indexJ][indexI]);
453  }
454  }
455  }
456  switch(getFilterType(method)){
457  case(filter2d::median):
458  if(windowBuffer.empty())
459  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
460  else
461  outBuffer[x/down]=stat.median(windowBuffer);
462  break;
463  case(filter2d::var):{
464  if(windowBuffer.empty())
465  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
466  else
467  outBuffer[x/down]=stat.var(windowBuffer);
468  break;
469  }
470  case(filter2d::stdev):{
471  if(windowBuffer.empty())
472  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
473  else
474  outBuffer[x/down]=sqrt(stat.var(windowBuffer));
475  break;
476  }
477  case(filter2d::mean):{
478  if(windowBuffer.empty())
479  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
480  else
481  outBuffer[x/down]=stat.mean(windowBuffer);
482  break;
483  }
484  case(filter2d::min):{
485  if(windowBuffer.empty())
486  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
487  else
488  outBuffer[x/down]=stat.mymin(windowBuffer);
489  break;
490  }
491  case(filter2d::ismin):{
492  if(windowBuffer.empty())
493  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
494  else
495  outBuffer[x/down]=(stat.mymin(windowBuffer)==windowBuffer[centre])? 1:0;
496  break;
497  }
498  case(filter2d::minmax):{//is the same as homog?
499  double min=0;
500  double max=0;
501  if(windowBuffer.empty())
502  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
503  else{
504  stat.minmax(windowBuffer,windowBuffer.begin(),windowBuffer.end(),min,max);
505  if(min!=max)
506  outBuffer[x/down]=0;
507  else
508  outBuffer[x/down]=windowBuffer[centre];//centre pixels
509  }
510  break;
511  }
512  case(filter2d::max):{
513  if(windowBuffer.empty())
514  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
515  else
516  outBuffer[x/down]=stat.mymax(windowBuffer);
517  break;
518  }
519  case(filter2d::ismax):{
520  if(windowBuffer.empty())
521  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
522  else
523  outBuffer[x/down]=(stat.mymax(windowBuffer)==windowBuffer[centre])? 1:0;
524  break;
525  }
526  case(filter2d::order):{
527  if(windowBuffer.empty())
528  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
529  else{
530  double lbound=0;
531  double ubound=dimX*dimY;
532  double theMin=stat.mymin(windowBuffer);
533  double theMax=stat.mymax(windowBuffer);
534  double scale=(ubound-lbound)/(theMax-theMin);
535  outBuffer[x/down]=static_cast<short>(scale*(windowBuffer[centre]-theMin)+lbound);
536  }
537  break;
538  }
539  case(filter2d::sum):{
540  outBuffer[x/down]=stat.sum(windowBuffer);
541  break;
542  }
543  case(filter2d::percentile):{
544  assert(m_threshold.size());
545  outBuffer[x/down]=stat.percentile(windowBuffer,windowBuffer.begin(),windowBuffer.end(),m_threshold[0]);
546  break;
547  }
548  case(filter2d::homog):
549  if(occurrence.size()==1)//all values in window are the same
550  outBuffer[x/down]=inBuffer[(dimY-1)/2][x];
551  else
552  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
553  break;
554  case(filter2d::heterog):{
555  if(occurrence.size()==windowBuffer.size())
556  outBuffer[x/down]=inBuffer[(dimY-1)/2][x];
557  else
558  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
559  // if(occurrence.size()==1)//all values in window are the same
560  // outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
561  // else
562  // outBuffer[x/down]=inBuffer[(dimY-1)/2][x];
563  // break;
564  // for(std::vector<double>::const_iterator wit=windowBuffer.begin();wit!=windowBuffer.end();++wit){
565  // if(wit==windowBuffer.begin()+windowBuffer.size()/2)
566  // continue;
567  // else if(*wit!=inBuffer[(dimY-1)/2][x]){
568  // outBuffer[x/down]=1;
569  // break;
570  // }
571  // else if(*wit==inBuffer[(dimY-1)/2][x]){//todo:wit mag niet central pixel zijn
572  // outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
573  // break;
574  // }
575  // }
576  // break;
577  }
578  case(filter2d::density):{
579  if(windowBuffer.size()){
580  std::vector<short>::const_iterator vit=m_class.begin();
581  while(vit!=m_class.end())
582  outBuffer[x/down]+=100.0*occurrence[*(vit++)]/windowBuffer.size();
583  }
584  else
585  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
586  break;
587  }
588  case(filter2d::countid):{
589  if(windowBuffer.size())
590  outBuffer[x/down]=occurrence.size();
591  else
592  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
593  break;
594  }
595  case(filter2d::mode):{
596  if(occurrence.size()){
597  std::map<long int,int>::const_iterator maxit=occurrence.begin();
598  for(std::map<long int,int>::const_iterator mit=occurrence.begin();mit!=occurrence.end();++mit){
599  if(mit->second>maxit->second)
600  maxit=mit;
601  }
602  if(occurrence[inBuffer[(dimY-1)/2][x]]<maxit->second)//
603  outBuffer[x/down]=maxit->first;
604  else//favorize original value in case of ties
605  outBuffer[x/down]=inBuffer[(dimY-1)/2][x];
606  }
607  else
608  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
609  break;
610  }
611  case(filter2d::threshold):{
612  assert(m_class.size()==m_threshold.size());
613  if(windowBuffer.size()){
614  outBuffer[x/down]=inBuffer[(dimY-1)/2][x];//initialize with original value (in case thresholds not met)
615  for(int iclass=0;iclass<m_class.size();++iclass){
616  if(100.0*(occurrence[m_class[iclass]])/windowBuffer.size()>m_threshold[iclass])
617  outBuffer[x/down]=m_class[iclass];
618  }
619  }
620  else
621  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
622  break;
623  }
624  case(filter2d::scramble):{//could be done more efficiently window by window with random shuffling entire buffer and assigning entire buffer at once to output image...
625  if(windowBuffer.size()){
626  int randomIndex=std::rand()%windowBuffer.size();
627  if(randomIndex>=windowBuffer.size())
628  outBuffer[x/down]=windowBuffer.back();
629  else if(randomIndex<0)
630  outBuffer[x/down]=windowBuffer[0];
631  else
632  outBuffer[x/down]=windowBuffer[randomIndex];
633  }
634  else
635  outBuffer[x/down]=(m_noDataValues.size())? m_noDataValues[0] : 0;
636  break;
637  }
638  case(filter2d::mixed):{
639  enum Type { BF=11, CF=12, MF=13, NF=20, W=30 };
640  double nBF=occurrence[BF];
641  double nCF=occurrence[CF];
642  double nMF=occurrence[MF];
643  double nNF=occurrence[NF];
644  double nW=occurrence[W];
645  if(windowBuffer.size()){
646  if((nBF+nCF+nMF)&&(nBF+nCF+nMF>=nNF+nW)){//forest
647  if(nBF/(nBF+nCF)>=0.75)
648  outBuffer[x/down]=BF;
649  else if(nCF/(nBF+nCF)>=0.75)
650  outBuffer[x/down]=CF;
651  else
652  outBuffer[x/down]=MF;
653  }
654  else{//non-forest
655  if(nW&&(nW>=nNF))
656  outBuffer[x/down]=W;
657  else
658  outBuffer[x/down]=NF;
659  }
660  }
661  else
662  outBuffer[x/down]=inBuffer[indexJ][indexI];
663  break;
664  }
665  default:{
666  std::ostringstream ess;
667  ess << "Error: filter method " << method << " not supported" << std::endl;
668  throw(ess.str());
669  break;
670  }
671  }
672  }
673  progress=(1.0+y/down);
674  progress+=(output.nrOfRow()*iband);
675  progress/=output.nrOfBand()*output.nrOfRow();
676  pfnProgress(progress,pszMessage,pProgressArg);
677  //write outBuffer to file
678  try{
679  output.writeData(outBuffer,GDT_Float64,y/down,iband);
680  }
681  catch(std::string errorstring){
682  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
683  }
684  }
685  }
686  pfnProgress(1.0,pszMessage,pProgressArg);
687 }
688 
689 void filter2d::Filter2d::mrf(const ImgReaderGdal& input, ImgWriterGdal& output, int dimX, int dimY, double beta, bool eightConnectivity, short down, bool verbose){
690  assert(m_class.size()>1);
691  Vector2d<double> fullBeta(m_class.size(),m_class.size());
692  for(int iclass1=0;iclass1<m_class.size();++iclass1)
693  for(int iclass2=0;iclass2<m_class.size();++iclass2)
694  fullBeta[iclass1][iclass2]=beta;
695  mrf(input,output,dimX,dimY,fullBeta,eightConnectivity,down,verbose);
696 }
697 
698 //beta[classTo][classFrom]
699 void filter2d::Filter2d::mrf(const ImgReaderGdal& input, ImgWriterGdal& output, int dimX, int dimY, Vector2d<double> beta, bool eightConnectivity, short down, bool verbose)
700 {
701  const char* pszMessage;
702  void* pProgressArg=NULL;
703  GDALProgressFunc pfnProgress=GDALTermProgress;
704  double progress=0;
705  pfnProgress(progress,pszMessage,pProgressArg);
706 
707  assert(dimX);
708  assert(dimY);
709 
710  Vector2d<short> inBuffer(dimY,input.nrOfCol());
711  Vector2d<double> outBuffer(m_class.size(),(input.nrOfCol()+down-1)/down);
712  assert(input.nrOfBand()==1);
713  assert(output.nrOfBand()==m_class.size());
714  assert(m_class.size()>1);
715  assert(beta.size()==m_class.size());
716  int indexI=0;
717  int indexJ=0;
718  //initialize last half of inBuffer
719  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
720  try{
721  input.readData(inBuffer[indexJ],GDT_Int16,abs(j));
722  }
723  catch(std::string errorstring){
724  std::cerr << errorstring << "in line " << indexJ << std::endl;
725  }
726  ++indexJ;
727  }
728  for(int y=0;y<input.nrOfRow();++y){
729  if(y){//inBuffer already initialized for y=0
730  //erase first line from inBuffer
731  if(dimY>1)
732  inBuffer.erase(inBuffer.begin());
733  //read extra line and push back to inBuffer if not out of bounds
734  if(y+dimY/2<input.nrOfRow()){
735  //allocate buffer
736  if(dimY>1)
737  inBuffer.push_back(inBuffer.back());
738  try{
739  input.readData(inBuffer[inBuffer.size()-1],GDT_Int16,y+dimY/2);
740  }
741  catch(std::string errorstring){
742  std::cerr << errorstring << "in line " << y << std::endl;
743  }
744  }
745  else{
746  int over=y+dimY/2-input.nrOfRow();
747  int index=(inBuffer.size()-1)-over;
748  assert(index>=0);
749  assert(index<inBuffer.size());
750  inBuffer.push_back(inBuffer[index]);
751  }
752  }
753  if((y+1+down/2)%down)
754  continue;
755  for(int x=0;x<input.nrOfCol();++x){
756  if((x+1+down/2)%down)
757  continue;
758  std::vector<short> potential(m_class.size());
759  for(int iclass=0;iclass<m_class.size();++iclass){
760  potential[iclass]=0;
761  outBuffer[iclass][x/down]=0;
762  }
763  std::vector<double> windowBuffer;
764  int centre=dimX*(dimY-1)/2+(dimX-1)/2;
765  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
766  for(int i=-(dimX-1)/2;i<=dimX/2;++i){
767  if(i!=0&&j!=0&&!eightConnectivity)
768  continue;
769  if(i==0&&j==0)
770  continue;
771  indexI=x+i;
772  //check if out of bounds
773  if(indexI<0)
774  indexI=-indexI;
775  else if(indexI>=input.nrOfCol())
776  indexI=input.nrOfCol()-i;
777  if(y+j<0)
778  indexJ=-j;
779  else if(y+j>=input.nrOfRow())
780  indexJ=(dimY>2) ? (dimY-1)/2-j : 0;
781  else
782  indexJ=(dimY-1)/2+j;
783  bool masked=false;
784  for(int imask=0;imask<m_noDataValues.size();++imask){
785  if(inBuffer[indexJ][indexI]==m_noDataValues[imask]){
786  masked=true;
787  break;
788  }
789  }
790  if(!masked){
791  for(int iclass=0;iclass<m_class.size();++iclass){
792  if(inBuffer[indexJ][indexI]==m_class[iclass])
793  potential[iclass]+=1;
794  }
795  }
796  }
797  }
798  double norm=0;
799  for(int iclass1=0;iclass1<m_class.size();++iclass1){
800  assert(beta[iclass1].size()==m_class.size());
801  double pot=0;
802  for(int iclass2=0;iclass2<m_class.size();++iclass2)
803  if(iclass2!=iclass1)
804  pot+=potential[iclass2]*beta[iclass1][iclass2];
805  double prior=exp(-pot);
806  outBuffer[iclass1][x/down]=prior;
807  norm+=prior;
808  }
809  if(norm){
810  for(int iclass1=0;iclass1<m_class.size();++iclass1)
811  outBuffer[iclass1][x/down]/=norm;
812  }
813  }
814  progress=(1.0+y/down)/output.nrOfRow();
815  pfnProgress(progress,pszMessage,pProgressArg);
816  //write outBuffer to file
817  assert(outBuffer.size()==m_class.size());
818  assert(y<output.nrOfRow());
819  for(int iclass=0;iclass<m_class.size();++iclass){
820  assert(outBuffer[iclass].size()==output.nrOfCol());
821  try{
822  output.writeData(outBuffer[iclass],GDT_Float64,y/down,iclass);
823  }
824  catch(std::string errorstring){
825  std::cerr << errorstring << "in class " << iclass << ", line " << y << std::endl;
826  }
827  }
828  }
829 }
830 
831 void filter2d::Filter2d::shift(const ImgReaderGdal& input, ImgWriterGdal& output, double offsetX, double offsetY, double randomSigma, RESAMPLE resample, bool verbose)
832 {
833  assert(input.nrOfCol()==output.nrOfCol());
834  assert(input.nrOfRow()==output.nrOfRow());
835  assert(input.nrOfBand()==output.nrOfBand());
836  const char* pszMessage;
837  void* pProgressArg=NULL;
838  GDALProgressFunc pfnProgress=GDALTermProgress;
839  double progress=0;
840  pfnProgress(progress,pszMessage,pProgressArg);
841  //process band per band in memory
842  Vector2d<double> inBuffer(input.nrOfRow(),output.nrOfCol());
843  Vector2d<double> outBuffer(input.nrOfRow(),output.nrOfCol());
844  for(int iband=0;iband<input.nrOfBand();++iband){
845  input.readDataBlock(inBuffer,GDT_Float64,0,inBuffer.nCols()-1,0,inBuffer.nRows()-1,iband);
846  shift(inBuffer,outBuffer,offsetX,offsetY,randomSigma,resample,verbose);
847  output.writeDataBlock(outBuffer,GDT_Float64,0,outBuffer.nCols()-1,0,outBuffer.nRows()-1,iband);
848  }
849 }
850 
851 //todo: re-implement without dependency of CImg and reg libraries
852 // void filter2d::Filter2d::dwt_texture(const std::string& inputFilename, const std::string& outputFilename,int dim, int scale, int down, int iband, bool verbose)
853 // {
854 // ImgReaderGdal input;
855 // ImgWriterGdal output;
856 // if(verbose)
857 // std::cout << "opening file " << inputFilename << std::endl;
858 // input.open(inputFilename);
859 // double magicX=1,magicY=1;
860 // output.open(outputFilename,(input.nrOfCol()+down-1)/down,(input.nrOfRow()+down-1)/down,scale*3,GDT_Float32,input.getImageType());
861 // if(input.isGeoRef()){
862 // output.setProjection(input.getProjection());
863 // output.copyGeoTransform(input);
864 // }
865 // if(verbose)
866 // std::cout << "Dimension texture (row x col x band) = " << (input.nrOfCol()+down-1)/down << " x " << (input.nrOfRow()+down-1)/down << " x " << scale*3 << std::endl;
867 // assert(dim%2);
868 // int dimX=dim;
869 // int dimY=dim;
870 // Vector2d<float> inBuffer(dimY,input.nrOfCol());
871 // Vector2d<float> outBuffer(scale*3,(input.nrOfCol()+down-1)/down);
872 // //initialize last half of inBuffer
873 // int indexI=0;
874 // int indexJ=0;
875 // for(int j=-dimY/2;j<(dimY+1)/2;++j){
876 // try{
877 // if(verbose)
878 // cout << "reading input line " << abs(j) << std::endl;
879 // input.readData(inBuffer[indexJ],GDT_Float32,abs(j),iband);
880 // ++indexJ;
881 // }
882 // catch(std::string errorstring){
883 // std::cerr << errorstring << "in band " << iband << ", line " << indexJ << std::endl;
884 // }
885 // }
886 // const char* pszMessage;
887 // void* pProgressArg=NULL;
888 // GDALProgressFunc pfnProgress=GDALTermProgress;
889 // double progress=0;
890 // pfnProgress(progress,pszMessage,pProgressArg);
891 // for(int y=0;y<input.nrOfRow();y+=down){
892 // if(verbose)
893 // std::cout << "calculating line " << y/down << std::endl;
894 // if(y){//inBuffer already initialized for y=0
895 // //erase first line from inBuffer
896 // inBuffer.erase(inBuffer.begin());
897 // //read extra line and push back to inBuffer if not out of bounds
898 // if(y+dimY/2<input.nrOfRow()){
899 // //allocate buffer
900 // inBuffer.push_back(inBuffer.back());
901 // try{
902 // if(verbose)
903 // std::cout << "reading input line " << y+dimY/2 << std::endl;
904 // input.readData(inBuffer[inBuffer.size()-1],GDT_Float32,y+dimY/2,iband);
905 // }
906 // catch(std::string errorstring){
907 // std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
908 // }
909 // }
910 // }
911 // for(int x=0;x<input.nrOfCol();x+=down){
912 // Vector2d<double> texture_feature(scale,3);
913 // CImg<> texture_in(dimX,dimY);
914 // int r=0;//index for row of texture_in
915 // for(int j=-dimY/2;j<(dimY+1)/2;++j){
916 // int c=0;
917 // for(int i=-dimX/2;i<(dimX+1)/2;++i){
918 // indexI=x+i;
919 // //check if out of bounds
920 // if(indexI<0)
921 // indexI=-indexI;
922 // else if(indexI>=input.nrOfCol())
923 // indexI=input.nrOfCol()-i;
924 // if(y+j<0)
925 // indexJ=-j;
926 // else if(y+j>=input.nrOfRow())
927 // indexJ=dimY/2-j;//indexJ=inBuffer.size()-1-j;
928 // else
929 // indexJ=dimY/2+j;
930 // assert(indexJ<inBuffer.size());
931 // assert(indexI<inBuffer[indexJ].size());
932 // texture_in(r,c)=inBuffer[indexJ][indexI];
933 // c++;
934 // }
935 // ++r;
936 // }
937 // texture_in.dwt_texture(texture_feature,scale);
938 // for(int v=0;v<scale*3;++v)
939 // outBuffer[v][x/down]=texture_feature[v/3][v%3];
940 // }
941 // //write outBuffer to file
942 // try{
943 // if(verbose)
944 // std::cout << "writing line " << y/down << std::endl;
945 // for(int v=0;v<scale*3;++v)
946 // output.writeData(outBuffer[v],GDT_Float32,y/down,v);
947 // }
948 // catch(std::string errorstring){
949 // std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
950 // }
951 // progress=(1.0+y)/output.nrOfRow();
952 // pfnProgress(progress,pszMessage,pProgressArg);
953 // }
954 // input.close();
955 // output.close();
956 // }
957 
958 void filter2d::Filter2d::morphology(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& method, int dimX, int dimY, const std::vector<double> &angle, bool disc)
959 {
960  const char* pszMessage;
961  void* pProgressArg=NULL;
962  GDALProgressFunc pfnProgress=GDALTermProgress;
963  double progress=0;
964  pfnProgress(progress,pszMessage,pProgressArg);
965 
966  assert(dimX);
967  assert(dimY);
968 
970  for(int iband=0;iband<input.nrOfBand();++iband){
971  Vector2d<double> inBuffer(dimY,input.nrOfCol());
972  std::vector<double> outBuffer(input.nrOfCol());
973  int indexI=0;
974  int indexJ=0;
975  //initialize last half of inBuffer
976  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
977  try{
978  input.readData(inBuffer[indexJ],GDT_Float64,abs(j),iband);
979  ++indexJ;
980  }
981  catch(std::string errorstring){
982  std::cerr << errorstring << "in line " << indexJ << std::endl;
983  }
984  }
985  for(int y=0;y<input.nrOfRow();++y){
986  if(y){//inBuffer already initialized for y=0
987  //erase first line from inBuffer
988  if(dimY>1)
989  inBuffer.erase(inBuffer.begin());
990  //read extra line and push back to inBuffer if not out of bounds
991  if(y+dimY/2<input.nrOfRow()){
992  //allocate buffer
993  if(dimY>1)
994  inBuffer.push_back(inBuffer.back());
995  try{
996  input.readData(inBuffer[inBuffer.size()-1],GDT_Float64,y+dimY/2,iband);
997  }
998  catch(std::string errorstring){
999  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
1000  }
1001  }
1002  else{
1003  int over=y+dimY/2-input.nrOfRow();
1004  int index=(inBuffer.size()-1)-over;
1005  assert(index>=0);
1006  assert(index<inBuffer.size());
1007  inBuffer.push_back(inBuffer[index]);
1008  }
1009  }
1010  for(int x=0;x<input.nrOfCol();++x){
1011  double currentValue=inBuffer[(dimY-1)/2][x];
1012  outBuffer[x]=currentValue;
1013  std::vector<double> statBuffer;
1014  bool currentMasked=false;
1015  int centre=dimX*(dimY-1)/2+(dimX-1)/2;
1016  for(int imask=0;imask<m_noDataValues.size();++imask){
1017  if(currentValue==m_noDataValues[imask]){
1018  currentMasked=true;
1019  break;
1020  }
1021  }
1022  if(currentMasked){
1023  outBuffer[x]=currentValue;
1024  }
1025  else{
1026  for(int j=-(dimY-1)/2;j<=dimY/2;++j){
1027  for(int i=-(dimX-1)/2;i<=dimX/2;++i){
1028  double d2=i*i+j*j;//square distance
1029  if(disc&&(d2>(dimX/2)*(dimY/2)))
1030  continue;
1031  if(angle.size()){
1032  double theta;
1033  //use polar coordinates in radians
1034  if(i>0){
1035  if(j<0)
1036  theta=atan(static_cast<double>(-j)/(static_cast<double>(i)));
1037  else
1038  theta=-atan(static_cast<double>(j)/(static_cast<double>(i)));
1039  }
1040  else if(i<0){
1041  if(j<0)
1042  theta=PI-atan(static_cast<double>(-j)/(static_cast<double>(-i)));
1043  else
1044  theta=PI+atan(static_cast<double>(j)/(static_cast<double>(-i)));
1045  }
1046  else if(j<0)
1047  theta=PI/2.0;
1048  else if(j>0)
1049  theta=3.0*PI/2.0;
1050  //convert to North (0), East (90), South (180), West (270) in degrees
1051  theta=360-(theta/PI*180)+90;
1052  if(theta<0)
1053  theta+=360;
1054  while(theta>360)
1055  theta-=360;
1056  bool alligned=false;
1057  for(int iangle=0;iangle<angle.size();++iangle){
1058  if(sqrt((theta-angle[iangle])*(theta-angle[iangle]))<10){
1059  alligned=true;
1060  break;
1061  }
1062  }
1063  if(!alligned)
1064  continue;
1065  }
1066  indexI=x+i;
1067  //check if out of bounds
1068  if(indexI<0)
1069  indexI=-indexI;
1070  else if(indexI>=input.nrOfCol())
1071  indexI=input.nrOfCol()-i;
1072  if(y+j<0)
1073  indexJ=-j;
1074  else if(y+j>=input.nrOfRow())
1075  indexJ=(dimY>2) ? (dimY-1)/2-j : 0;
1076  else
1077  indexJ=(dimY-1)/2+j;
1078  //todo: introduce novalue as this: ?
1079  // if(inBuffer[indexJ][indexI]==(m_noDataValues.size())? m_noDataValues[0] : 0)
1080  // continue;
1081  bool masked=false;
1082  for(int imask=0;imask<m_noDataValues.size();++imask){
1083  if(inBuffer[indexJ][indexI]==m_noDataValues[imask]){
1084  masked=true;
1085  break;
1086  }
1087  }
1088  if(!masked){
1089  short binValue=0;
1090  for(int iclass=0;iclass<m_class.size();++iclass){
1091  if(inBuffer[indexJ][indexI]==m_class[iclass]){
1092  binValue=1;
1093  break;
1094  }
1095  }
1096  if(m_class.size())
1097  statBuffer.push_back(binValue);
1098  else
1099  statBuffer.push_back(inBuffer[indexJ][indexI]);
1100  }
1101  }
1102  }
1103  if(statBuffer.size()){
1104  switch(getFilterType(method)){
1105  case(filter2d::dilate):
1106  outBuffer[x]=stat.mymax(statBuffer);
1107  break;
1108  case(filter2d::erode):
1109  outBuffer[x]=stat.mymin(statBuffer);
1110  break;
1111  default:
1112  std::ostringstream ess;
1113  ess << "Error: morphology method " << method << " not supported, choose " << filter2d::dilate << " (dilate) or " << filter2d::erode << " (erode)" << std::endl;
1114  throw(ess.str());
1115  break;
1116  }
1117  }
1118  if(outBuffer[x]&&m_class.size())
1119  outBuffer[x]=m_class[0];
1120  }
1121  }
1122  //write outBuffer to file
1123  try{
1124  output.writeData(outBuffer,GDT_Float64,y,iband);
1125  }
1126  catch(std::string errorstring){
1127  std::cerr << errorstring << "in band " << iband << ", line " << y << std::endl;
1128  }
1129  progress=(1.0+y);
1130  progress+=(output.nrOfRow()*iband);
1131  progress/=output.nrOfBand()*output.nrOfRow();
1132  pfnProgress(progress,pszMessage,pProgressArg);
1133  }
1134  }
1135 }
1136 
1137 void filter2d::Filter2d::shadowDsm(const ImgReaderGdal& input, ImgWriterGdal& output, double sza, double saa, double pixelSize, short shadowFlag){
1138  Vector2d<float> inputBuffer;
1139  Vector2d<float> outputBuffer;
1140  input.readDataBlock(inputBuffer, GDT_Float32, 0, input.nrOfCol()-1, 0, input.nrOfRow()-1, 0);
1141  shadowDsm(inputBuffer, outputBuffer, sza, saa, pixelSize, shadowFlag);
1142  output.writeDataBlock(outputBuffer,GDT_Float32,0,output.nrOfCol()-1,0,output.nrOfRow()-1,0);
1143 }
1144 
1145 void filter2d::Filter2d::dwtForward(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& wavelet_type, int family){
1146  Vector2d<float> theBuffer;
1147  for(int iband=0;iband<input.nrOfBand();++iband){
1148  input.readDataBlock(theBuffer, GDT_Float32, 0, input.nrOfCol()-1, 0, input.nrOfRow()-1, iband);
1149  std::cout << "filtering band " << iband << std::endl << std::flush;
1150  dwtForward(theBuffer, wavelet_type, family);
1151  output.writeDataBlock(theBuffer,GDT_Float32,0,output.nrOfCol()-1,0,output.nrOfRow()-1,iband);
1152  }
1153 }
1154 
1155 void filter2d::Filter2d::dwtInverse(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& wavelet_type, int family){
1156  Vector2d<float> theBuffer;
1157  for(int iband=0;iband<input.nrOfBand();++iband){
1158  input.readDataBlock(theBuffer, GDT_Float32, 0, input.nrOfCol()-1, 0, input.nrOfRow()-1, iband);
1159  std::cout << "filtering band " << iband << std::endl << std::flush;
1160  dwtInverse(theBuffer, wavelet_type, family);
1161  output.writeDataBlock(theBuffer,GDT_Float32,0,output.nrOfCol()-1,0,output.nrOfRow()-1,iband);
1162  }
1163 }
1164 
1165 void filter2d::Filter2d::dwtCut(const ImgReaderGdal& input, ImgWriterGdal& output, const std::string& wavelet_type, int family, double cut, bool verbose){
1166  Vector2d<float> theBuffer;
1167  for(int iband=0;iband<input.nrOfBand();++iband){
1168  input.readDataBlock(theBuffer, GDT_Float32, 0, input.nrOfCol()-1, 0, input.nrOfRow()-1, iband);
1169  std::cout << "filtering band " << iband << std::endl << std::flush;
1170  dwtCut(theBuffer, wavelet_type, family, cut);
1171  output.writeDataBlock(theBuffer,GDT_Float32,0,output.nrOfCol()-1,0,output.nrOfRow()-1,iband);
1172  }
1173 }
1174 
1175 void filter2d::Filter2d::linearFeature(const ImgReaderGdal& input, ImgWriterGdal& output, float angle, float angleStep, float maxDistance, float eps, bool l1, bool a1, bool l2, bool a2, int band, bool verbose){
1176  Vector2d<float> inputBuffer;
1177  std::vector< Vector2d<float> > outputBuffer;
1178  input.readDataBlock(inputBuffer, GDT_Float32, 0, input.nrOfCol()-1, 0, input.nrOfRow()-1, band);
1179  if(maxDistance<=0)
1180  maxDistance=sqrt(static_cast<float>(input.nrOfCol()*input.nrOfRow()));
1181  linearFeature(inputBuffer,outputBuffer,angle,angleStep,maxDistance,eps, l1, a1, l2, a2,verbose);
1182  for(int iband=0;iband<outputBuffer.size();++iband)
1183  output.writeDataBlock(outputBuffer[iband],GDT_Float32,0,output.nrOfCol()-1,0,output.nrOfRow()-1,iband);
1184 }
1185 
1186 void filter2d::Filter2d::linearFeature(const Vector2d<float>& input, std::vector< Vector2d<float> >& output, float angle, float angleStep, float maxDistance, float eps, bool l1, bool a1, bool l2, bool a2, bool verbose)
1187 {
1188  output.clear();
1189  int nband=0;//linear feature
1190  if(l1)
1191  ++nband;
1192  if(a1)
1193  ++nband;
1194  if(l2)
1195  ++nband;
1196  if(a2)
1197  ++nband;
1198  output.resize(nband);
1199  for(int iband=0;iband<output.size();++iband)
1200  output[iband].resize(input.nRows(),input.nCols());
1201  if(maxDistance<=0)
1202  maxDistance=sqrt(static_cast<float>(input.nRows()*input.nCols()));
1203  int indexI=0;
1204  int indexJ=0;
1205  const char* pszMessage;
1206  void* pProgressArg=NULL;
1207  GDALProgressFunc pfnProgress=GDALTermProgress;
1208  double progress=0;
1209  pfnProgress(progress,pszMessage,pProgressArg);
1210  for(int y=0;y<input.nRows();++y){
1211  for(int x=0;x<input.nCols();++x){
1212  float currentValue=input[y][x];
1213  //find values equal to current value with some error margin
1214  //todo: add distance for two opposite directions
1215  float lineDistance1=0;//longest line of object
1216  float lineDistance2=maxDistance;//shortest line of object
1217  float lineAngle1=0;//angle to longest line (North=0)
1218  float lineAngle2=0;//angle to shortest line (North=0)
1219  float northAngle=0;//rotating angle
1220  for(northAngle=0;northAngle<180;northAngle+=angleStep){
1221  if(angle<=360&&angle>=0&&angle!=northAngle)
1222  continue;
1223  //test
1224  if(verbose)
1225  std::cout << "northAngle: " << northAngle << std::endl;
1226  float currentDistance=0;
1227  float theDir=0;
1228  for(short side=0;side<=1;side+=1){
1229  theDir=PI/2.0-DEG2RAD(northAngle)+side*PI;//in radians
1230  //test
1231  if(verbose)
1232  std::cout << "theDir in deg: " << RAD2DEG(theDir) << std::endl;
1233  if(theDir<0)
1234  theDir+=2*PI;
1235  //test
1236  if(verbose)
1237  std::cout << "theDir in deg: " << RAD2DEG(theDir) << std::endl;
1238  float nextValue=currentValue;
1239  for(float currentRay=1;currentRay<maxDistance;++currentRay){
1240  indexI=x+currentRay*cos(theDir);
1241  indexJ=y-currentRay*sin(theDir);
1242  if(indexJ<0||indexJ>=input.size())
1243  break;
1244  if(indexI<0||indexI>=input[indexJ].size())
1245  break;
1246  nextValue=input[indexJ][indexI];
1247  if(verbose){
1248  std::cout << "x: " << x << std::endl;
1249  std::cout << "y: " << y << std::endl;
1250  std::cout << "currentValue: " << currentValue << std::endl;
1251  std::cout << "theDir in degrees: " << RAD2DEG(theDir) << std::endl;
1252  std::cout << "cos(theDir): " << cos(theDir) << std::endl;
1253  std::cout << "sin(theDir): " << sin(theDir) << std::endl;
1254  std::cout << "currentRay: " << currentRay << std::endl;
1255  std::cout << "currentDistance: " << currentDistance << std::endl;
1256  std::cout << "indexI: " << indexI << std::endl;
1257  std::cout << "indexJ: " << indexJ << std::endl;
1258  std::cout << "nextValue: " << nextValue << std::endl;
1259  }
1260  if(fabs(currentValue-nextValue)<=eps){
1261  ++currentDistance;
1262  //test
1263  if(verbose)
1264  std::cout << "currentDistance: " << currentDistance << ", continue" << std::endl;
1265  }
1266  else{
1267  if(verbose)
1268  std::cout << "currentDistance: " << currentDistance << ", break" << std::endl;
1269  break;
1270  }
1271  }
1272  }
1273  if(lineDistance1<currentDistance){
1274  lineDistance1=currentDistance;
1275  lineAngle1=northAngle;
1276  }
1277  if(lineDistance2>currentDistance){
1278  lineDistance2=currentDistance;
1279  lineAngle2=northAngle;
1280  }
1281  if(verbose){
1282  std::cout << "lineDistance1: " << lineDistance1 << std::endl;
1283  std::cout << "lineAngle1: " << lineAngle1 << std::endl;
1284  std::cout << "lineDistance2: " << lineDistance2 << std::endl;
1285  std::cout << "lineAngle2: " << lineAngle2 << std::endl;
1286  }
1287  }
1288  int iband=0;
1289  if(l1)
1290  output[iband++][y][x]=lineDistance1;
1291  if(a1)
1292  output[iband++][y][x]=lineAngle1;
1293  if(l2)
1294  output[iband++][y][x]=lineDistance2;
1295  if(a2)
1296  output[iband++][y][x]=lineAngle2;
1297  assert(iband==nband);
1298  }
1299  progress=(1.0+y);
1300  progress/=input.nRows();
1301  pfnProgress(progress,pszMessage,pProgressArg);
1302  }
1303 }