libcrn  3.9.5
A document image processing library
•All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
CRNGradientShapeContext.h
Go to the documentation of this file.
1 /* Copyright 2015 Université Paris Descartes
2  *
3  * This file is part of libcrn.
4  *
5  * libcrn is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * libcrn 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 Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with libcrn. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * file: CRNGradientShapeContext.h
19  * \author Yann LEYDIER
20  */
21 
22 #ifndef CRNGradientShapeContext_H
23 #define CRNGradientShapeContext_H
24 
27 #include <CRNAI/CRNkMedoids.h>
28 #include <CRNAI/CRNBipartite.h>
29 #include <array>
30 #include <vector>
31 #include <algorithm>
32 
33 namespace crn
34 {
35  /****************************************************************************/
42  template<size_t ngrad, size_t ntheta, size_t nrho> class GradientShapeContext
43  {
44  public:
45  static constexpr auto size = ngrad * ntheta * nrho;
46  static constexpr auto toffset = ngrad;
47  static constexpr auto roffset = toffset * ntheta;
48 
49  struct SC
50  {
51  SC(): X(0), Y(0) { std::fill(histo.begin(), histo.end(), 0); }
52  SC(const Point2DInt &p): X(p.X), Y(p.Y) { std::fill(histo.begin(), histo.end(), 0); }
53  SC(const SC&) = default;
54  SC(SC&&) = default;
55  SC& operator=(const SC&) = default;
56  SC& operator=(SC&&) = default;
57  int X, Y;
58  std::array<int, size> histo;
59  };
60 
61  static std::vector<SC> CreateFixed(const ImageGradient &igr, size_t npoints, size_t ndummy);
62  static std::vector<SC> CreateRatio(const ImageGradient &igr, size_t divisor, size_t ndummy);
63  static double Distance(const std::vector<SC> &img1, const std::vector<SC> &img2);
64 
65  private:
66  template<typename Sc> static int dist(const Sc &p1, const Sc &p2);
67  };
68 
69  template<size_t ngrad, size_t ntheta, size_t nrho>
70  std::vector<typename GradientShapeContext<ngrad, ntheta, nrho>::SC> GradientShapeContext<ngrad, ntheta, nrho>::CreateFixed(const ImageGradient &igr, size_t npoints, size_t ndummy)
71  {
72  // collect significant points
73  auto pts = std::vector<Point2DInt>{};
74  FOREACHPIXEL(x, y, igr)
75  if (igr.IsSignificant(x, y))
76  pts.emplace_back(int(x), int(y));
77  auto distmat = std::vector<std::vector<double>>(pts.size(), std::vector<double>(pts.size(), 0));
78  for (auto p1 : Range(pts))
79  for (auto p2 = p1 + 1; p2 < pts.size(); ++p2)
80  distmat[p1][p2] = distmat[p2][p1] = sqrt(Sqr(pts[p1].X - pts[p2].X) + Sqr(pts[p1].Y - pts[p2].Y));
81  // select a few points
82  auto meds = std::vector<size_t>{};
83  if (pts.size() <= npoints)
84  {
85  meds.reserve(pts.size());
86  std::iota(meds.begin(), meds.end(), 0);
87  }
88  else
89  meds = std::move(std::get<2>(kmedoids::Run(kmedoids::init::Central{npoints}, kmedoids::update::Local{}, distmat, 100)));
90 
91  // compute context for the selected points
92  auto res = std::vector<SC>{};
93  for (auto tp : Range(meds))
94  {
95  const auto p = meds[tp];
96  auto refp = SC{pts[p]};
97  const auto maxdist = log(1 + *std::max_element(distmat[p].begin(), distmat[p].end())) + 1;
98  for (auto p2 : Range(pts))
99  {
100  if (p == p2)
101  continue;
102  const auto &op = pts[p2];
103  const auto vec = pixel::Polar2D<double, Angle<ByteAngle>>(pixel::Cart2D<int>(refp.X - op.X, refp.Y - op.Y));
104  const auto r = size_t(log(1 + vec.rho) * nrho / maxdist);
105  const auto t = size_t(vec.theta.value * ntheta / 256);
106  refp.histo[r * roffset + t * toffset + igr.At(op.X, op.Y).theta.value * ngrad / 256] += 1;
107  }
108  // normalize
109  for (auto &g : refp.histo)
110  {
111  g = (g * 1000) / int(pts.size());
112  }
113 
114  res.push_back(std::move(refp));
115  }
116  // add dummy points
117  res.insert(res.end(), ndummy, SC{});
118  return res;
119  }
120 
121  template<size_t ngrad, size_t ntheta, size_t nrho>
122  std::vector<typename GradientShapeContext<ngrad, ntheta, nrho>::SC> GradientShapeContext<ngrad, ntheta, nrho>::CreateRatio(const ImageGradient &igr, size_t divisor, size_t ndummy)
123  {
124  auto npoints = size_t(0);
125  for (auto tmp : Range(igr))
126  if (igr.IsSignificant(tmp))
127  npoints += 1;
128  return CreateFixed(igr, crn::Max(size_t(1), npoints / divisor), ndummy);
129  }
130 
131  template<size_t ngrad, size_t ntheta, size_t nrho>
133  {
134  const auto s = Max(img1.size(), img2.size());
135  auto distmat = std::vector<std::vector<double>>(s, std::vector<double>(s, 0));
136  for (auto p1 : Range(img1))
137  for (auto p2 : Range(img2))
138  distmat[p1][p2] = dist(img1[p1], img2[p2]);
139  return std::get<0>(Hungarian(distmat)) / double(Min(img1.size(), img2.size()));
140  }
141 
142  template<size_t ngrad, size_t ntheta, size_t nrho>
143  template<typename Sc> int GradientShapeContext<ngrad, ntheta, nrho>::dist(const Sc &p1, const Sc &p2)
144  {
145  if (p1.X == 0 && p1.Y == 0)
146  return 0;
147  if (p2.X == 0 && p2.Y == 0)
148  return 0;
149  auto diff = 0;
150  for (auto tmp : Range(p1.histo))
151  diff += Abs(p1.histo[tmp] - p2.histo[tmp]);
152  return diff;
153  }
154 }
155 
156 #endif
157 
ScalarRange< T > Range(T b, T e)
Creates a range [[b, e[[.
Definition: CRNType.h:257
std::vector< pixel_type >::reference At(size_t x, size_t y) noexcept
Returns a reference to a pixel.
Definition: CRNImage.h:224
const T & Max(const T &a, const T &b)
Returns the max of two values.
Definition: CRNMath.h:47
#define FOREACHPIXEL(x, y, img)
Convenience macro to sweep an image.
Definition: CRNImage.h:37
static std::vector< SC > CreateFixed(const ImageGradient &igr, size_t npoints, size_t ndummy)
Finds the k most central elements.
Definition: CRNkMedoids.h:46
static std::vector< SC > CreateRatio(const ImageGradient &igr, size_t divisor, size_t ndummy)
static double Distance(const std::vector< SC > &img1, const std::vector< SC > &img2)
SC & operator=(const SC &)=default
constexpr SumType< T > Sqr(const T &v) noexcept(noexcept(v *v))
Returns the square of a value.
Definition: CRNMath.h:61
Gradient image in polar form.
bool IsSignificant(size_t i) const
Tests if a pixel has a significant gradient.
std::tuple< double, std::vector< std::pair< size_t, size_t > > > Hungarian(const std::vector< std::vector< double >> &distmat)
void Abs(Image< T > &img, typename std::enable_if< std::is_arithmetic< T >::value >::type *dummy=nullptr) noexcept
Replaces each pixel by its absolute value.
Definition: CRNImageGray.h:47
const T & Min(const T &a, const T &b)
Returns the min of two values.
Definition: CRNMath.h:49
Gets the element with the lower distance to other elements in the cluster.
Definition: CRNkMedoids.h:72
std::tuple< std::vector< size_t >, std::vector< std::multimap< double, size_t > >, std::vector< size_t > > Run(Init init, Update update, const std::vector< std::vector< double >> &distmat, size_t maxiter=std::numeric_limits< size_t >::max())
k medoids
Definition: CRNkMedoids.h:95
A 2D point class.
Definition: CRNPoint2DInt.h:39
Gradient shape context factory.