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