1 /******************************************************************************************************
2 * (C) 2018 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 "GridHealerHorizontal.h"
8 #include "GridIndependentToDependent.h"
9 #include "GridLog.h"
10 #include "Pixels.h"
11 #include <qmath.h>
12
GridHealerHorizontal(GridLog & gridLog,const DocumentModelGridRemoval & modelGridRemoval)13 GridHealerHorizontal::GridHealerHorizontal(GridLog &gridLog,
14 const DocumentModelGridRemoval &modelGridRemoval) :
15 GridHealerAbstractBase (gridLog,
16 modelGridRemoval)
17 {
18 }
19
applyMutualPairs(const QImage & image)20 void GridHealerHorizontal::applyMutualPairs (const QImage &image)
21 {
22 MutualPairHalves::const_iterator itrBelow = mutualPairHalvesBelow().begin();
23 MutualPairHalves::const_iterator itrAbove = mutualPairHalvesAbove().begin();
24
25 while (itrBelow != mutualPairHalvesBelow().end() &&
26 itrAbove != mutualPairHalvesAbove().end()) {
27
28 QPoint p0 = *(itrBelow++);
29 QPoint p1 = *(itrAbove++);
30
31 // Save (independent,dependent) pairs
32 if (Pixels::pixelIsBlack (image, p0.x(), p0.y())) {
33 m_blackPixelsBelow [p0.x()] = p0.y();
34 }
35
36 if (Pixels::pixelIsBlack (image, p1.x(), p1.y())) {
37 m_blackPixelsAbove [p1.x()] = p1.y();
38 }
39
40 saveGapSeparation (qAbs (p1.y() - p0.y()));
41 }
42 }
43
doHealingAcrossGaps(QImage & image)44 void GridHealerHorizontal::doHealingAcrossGaps (QImage &image)
45 {
46 // LOG4CPP_INFO_S is replaced by GridLog
47 GridIndependentToDependent::const_iterator itrBelow, itrAbove;
48 for (itrBelow = m_blackPixelsBelow.begin(); itrBelow != m_blackPixelsBelow.end(); itrBelow++) {
49 QPoint p (itrBelow.key(),
50 itrBelow.value());
51 gridLog().showInputPixel(p,
52 HALFWIDTH_HORIZONTAL);
53 }
54 for (itrAbove = m_blackPixelsAbove.begin(); itrAbove != m_blackPixelsAbove.end(); itrAbove++) {
55 QPoint p (itrAbove.key(),
56 itrAbove.value());
57 gridLog().showInputPixel(p,
58 HALFWIDTH_HORIZONTAL);
59 }
60
61 // Algorithm requires at least one point in each of the lists
62 if (m_blackPixelsBelow.count() > 0 &&
63 m_blackPixelsAbove.count() > 0) {
64
65 int xFirst = qMin (m_blackPixelsBelow.firstKey (),
66 m_blackPixelsAbove.firstKey ());
67 int xLast = qMax (m_blackPixelsBelow.lastKey (),
68 m_blackPixelsAbove.lastKey ());
69
70 int xBelowEnd = 0; // Used by inner loop to skip to this iterator value
71
72 for (int xBelowStart = xFirst; xBelowStart <= xLast; xBelowStart++) {
73
74 if ((xBelowEnd < xBelowStart) &&
75 m_blackPixelsBelow.contains (xBelowStart)) {
76
77 // This could be the start of a new trapezoid. Find where the range on the same side ends
78 int xBelowOutOfBounds = xLast + 1; // Value forcing transition to out of range
79
80 for (xBelowEnd = xBelowStart + 1; xBelowEnd <= xBelowOutOfBounds; xBelowEnd++) {
81
82 if (!m_blackPixelsBelow.contains (xBelowEnd) || (xBelowEnd == xBelowOutOfBounds)) {
83
84 doHealingOnBelowRange (image,
85 xBelowStart,
86 xBelowEnd,
87 qFloor (maxPointSeparation()));
88
89 // Go back to outer loop, which will skip to xBelowEnd
90 break;
91 }
92 }
93 }
94 }
95 }
96 }
97
doHealingOnBelowAndAboveRangePair(QImage & image,int xBelowStart,int xBelowEnd,int xAboveStart,int xAboveEnd)98 void GridHealerHorizontal::doHealingOnBelowAndAboveRangePair (QImage &image,
99 int xBelowStart,
100 int xBelowEnd,
101 int xAboveStart,
102 int xAboveEnd)
103 {
104 // LOG4CPP_INFO_S is replaced by GridLog
105
106 int x0 = xBelowStart;
107 int x1 = xBelowEnd;
108 int x2 = xAboveEnd;
109 int x3 = xAboveStart;
110 int y0 = m_blackPixelsBelow [xBelowStart];
111 int y1 = m_blackPixelsBelow [xBelowEnd ];
112 int y2 = m_blackPixelsAbove [xAboveEnd ];
113 int y3 = m_blackPixelsAbove [xAboveStart];
114
115 gridLog().showOutputTrapezoid (QPoint (x0, y0),
116 QPoint (x1, y1),
117 QPoint (x2, y2),
118 QPoint (x3, y3));
119
120 if (pointsAreGood (image, x0, y0, x2, y2)) {
121
122 // Big enough so keep it. Four points that define the trapezoid to be filled in
123 fillTrapezoid (image,
124 x0, y0,
125 x1, y1,
126 x2, y2,
127 x3, y3);
128 }
129 }
130
doHealingOnBelowRange(QImage & image,int xBelowStart,int xBelowEnd,int maxHorSep)131 void GridHealerHorizontal::doHealingOnBelowRange (QImage &image,
132 int xBelowStart,
133 int xBelowEnd,
134 int maxHorSep)
135 {
136 // LOG4CPP_INFO_S is replaced by GridLog
137
138 // Below range goes from xBelowStart (inclusive) to xBelowEnd (exclusive). There could
139 // be zero, one or more above ranges that overlap within maxHorSep, corresponding
140 // to an equal number of trapezoids to be filled in
141 //
142 // It is important to note that every above point between xBelowStart-maxHorSep to
143 // xBelowEnd+maxHorSep is close enough (<close distance) to a point in the below range
144
145 int xAboveOutOfBounds = xBelowEnd + maxHorSep + 1; // Value forcing transition to out of range
146
147 int xAboveEnd = 0; // Used by inner loop to skip to this iterator value
148
149 for (int xAboveStart = xBelowStart - maxHorSep; xAboveStart <= xAboveOutOfBounds; xAboveStart++) {
150
151 if ((xAboveEnd < xAboveStart) &&
152 m_blackPixelsAbove.contains (xAboveStart) &&
153 (xAboveStart < xAboveOutOfBounds)) {
154
155 for (xAboveEnd = xAboveStart + 1; xAboveEnd <= xAboveOutOfBounds; xAboveEnd++) {
156
157 if (!m_blackPixelsAbove.contains (xAboveEnd) || (xAboveEnd == xAboveOutOfBounds)) {
158
159 int xBelowStartNearEnough = qMax (xBelowStart, xAboveStart - maxHorSep);
160 int xBelowEndNearEnough = qMin (xBelowEnd - 1, xAboveEnd + maxHorSep);
161 int xAboveEndInclusive = xAboveEnd - 1;
162
163 if (xBelowStartNearEnough <= xBelowEndNearEnough) {
164
165 doHealingOnBelowAndAboveRangePair (image,
166 xBelowStartNearEnough,
167 xBelowEndNearEnough,
168 xAboveStart,
169 xAboveEndInclusive);
170
171 // Go back to outer loop, which will skip to xAboveEnd
172 break;
173 }
174 }
175 }
176 }
177 }
178 }
179