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 "GridHealerAbstractBase.h"
10 #include "GridLog.h"
11 #include "GridTriangleFill.h"
12 #include "Logger.h"
13 #include "Pixels.h"
14 #include <QFile>
15 #include <QImage>
16 #include <qmath.h>
17 #include <QRgb>
18 #include <QTextStream>
19
GridHealerAbstractBase(GridLog & gridLog,const DocumentModelGridRemoval & modelGridRemoval)20 GridHealerAbstractBase::GridHealerAbstractBase(GridLog &gridLog,
21 const DocumentModelGridRemoval &modelGridRemoval) :
22 m_modelGridRemoval (modelGridRemoval),
23 m_maxPointSeparation (0),
24 m_gridLog (gridLog)
25 {
26 }
27
~GridHealerAbstractBase()28 GridHealerAbstractBase::~GridHealerAbstractBase()
29 {
30 }
31
addMutualPair(int x0,int y0,int x1,int y1)32 void GridHealerAbstractBase::addMutualPair (int x0,
33 int y0,
34 int x1,
35 int y1)
36 {
37 m_mutualPairHalvesBelow.push_back (QPoint (x0, y0));
38 m_mutualPairHalvesAbove.push_back (QPoint (x1, y1));
39 }
40
fillTrapezoid(QImage & image,int xBL,int yBL,int xBR,int yBR,int xTR,int yTR,int xTL,int yTL)41 void GridHealerAbstractBase::fillTrapezoid (QImage &image,
42 int xBL, int yBL,
43 int xBR, int yBR,
44 int xTR, int yTR,
45 int xTL, int yTL)
46 {
47 // Sanity checks
48 if (xBL == 0 || yBL == 0 || xBR == 0 || yBR == 0 || xTR == 0 || yTR == 0 || xTL == 0 || yTL == 0) {
49 LOG4CPP_ERROR_S ((*mainCat)) << "GridHealerAbstractBase::fillTrapezoid received undefined corner coordinate "
50 << "xBL=" << xBL << " yBL=" << yBL << " xBR=" << xBR << " yBR=" << yBR
51 << "xTR=" << xTR << " yTR=" << yTR << " xTL=" << xTL << " yTL=" << yTL;
52 }
53
54 if (!Pixels::pixelIsBlack(image, xBL, yBL)) {
55 LOG4CPP_ERROR_S ((*mainCat)) << "GridHealerAbstractBase::fillTrapezoid has bad bottom left point";
56 }
57 if (!Pixels::pixelIsBlack(image, xBR, yBR)) {
58 LOG4CPP_ERROR_S ((*mainCat)) << "GridHealerAbstractBase::fillTrapezoid has bad bottom right point";
59 }
60 if (!Pixels::pixelIsBlack(image, xTR, yTR)) {
61 LOG4CPP_ERROR_S ((*mainCat)) << "GridHealerAbstractBase::fillTrapezoid has bad top right point";
62 }
63 if (!Pixels::pixelIsBlack(image, xTL, yTL)) {
64 LOG4CPP_ERROR_S ((*mainCat)) << "GridHealerAbstractBase::fillTrapezoid has bad top left point";
65 }
66
67 // Any quadrilateral (including this trapezoid) can be considered the union of two triangles
68 GridTriangleFill triangleFill;
69 triangleFill.fill (m_gridLog,
70 image,
71 QPoint (xBL, yBL),
72 QPoint (xBR, yBR),
73 QPoint (xTR, yTR));
74 triangleFill.fill (m_gridLog,
75 image,
76 QPoint (xBL, yBL),
77 QPoint (xTL, yTL),
78 QPoint (xTR, yTR));
79 }
80
gridLog()81 GridLog &GridHealerAbstractBase::gridLog ()
82 {
83 return m_gridLog;
84 }
85
healed(QImage & image)86 void GridHealerAbstractBase::healed (QImage &image)
87 {
88 applyMutualPairs (image);
89 doHealingAcrossGaps (image);
90 }
91
maxPointSeparation() const92 double GridHealerAbstractBase::maxPointSeparation () const
93 {
94 return m_maxPointSeparation;
95 }
96
modelGridRemoval()97 DocumentModelGridRemoval &GridHealerAbstractBase::modelGridRemoval()
98 {
99 return m_modelGridRemoval;
100 }
101
mutualPairHalvesAbove() const102 const MutualPairHalves &GridHealerAbstractBase::mutualPairHalvesAbove () const
103 {
104 return m_mutualPairHalvesAbove;
105 }
106
mutualPairHalvesBelow() const107 const MutualPairHalves &GridHealerAbstractBase::mutualPairHalvesBelow () const
108 {
109 return m_mutualPairHalvesBelow;
110 }
111
pixelCountInRegionThreshold(const DocumentModelGridRemoval & modelGridRemoval)112 int GridHealerAbstractBase::pixelCountInRegionThreshold (const DocumentModelGridRemoval &modelGridRemoval)
113 {
114 // For now we will use the close distance as the minimum pixel count
115 return qFloor (modelGridRemoval.closeDistance());
116 }
117
pointsAreGood(const QImage & image,int x0,int y0,int x1,int y1) const118 bool GridHealerAbstractBase::pointsAreGood (const QImage &image,
119 int x0,
120 int y0,
121 int x1,
122 int y1) const
123 {
124 Pixels pixels;
125
126 int stopCountAt = pixelCountInRegionThreshold (m_modelGridRemoval);
127
128 // Skip if either endpoint is an unwanted artifact. Look at start point below (since it is connected
129 // to the end point below), and the start point above (which is connected to the end point above)
130 return ((pixels.countBlackPixelsAroundPoint (image, x0, y0, stopCountAt) >= stopCountAt) &&
131 (pixels.countBlackPixelsAroundPoint (image, x1, y1, stopCountAt) >= stopCountAt));
132 }
133
saveGapSeparation(double gapSeparation)134 void GridHealerAbstractBase::saveGapSeparation (double gapSeparation)
135 {
136 // Right triangle with one edge crossing the gap (separation value) and hypotenuse giving
137 // maximum point separation (closest distance) gives the maximum horizontal separation
138 m_maxPointSeparation = qFloor (qSqrt (qPow (modelGridRemoval().closeDistance(), 2) -
139 qPow (gapSeparation, 2)));
140 }
141