21 #ifndef otbLineSegmentDetector_hxx
22 #define otbLineSegmentDetector_hxx
25 #include "itkImageRegionIterator.h"
26 #include "itkMinimumMaximumImageCalculator.h"
27 #include "itkImageConstIterator.h"
28 #include "itkNeighborhoodIterator.h"
30 #include "itkCastImageFilter.h"
36 #include "itkMatrix.h"
37 #include "itkSymmetricEigenAnalysis.h"
38 #include "vcl_legacy_aliases.h"
42 extern "C" double dbetai_(
double* x,
double* a,
double* b);
47 template <
class TInputImage,
class TPrecision>
50 this->SetNumberOfRequiredInputs(1);
51 this->SetNumberOfRequiredOutputs(1);
53 m_DirectionsAllowed = 1. / 8.;
54 m_Prec =
CONST_PI * m_DirectionsAllowed;
58 m_GradientFilter = GradientFilterType::New();
59 m_MagnitudeFilter = MagnitudeFilterType::New();
60 m_OrientationFilter = OrientationFilterType::New();
64 m_UsedPointImage = LabelImageType::New();
67 template <
class TInputImage,
class TPrecision>
70 this->Superclass::SetNthInput(0,
const_cast<InputImageType*
>(input));
73 template <
class TInputImage,
class TPrecision>
76 if (this->GetNumberOfInputs() < 1)
81 return static_cast<const InputImageType*
>(this->Superclass::GetInput(0));
84 template <
class TInputImage,
class TPrecision>
88 Superclass::GenerateInputRequestedRegion();
91 typename InputImageType::Pointer input =
const_cast<InputImageType*
>(this->GetInput());
100 input->SetRequestedRegionToLargestPossibleRegion();
103 template <
class TInputImage,
class TPrecision>
106 if (this->GetInput()->GetRequestedRegion() != this->GetInput()->GetLargestPossibleRegion())
108 itkExceptionMacro(<<
"Not streamed filter. ERROR : requested region is not the largest possible region.");
112 m_UsedPointImage->SetRegions(this->GetInput()->GetLargestPossibleRegion());
113 m_UsedPointImage->Allocate();
114 m_UsedPointImage->FillBuffer(0);
118 typedef itk::CastImageFilter<InputImageType, OutputImageType> castFilerType;
119 typename castFilerType::Pointer castFilter = castFilerType::New();
120 castFilter->SetInput(this->GetInput());
124 m_GradientFilter->SetInput(castFilter->GetOutput());
125 m_GradientFilter->SetSigma(0.6);
126 m_MagnitudeFilter->SetInput(m_GradientFilter->GetOutput());
127 m_OrientationFilter->SetInput(m_GradientFilter->GetOutput());
130 m_MagnitudeFilter->Update();
131 m_OrientationFilter->Update();
134 CoordinateHistogramType CoordinateHistogram;
135 CoordinateHistogram = this->SortImageByModulusValue(m_MagnitudeFilter->GetOutput());
138 this->LineSegmentDetection(CoordinateHistogram);
141 this->ComputeRectangles();
144 template <
class TInputImage,
class TPrecision>
148 RegionType largestRegion = this->GetInput()->GetLargestPossibleRegion();
151 double logNT = 5. * std::log10(
static_cast<double>(largestRegion.GetNumberOfPixels())) / 2.;
152 double log1_p = std::log10(m_DirectionsAllowed);
153 double rapport = logNT / log1_p;
154 m_MinimumRegionSize =
static_cast<unsigned int>(-rapport);
157 typedef itk::MinimumMaximumImageCalculator<OutputImageType> MinMaxCalculatorFilter;
158 typename MinMaxCalculatorFilter::Pointer minmaxCalculator = MinMaxCalculatorFilter::New();
160 minmaxCalculator->SetImage(modulusImage);
161 minmaxCalculator->ComputeMinimum();
162 OutputPixelType min = minmaxCalculator->GetMinimum();
163 minmaxCalculator->ComputeMaximum();
164 OutputPixelType max = minmaxCalculator->GetMaximum();
167 m_Threshold = m_Threshold * ((max - min) / 255.);
170 unsigned int NbBin = 1024;
171 double lengthBin =
static_cast<double>((max - min)) /
static_cast<double>(NbBin - 1);
177 SizeType size = modulusImage->GetRequestedRegion().GetSize();
178 InputIndexType
id = modulusImage->GetRequestedRegion().GetIndex();
182 if (modulusImage->GetRequestedRegion().GetIndex()[0] == 0)
186 if (modulusImage->GetRequestedRegion().GetSize()[0] + modulusImage->GetRequestedRegion().GetIndex()[0] == largestRegion.GetSize(0))
189 else if (modulusImage->GetRequestedRegion().GetSize()[0] + modulusImage->GetRequestedRegion().GetIndex()[0] == largestRegion.GetSize(0))
194 if (modulusImage->GetRequestedRegion().GetIndex()[1] == 0)
198 if (modulusImage->GetRequestedRegion().GetSize()[1] + modulusImage->GetRequestedRegion().GetIndex()[1] == largestRegion.GetSize(1))
201 else if (modulusImage->GetRequestedRegion().GetSize()[1] + modulusImage->GetRequestedRegion().GetIndex()[1] == largestRegion.GetSize(1))
207 region.SetSize(size);
209 itk::ImageRegionIterator<OutputImageType> it(modulusImage, region);
212 while (!it.IsAtEnd())
214 unsigned int bin =
static_cast<unsigned int>(
static_cast<double>(it.Value() - min) / lengthBin);
219 if (it.Value() - m_Threshold > 1e-10)
220 tempHisto[NbBin - bin - 1].push_back(it.GetIndex());
222 SetPixelToUsed(it.GetIndex());
235 template <
class TInputImage,
class TPrecision>
240 CoordinateHistogramIteratorType ItCoordinateList = CoordinateHistogram.begin();
242 while (ItCoordinateList != CoordinateHistogram.end())
244 typename IndexVectorType::iterator ItIndexVector = (*ItCoordinateList).begin();
245 while (ItIndexVector != (*ItCoordinateList).end())
247 InputIndexType index = *(ItIndexVector);
250 if (this->IsNotUsed(index))
252 IndexVectorType region;
253 double regionAngle = 0.;
255 bool fail = GrowRegion(index, region, regionAngle);
260 RectangleType rectangle = Region2Rectangle(region, regionAngle);
263 double nfa = ImproveRectangle(rectangle);
266 (double)region.size() /
267 (std::sqrt((rectangle[2] - rectangle[0]) * (rectangle[2] - rectangle[0]) + (rectangle[3] - rectangle[1]) * (rectangle[3] - rectangle[1])) *
278 m_RectangleList.push_back(rectangle);
282 SetRegionToNotIni(region);
298 template <
class TInputImage,
class TPrecision>
302 this->GetOutput(0)->SetMetaDataDictionary(this->GetInput()->GetMetaDataDictionary());
304 typename DataNodeType::Pointer root = this->GetOutput(0)->GetRoot();
306 typename DataNodeType::Pointer document = DataNodeType::New();
309 this->GetOutput(0)->Add(document, root);
311 typename DataNodeType::Pointer folder = DataNodeType::New();
314 this->GetOutput(0)->Add(folder, document);
315 this->GetOutput(0)->SetProjectionRef(this->GetInput()->GetProjectionRef());
318 SpacingType spacing = this->GetInput()->GetSignedSpacing();
319 OriginType origin = this->GetInput()->GetOrigin();
322 RectangleListTypeIterator itRec = m_RectangleList.begin();
323 while (itRec != m_RectangleList.end())
325 VertexType start, end;
328 start[0] = origin[0] +
static_cast<TPrecision
>((*itRec)[0]) * spacing[0];
329 start[1] = origin[1] +
static_cast<TPrecision
>((*itRec)[1]) * spacing[1];
331 end[0] = origin[0] +
static_cast<TPrecision
>((*itRec)[2]) * spacing[0];
332 end[1] = origin[1] +
static_cast<TPrecision
>((*itRec)[3]) * spacing[1];
334 typename DataNodeType::Pointer CurrentGeometry = DataNodeType::New();
335 CurrentGeometry->SetNodeId(
"FEATURE_LINE");
337 typename LineType::Pointer line = LineType::New();
338 CurrentGeometry->SetLine(line);
339 this->GetOutput(0)->Add(CurrentGeometry, folder);
340 CurrentGeometry->GetLine()->AddVertex(start);
341 CurrentGeometry->GetLine()->AddVertex(end);
354 template <
class TInputImage,
class TPrecision>
357 RectangleIteratorType itSrc = rSrc.begin();
359 while (itSrc != rSrc.end())
361 rDst.push_back(*(itSrc));
372 template <
class TInputImage,
class TPrecision>
378 double delta_2 = delta / 2.0;
381 double nfa_rect = this->ComputeRectNFA(rec);
387 CopyRectangle(r, rec);
388 for (n = 0; n < 5; ++n)
392 nfa_new = this->ComputeRectNFA(r);
393 if (nfa_new > nfa_rect)
396 CopyRectangle(rec, r);
404 CopyRectangle(r, rec);
405 for (n = 0; n < 5; ++n)
408 nfa_new = this->ComputeRectNFA(r);
409 if (nfa_new > nfa_rect)
412 CopyRectangle(rec, r);
419 CopyRectangle(r, rec);
420 for (n = 0; n < 5; ++n)
422 if ((r[4] - delta) >= 0.5)
424 r[0] += -std::sin(r[5]) * delta_2;
425 r[1] += std::cos(r[5]) * delta_2;
426 r[2] += -std::sin(r[5]) * delta_2;
427 r[3] += std::cos(r[5]) * delta_2;
430 nfa_new = this->ComputeRectNFA(r);
431 if (nfa_new > nfa_rect)
434 CopyRectangle(rec, r);
441 CopyRectangle(r, rec);
442 for (n = 0; n < 5; ++n)
444 if ((r[4] - delta) >= 0.5)
446 r[0] -= -std::sin(r[5]) * delta_2;
447 r[1] -= std::cos(r[5]) * delta_2;
448 r[2] -= -std::sin(r[5]) * delta_2;
449 r[3] -= std::cos(r[5]) * delta_2;
452 nfa_new = this->ComputeRectNFA(r);
453 if (nfa_new > nfa_rect)
456 CopyRectangle(rec, r);
464 CopyRectangle(r, rec);
465 for (n = 0; n < 5; ++n)
469 nfa_new = this->ComputeRectNFA(r);
470 if (nfa_new > nfa_rect)
473 CopyRectangle(rec, r);
485 template <
class TInputImage,
class TPrecision>
488 bool isNotUsed =
false;
490 typedef itk::ImageConstIterator<LabelImageType> ImageIteratorType;
491 RegionType region = m_UsedPointImage->GetLargestPossibleRegion();
492 InputIndexType indexRef = region.GetIndex();
493 ImageIteratorType itLabel(m_UsedPointImage, region);
496 if (m_UsedPointImage->GetLargestPossibleRegion().IsInside(index))
498 itLabel.SetIndex(index);
499 if (itLabel.Get() == 0)
504 itkExceptionMacro(<<
"Can't access to index " << index <<
", outside the image largest region (" << indexRef <<
", " << region.GetSize() <<
")");
514 template <
class TInputImage,
class TPrecision>
519 typedef itk::ImageConstIterator<LabelImageType> ImageIteratorType;
520 RegionType region = m_UsedPointImage->GetLargestPossibleRegion();
521 InputIndexType indexRef = region.GetIndex();
522 ImageIteratorType itLabel(m_UsedPointImage, region);
525 if (m_UsedPointImage->GetLargestPossibleRegion().IsInside(index))
527 itLabel.SetIndex(index);
528 if (itLabel.Get() == 255)
533 itkExceptionMacro(<<
"Can't access to index " << index <<
", outside the image largest region (" << indexRef <<
", " << region.GetSize() <<
")");
544 template <
class TInputImage,
class TPrecision>
547 bool isNotIni =
false;
549 typedef itk::ImageConstIterator<LabelImageType> ImageIteratorType;
550 RegionType region = m_UsedPointImage->GetLargestPossibleRegion();
551 InputIndexType indexRef = region.GetIndex();
552 ImageIteratorType itLabel(m_UsedPointImage, region);
555 if (m_UsedPointImage->GetLargestPossibleRegion().IsInside(index))
557 itLabel.SetIndex(index);
558 if (itLabel.Get() == 127)
563 itkExceptionMacro(<<
"Can't access to index " << index <<
", outside the image largest region (" << indexRef <<
", " << region.GetSize() <<
")");
574 template <
class TInputImage,
class TPrecision>
577 typedef itk::NeighborhoodIterator<LabelImageType> NeighborhoodLabelIteratorType;
578 typename NeighborhoodLabelIteratorType::SizeType radiusLabel;
580 NeighborhoodLabelIteratorType itLabel(radiusLabel, m_UsedPointImage, m_UsedPointImage->GetRequestedRegion());
581 itLabel.SetLocation(index);
582 itLabel.SetCenterPixel(255);
591 template <
class TInputImage,
class TPrecision>
594 typedef itk::NeighborhoodIterator<LabelImageType> NeighborhoodLabelIteratorType;
595 typename NeighborhoodLabelIteratorType::SizeType radiusLabel;
597 NeighborhoodLabelIteratorType itLabel(radiusLabel, m_UsedPointImage, m_UsedPointImage->GetRequestedRegion());
598 itLabel.SetLocation(index);
599 itLabel.SetCenterPixel(127);
608 template <
class TInputImage,
class TPrecision>
611 IndexVectorIteratorType it = region.begin();
612 while (it != region.end())
614 this->SetPixelToNotIni(*it);
626 template <
class TInputImage,
class TPrecision>
631 this->SetPixelToUsed(index);
634 typedef itk::ConstNeighborhoodIterator<OutputImageType> NeighborhoodIteratorType;
635 typename NeighborhoodIteratorType::SizeType radius;
637 NeighborhoodIteratorType itNeigh(radius, m_MagnitudeFilter->GetOutput(), m_MagnitudeFilter->GetOutput()->GetRequestedRegion());
638 NeighborhoodIteratorType itNeighDir(radius, m_OrientationFilter->GetOutput(), m_OrientationFilter->GetOutput()->GetRequestedRegion());
642 unsigned int neighSize = itNeigh.GetSize()[0] * itNeigh.GetSize()[1];
645 region.push_back(index);
652 for (
unsigned int cpt = 0; cpt < region.size(); ++cpt)
654 itNeigh.SetLocation(region[cpt]);
655 itNeighDir.SetLocation(region[cpt]);
656 sumX += std::cos(*(itNeighDir.GetCenterValue()));
657 sumY += std::sin(*(itNeighDir.GetCenterValue()));
658 regionAngle = std::atan2(sumY, sumX);
662 while (s < neighSize)
664 InputIndexType NeighIndex = itNeigh.GetIndex(s);
665 double angleComp = itNeighDir.GetPixel(s);
669 if ((this->IsNotUsed(NeighIndex) || this->IsNotIni(NeighIndex)) && this->IsAligned(angleComp, regionAngle, m_Prec))
671 this->SetPixelToUsed(NeighIndex);
672 region.push_back(NeighIndex);
680 unsigned int nbPixels = this->GetInput()->GetLargestPossibleRegion().GetNumberOfPixels();
681 if (region.size() > m_MinimumRegionSize && region.size() < nbPixels / 4)
696 template <
class TInputImage,
class TPrecision>
699 double diff = Angle - regionAngle;
718 template <
class TInputImage,
class TPrecision>
724 double weight = 0., sumWeight = 0.;
725 double x = 0., y = 0.;
726 double l_min = 0., l_max = 0., l = 0., w = 0., w_min = 0., w_max = 0.;
729 typedef itk::ConstNeighborhoodIterator<OutputImageType> NeighborhoodIteratorType;
730 typename NeighborhoodIteratorType::SizeType radius;
732 NeighborhoodIteratorType itNeigh(radius, m_MagnitudeFilter->GetOutput(), m_MagnitudeFilter->GetOutput()->GetRequestedRegion());
736 IndexVectorIteratorType it = region.begin();
737 while (it != region.end())
739 itNeigh.SetLocation(*it);
740 weight = *itNeigh.GetCenterValue();
741 x +=
static_cast<double>((*it)[0]) * weight;
742 y +=
static_cast<double>((*it)[1]) * weight;
746 if (sumWeight < 1e-10)
759 double theta = this->ComputeRegionOrientation(region, x, y, regionAngle);
762 typedef std::vector<MagnitudePixelType> MagnitudeVector;
764 RegionType largestRegion = this->GetInput()->GetLargestPossibleRegion();
765 unsigned int Diagonal =
766 static_cast<unsigned int>(vnl_math_hypot(
static_cast<double>(largestRegion.GetSize(1)),
static_cast<double>(largestRegion.GetSize(0))) + 2);
768 MagnitudeVector sum_l(2 * Diagonal, itk::NumericTraits<MagnitudePixelType>::Zero);
769 MagnitudeVector sum_w(2 * Diagonal, itk::NumericTraits<MagnitudePixelType>::Zero);
771 double dx = std::cos(theta);
772 double dy = std::sin(theta);
775 while (it != region.end())
777 itNeigh.SetLocation(*it);
778 weight = *itNeigh.GetCenterValue();
779 l = (
static_cast<double>((*it)[0]) - x) * dx + (
static_cast<double>((*it)[1]) - y) * dy;
780 w = -(
static_cast<double>((*it)[0]) - x) * dy + (
static_cast<double>((*it)[1]) - y) * dx;
791 sum_l[
static_cast<int>(std::floor(l) + 0.5) + Diagonal] +=
static_cast<MagnitudePixelType
>(weight);
792 sum_w[
static_cast<int>(std::floor(w) + 0.5) + Diagonal] +=
static_cast<MagnitudePixelType
>(weight);
798 double sum_th = 0.01 * sumWeight;
802 for (s = 0.0, i =
static_cast<int>(l_min); s < sum_th && i <= static_cast<int>(l_max); ++i)
803 s += sum_l[Diagonal + i];
805 double lb = (
static_cast<double>(i - 1) - 0.5);
807 for (s = 0.0, i =
static_cast<int>(l_max); s < sum_th && i >=
static_cast<int>(l_min); i--)
808 s += sum_l[Diagonal + i];
809 double lf = (
static_cast<double>(i + 1) + 0.5);
811 for (s = 0.0, i =
static_cast<int>(w_min); s < sum_th && i <= static_cast<int>(w_max); ++i)
812 s += sum_w[Diagonal + i];
814 double wr = (
static_cast<double>(i - 1) - 0.5);
816 for (s = 0.0, i =
static_cast<int>(w_max); s < sum_th && i >=
static_cast<int>(w_min); i--)
817 s += sum_w[Diagonal + i];
819 double wl = (
static_cast<double>(i + 1) + 0.5);
831 RectangleType rec(8, 0.);
834 if (std::abs(wl - wr) -
835 std::sqrt(
static_cast<double>(largestRegion.GetSize(0) * largestRegion.GetSize(0) + largestRegion.GetSize(1) * largestRegion.GetSize(1))) <
838 rec[0] = (x + lb * dx > 0) ? x + lb * dx : 0.;
839 rec[1] = (y + lb * dy > 0) ? y + lb * dy : 0.;
840 rec[2] = (x + lf * dx > 0) ? x + lf * dx : 0.;
841 rec[3] = (y + lf * dy > 0) ? y + lf * dy : 0;
845 rec[7] = m_DirectionsAllowed;
847 if (rec[4] - 1. < 1e-10)
859 template <
class TInputImage,
class TPrecision>
867 double weight = 0., sum = 0.;
870 typedef itk::ConstNeighborhoodIterator<OutputImageType> NeighborhoodIteratorType;
871 typename NeighborhoodIteratorType::SizeType radius;
873 NeighborhoodIteratorType itNeigh(radius, m_MagnitudeFilter->GetOutput(), m_MagnitudeFilter->GetOutput()->GetRequestedRegion());
877 IndexVectorIteratorType it = region.begin();
878 while (it != region.end())
880 itNeigh.SetLocation(*it);
881 weight = *itNeigh.GetCenterValue();
882 double Iyy2 =
static_cast<double>((*it)[0]) - x;
883 double Ixx2 =
static_cast<double>((*it)[1]) - y;
886 Ixx += Ixx2 * Ixx2 * weight;
887 Iyy += Iyy2 * Iyy2 * weight;
888 Ixy -= Ixx2 * Iyy2 * weight;
894 typedef itk::Matrix<double, 2, 2> MatrixType;
895 typedef std::vector<double> MatrixEigenType;
896 MatrixType Inertie, eigenVector;
897 MatrixEigenType eigenMatrix(2, 0.);
903 typedef itk::SymmetricEigenAnalysis<MatrixType, MatrixEigenType> EigenAnalysisType;
904 EigenAnalysisType eigenFilter(2);
905 eigenFilter.ComputeEigenValuesAndVectors(Inertie, eigenMatrix, eigenVector);
906 theta = std::atan2(eigenVector[1][1], -eigenVector[1][0]);
912 if (this->angle_diff(theta, angleRegion) > m_Prec)
923 template <
class TInputImage,
class TPrecision>
942 template <
class TInputImage,
class TPrecision>
955 OTBRectangleType::Pointer rectangle = OTBRectangleType::New();
958 for (
int i = 0; i < 2; ++i)
960 typename OTBRectangleType::VertexType vertex;
961 vertex[0] = rec[2 * i];
962 vertex[1] = rec[2 * i + 1];
963 rectangle->AddVertex(vertex);
965 rectangle->SetWidth(rec[4]);
966 rectangle->SetOrientation(rec[5]);
970 OutputImageDirRegionType region = m_OrientationFilter->GetOutput()->GetLargestPossibleRegion();
971 region.Crop(rectangle->GetBoundingRegion());
975 itk::ImageRegionIterator<OutputImageDirType> it(m_OrientationFilter->GetOutput(), region);
980 while (!it.IsAtEnd())
982 if (rectangle->IsInside(it.GetIndex()) && m_OrientationFilter->GetOutput()->GetBufferedRegion().IsInside(it.GetIndex()))
986 if (this->IsAligned(it.Get(), rec[5] , rec[6] ))
993 RegionType largestRegion =
const_cast<Self*
>(
this)->GetInput()->GetLargestPossibleRegion();
994 double logNT = 5. * std::log10(
static_cast<double>(largestRegion.GetNumberOfPixels())) / 2.;
997 nfa_val = NFA(pts, NbAligned, m_DirectionsAllowed, logNT);
1011 template <
class TInputImage,
class TPrecision>
1015 double n_d =
static_cast<double>(n);
1016 double k_d =
static_cast<double>(k);
1023 double b = n_d - k_d + 1.0;
1024 val = -logNT - log10(
dbetai_(&p, &a, &b));
1026 if (vnl_math_isinf(val))
1028 double x1 = n_d + 1.0;
1029 double x2 = k_d + 1.0;
1030 double x3 = n_d - k_d + 1.0;
1040 template <
class TInputImage,
class TPrecision>
1043 Superclass::PrintSelf(os, indent);
virtual const InputImageType * GetInput(void)
virtual void SetInput(const InputImageType *input)
virtual void SetPixelToNotIni(InputIndexType index)
virtual bool IsNotUsed(InputIndexType &index) const
virtual double ImproveRectangle(RectangleType &rectangle) const
virtual bool IsUsed(InputIndexType &index) const
virtual RectangleType Region2Rectangle(IndexVectorType region, double regionAngle)
virtual void SetPixelToUsed(InputIndexType index)
void GenerateData() override
virtual int ComputeRectangles()
virtual bool IsNotIni(InputIndexType &index) const
virtual void CopyRectangle(RectangleType &rDst, RectangleType &rSrc) const
virtual double angle_diff(double a, double b) const
void GenerateInputRequestedRegion() override
std::vector< double > RectangleType
virtual CoordinateHistogramType SortImageByModulusValue(MagnitudeImagePointerType modulusImage)
virtual double ComputeRegionOrientation(IndexVectorType region, double x, double y, double angleRegion) const
virtual bool IsAligned(double Angle, double regionAngle, double prec) const
std::vector< IndexVectorType > CoordinateHistogramType
TInputImage InputImageType
void PrintSelf(std::ostream &os, itk::Indent indent) const override
virtual void SetRegionToNotIni(IndexVectorType region)
virtual bool GrowRegion(InputIndexType index, IndexVectorType ®ion, double ®ionAngle)
virtual double NFA(int n, int k, double p, double logNT) const
virtual void LineSegmentDetection(CoordinateHistogramType &CoordinateHistogram)
virtual double ComputeRectNFA(const RectangleType &rec) const
This class represent a Rectangle.
The "otb" namespace contains all Orfeo Toolbox (OTB) classes.
constexpr double CONST_LN10
constexpr double CONST_PI
constexpr double CONST_2PI
double dbetai_(double *x, double *a, double *b)
double dlngam_(double *x)