Engauge Digitizer  2
 All Classes Files Functions Variables Enumerations Enumerator Friends Pages
GridHealer.cpp
1 #include "DocumentModelGridRemoval.h"
2 #include "EngaugeAssert.h"
3 #include "GridHealer.h"
4 #include "Logger.h"
5 #include <QImage>
6 #include <qmath.h>
7 #include <QRgb>
8 
9 // Group numbers start at this value. Each group is effectively its own pixel state
10 const BoundaryGroup BOUNDARY_GROUP_FIRST = 100;
11 
12 GridHealer::GridHealer(const QImage &imageBefore,
13  const DocumentModelGridRemoval &modelGridRemoval) :
14  m_boundaryGroupNext (BOUNDARY_GROUP_FIRST),
15  m_modelGridRemoval (modelGridRemoval)
16 {
17  LOG4CPP_INFO_S ((*mainCat)) << "GridHealer::GridHealer";
18 
19  // Prevent ambiguity between PixelState and the group numbers
20  ENGAUGE_ASSERT (NUM_PIXEL_STATES < BOUNDARY_GROUP_FIRST);
21 
22  m_pixels.resize (imageBefore.height());
23  for (int row = 0; row < imageBefore.height(); row++) {
24  m_pixels [row].resize (imageBefore.width());
25 
26  for (int col = 0; col < imageBefore.width(); col++) {
27 
28  QRgb rgb = imageBefore.pixel(col, row);
29  if (qGray (rgb) > 128) {
30  m_pixels [row] [col] = PIXEL_STATE_BACKGROUND;
31  } else {
32  m_pixels [row] [col] = PIXEL_STATE_FOREGROUND;
33  }
34  }
35  }
36 }
37 
38 void GridHealer::connectCloseGroups(QImage &imageToHeal)
39 {
40  LOG4CPP_INFO_S ((*mainCat)) << "GridHealer::connectCloseGroups";
41 
42  // N*(N-1)/2 search for groups that are close to each other
43  for (int iFrom = 0; iFrom < m_groupNumberToCentroid.count() - 1; iFrom++) {
44 
45  BoundaryGroup groupFrom = m_groupNumberToCentroid.keys().at (iFrom);
46 
47  ENGAUGE_ASSERT (m_groupNumberToCentroid.contains (groupFrom));
48  ENGAUGE_ASSERT (m_groupNumberToPixel.contains (groupFrom));
49 
50  QPointF posCentroidFrom = m_groupNumberToCentroid [groupFrom];
51  QPointF pixelPointFrom = m_groupNumberToPixel [groupFrom];
52 
53  for (int iTo = iFrom + 1; iTo < m_groupNumberToCentroid.count(); iTo++) {
54 
55  BoundaryGroup groupTo = m_groupNumberToCentroid.keys().at (iTo);
56 
57  ENGAUGE_ASSERT (m_groupNumberToCentroid.contains (groupTo));
58  ENGAUGE_ASSERT (m_groupNumberToPixel.contains (groupTo));
59 
60  QPointF posCentroidTo = m_groupNumberToCentroid [groupTo];
61  QPointF pixelPointTo = m_groupNumberToPixel [groupTo];
62 
63  QPointF separation = posCentroidFrom - posCentroidTo;
64  double separationMagnitude = qSqrt (separation.x() * separation.x() + separation.y() * separation.y());
65 
66  if (separationMagnitude < m_modelGridRemoval.closeDistance()) {
67 
68  // Draw line from pixelPointFrom to pixelPointTo
69  int count = 1 + qMax (qAbs (pixelPointFrom.x() - pixelPointTo.x()),
70  qAbs (pixelPointFrom.y() - pixelPointTo.y()));
71 
72  for (int index = 0; index < count; index++) {
73 
74  // Replace PIXEL_STATE_REMOVED by PIXEL_STATE_HEALED
75  double s = (double) index / (double) (count - 1);
76  int xCol = (int) (0.5 + (1.0 - s) * pixelPointFrom.y() + s * pixelPointTo.y());
77  int yRow = (int) (0.5 + (1.0 - s) * pixelPointFrom.x() + s * pixelPointTo.x());
78  m_pixels [yRow] [xCol] = PIXEL_STATE_HEALED;
79 
80  // Fill in the pixel
81  imageToHeal.setPixel (QPoint (xCol,
82  yRow),
83  Qt::black);
84  }
85  }
86  }
87  }
88 }
89 
90 void GridHealer::erasePixel (int xCol,
91  int yRow)
92 {
93  m_pixels [yRow] [xCol] = PIXEL_STATE_REMOVED;
94 
95  for (int rowOffset = -1; rowOffset <= 1; rowOffset++) {
96  int rowSearch = yRow + rowOffset;
97  if (0 <= rowSearch && rowSearch < m_pixels.count()) {
98 
99  for (int colOffset = -1; colOffset <= 1; colOffset++) {
100  int colSearch = xCol + colOffset;
101  if (0 <= colSearch && colSearch < m_pixels[0].count()) {
102 
103  if (m_pixels [rowSearch] [colSearch] == PIXEL_STATE_FOREGROUND) {
104 
105  m_pixels [rowSearch] [colSearch] = PIXEL_STATE_ADJACENT;
106 
107  }
108  }
109  }
110  }
111  }
112 }
113 
114 void GridHealer::groupContiguousAdjacentPixels()
115 {
116  LOG4CPP_INFO_S ((*mainCat)) << "GridHealer::groupContiguousAdjacentPixels";
117 
118  for (int row = 0; row < m_pixels.count(); row++) {
119  for (int col = 0; col < m_pixels [0].count(); col++) {
120 
121  if (m_pixels [row] [col] == PIXEL_STATE_ADJACENT) {
122 
123  // This adjacent pixel will be grouped together with all touching adjacent pixels.
124  // A centroid is calculated
125  int centroidCount = 0;
126  double rowCentroidSum = 0, colCentroidSum = 0;
127 
128  recursiveSearchForAdjacentPixels (m_boundaryGroupNext,
129  row,
130  col,
131  centroidCount,
132  rowCentroidSum,
133  colCentroidSum);
134 
135  // Save the centroid and a representative point in hash tables that are indexed by group number
136  m_groupNumberToCentroid [m_boundaryGroupNext] = QPointF (rowCentroidSum / centroidCount,
137  colCentroidSum / centroidCount);
138  m_groupNumberToPixel [m_boundaryGroupNext] = QPointF (row,
139  col);
140 
141  ++m_boundaryGroupNext;
142  }
143  }
144  }
145 }
146 
147 void GridHealer::heal (QImage &imageToHeal)
148 {
149  LOG4CPP_INFO_S ((*mainCat)) << "GridHealer::heal";
150 
151  groupContiguousAdjacentPixels ();
152  connectCloseGroups (imageToHeal);
153 }
154 
155 void GridHealer::recursiveSearchForAdjacentPixels (int boundaryGroup,
156  int row,
157  int col,
158  int &centroidCount,
159  double &rowCentroidSum,
160  double &colCentroidSum)
161 {
162  ENGAUGE_ASSERT (m_pixels [row] [col] == PIXEL_STATE_ADJACENT);
163 
164  // Merge coordinates into centroid
165  ++centroidCount;
166  rowCentroidSum += row;
167  colCentroidSum += col;
168 
169  m_pixels [row] [col] = boundaryGroup;
170 
171  for (int rowOffset = -1; rowOffset <= 1; rowOffset++) {
172  int rowNeighbor = row + rowOffset;
173  if (0 <= rowNeighbor && rowNeighbor < m_pixels.count()) {
174 
175  for (int colOffset = -1; colOffset <= 1; colOffset++) {
176  int colNeighbor = col + colOffset;
177  if (0 <= colNeighbor && colNeighbor < m_pixels[0].count()) {
178 
179  if (m_pixels [rowNeighbor] [colNeighbor] == PIXEL_STATE_ADJACENT) {
180 
181  recursiveSearchForAdjacentPixels (boundaryGroup,
182  rowNeighbor,
183  colNeighbor,
184  centroidCount,
185  rowCentroidSum,
186  colCentroidSum);
187  }
188  }
189  }
190  }
191  }
192 }
double closeDistance() const
Get method for close distance.
void erasePixel(int xCol, int yRow)
Remember that pixel was erased since it belongs to an grid line.
Definition: GridHealer.cpp:90
GridHealer(const QImage &imageBefore, const DocumentModelGridRemoval &modelGridRemoval)
Single constructor.
Definition: GridHealer.cpp:12
Model for DlgSettingsGridRemoval and CmdSettingsGridRemoval. The settings are unstable until the user...
void heal(QImage &imageToHeal)
Heal the broken curve lines by spanning the gaps across the newly-removed grid lines.
Definition: GridHealer.cpp:147