Engauge Digitizer  2
GridRemoval.cpp
Go to the documentation of this file.
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 
8 #include "EngaugeAssert.h"
9 #include "GridHealerHorizontal.h"
10 #include "GridHealerVertical.h"
11 #include "GridRemoval.h"
12 #include "Logger.h"
13 #include "Pixels.h"
14 #include <QImage>
15 #include <qmath.h>
16 #include "Transformation.h"
17 
18 const double EPSILON = 0.000001;
19 
20 GridRemoval::GridRemoval (bool isGnuplot) :
21  m_gridLog (isGnuplot)
22 {
23 }
24 
25 QPointF GridRemoval::clipX (const QPointF &posUnprojected,
26  double xBoundary,
27  const QPointF &posOther) const
28 {
29  double s = 0;
30  if (posOther.x() != posUnprojected.x()) {
31  s = (xBoundary - posUnprojected.x()) / (posOther.x() - posUnprojected.x());
32  }
33  ENGAUGE_ASSERT ((-1.0 * EPSILON < s) && (s < 1.0 + EPSILON));
34 
35  return QPointF ((1.0 - s) * posUnprojected.x() + s * posOther.x(),
36  (1.0 - s) * posUnprojected.y() + s * posOther.y());
37 }
38 
39 QPointF GridRemoval::clipY (const QPointF &posUnprojected,
40  double yBoundary,
41  const QPointF &posOther) const
42 {
43  double s = 0;
44  if (posOther.y() != posUnprojected.y()) {
45  s = (yBoundary - posUnprojected.y()) / (posOther.y() - posUnprojected.y());
46  }
47  ENGAUGE_ASSERT ((-1.0 * EPSILON < s) && (s < 1.0 + EPSILON));
48 
49  return QPointF ((1.0 - s) * posUnprojected.x() + s * posOther.x(),
50  (1.0 - s) * posUnprojected.y() + s * posOther.y());
51 }
52 
53 QPixmap GridRemoval::remove (const Transformation &transformation,
54  const DocumentModelGridRemoval &modelGridRemoval,
55  const QImage &imageBefore)
56 {
57  LOG4CPP_INFO_S ((*mainCat)) << "GridRemoval::remove"
58  << " transformationIsDefined=" << (transformation.transformIsDefined() ? "true" : "false")
59  << " removeDefinedGridLines=" << (modelGridRemoval.removeDefinedGridLines() ? "true" : "false");
60 
61  QImage image = imageBefore;
62 
63  // Collect GridHealers instances, one per grid line
64  GridHealers gridHealers;
65 
66  // Make sure grid line removal is wanted, and possible. Otherwise all processing is skipped
67  if (modelGridRemoval.removeDefinedGridLines() &&
68  transformation.transformIsDefined()) {
69 
70  double yGraphMin = modelGridRemoval.startY();
71  double yGraphMax = modelGridRemoval.stopY();
72  for (int i = 0; i < modelGridRemoval.countX(); i++) {
73  double xGraph = modelGridRemoval.startX() + i * modelGridRemoval.stepX();
74 
75  // Convert line between graph coordinates (xGraph,yGraphMin) and (xGraph,yGraphMax) to screen coordinates
76  QPointF posScreenMin, posScreenMax;
77  transformation.transformRawGraphToScreen (QPointF (xGraph,
78  yGraphMin),
79  posScreenMin);
80  transformation.transformRawGraphToScreen (QPointF (xGraph,
81  yGraphMax),
82  posScreenMax);
83 
84  removeLine (posScreenMin,
85  posScreenMax,
86  image,
87  modelGridRemoval,
88  gridHealers);
89  }
90 
91  double xGraphMin = modelGridRemoval.startX();
92  double xGraphMax = modelGridRemoval.stopX();
93  for (int j = 0; j < modelGridRemoval.countY(); j++) {
94  double yGraph = modelGridRemoval.startY() + j * modelGridRemoval.stepY();
95 
96  // Convert line between graph coordinates (xGraphMin,yGraph) and (xGraphMax,yGraph) to screen coordinates
97  QPointF posScreenMin, posScreenMax;
98  transformation.transformRawGraphToScreen (QPointF (xGraphMin,
99  yGraph),
100  posScreenMin);
101  transformation.transformRawGraphToScreen (QPointF (xGraphMax,
102  yGraph),
103  posScreenMax);
104 
105  removeLine (posScreenMin,
106  posScreenMax,
107  image,
108  modelGridRemoval,
109  gridHealers);
110  }
111 
112  // Heal the broken lines now that all grid lines have been removed and the image has stabilized
113  GridHealers::iterator itr;
114  for (itr = gridHealers.begin(); itr != gridHealers.end(); itr++) {
115  GridHealerAbstractBase *gridHealer = *itr;
116  gridHealer->healed (image);
117  delete gridHealer;
118  }
119  }
120 
121  return QPixmap::fromImage (image);
122 }
123 
124 void GridRemoval::removeLine (const QPointF &posMin,
125  const QPointF &posMax,
126  QImage &image,
127  const DocumentModelGridRemoval &modelGridRemoval,
128  GridHealers &gridHealers)
129 {
130  const int HALF_WIDTH = 1;
131 
132  double w = image.width() - 1; // Inclusive width = exclusive width - 1
133  double h = image.height() - 1; // Inclusive height = exclusive height - 1
134 
135  QPointF pos1 = posMin;
136  QPointF pos2 = posMax;
137 
138  // Throw away all lines that are entirely above or below or left or right to the screen, since
139  // they cannot intersect the screen
140  bool onLeft = (pos1.x() < 0 && pos2.x () < 0);
141  bool onTop = (pos1.y() < 0 && pos2.y () < 0);
142  bool onRight = (pos1.x() > w && pos2.x () > w);
143  bool onBottom = (pos1.y() > h && pos2.y () > h);
144  if (!onLeft && !onTop && !onRight && !onBottom) {
145 
146  // Clip to within the four sides
147  if (pos1.x() < 0) { pos1 = clipX (pos1, 0, pos2); }
148  if (pos2.x() < 0) { pos2 = clipX (pos2, 0, pos1); }
149  if (pos1.y() < 0) { pos1 = clipY (pos1, 0, pos2); }
150  if (pos2.y() < 0) { pos2 = clipY (pos2, 0, pos1); }
151  if (pos1.x() > w) { pos1 = clipX (pos1, w, pos2); }
152  if (pos2.x() > w) { pos2 = clipX (pos2, w, pos1); }
153  if (pos1.y() > h) { pos1 = clipY (pos1, h, pos2); }
154  if (pos2.y() > h) { pos2 = clipY (pos2, h, pos1); }
155 
156  // Is line more horizontal or vertical?
157  double deltaX = qAbs (pos1.x() - pos2.x());
158  double deltaY = qAbs (pos1.y() - pos2.y());
159  if (deltaX > deltaY) {
160 
161  // More horizontal
162  GridHealerAbstractBase *gridHealer = new GridHealerHorizontal (m_gridLog,
163  modelGridRemoval);
164  gridHealers.push_back (gridHealer);
165 
166  int xMin = qMin (qFloor (pos1.x()), qFloor (pos2.x()));
167  int xMax = qMax (qFloor (pos1.x()), qFloor (pos2.x()));
168  int yAtXMin = (pos1.x() < pos2.x() ? qFloor (pos1.y()) : qFloor (pos2.y()));
169  int yAtXMax = (pos1.x() < pos2.x() ? qFloor (pos2.y()) : qFloor (pos1.y()));
170  for (int x = xMin; x <= xMax; x++) {
171  double s = double (x - xMin) / double (xMax - xMin);
172  int yLine = qFloor (0.5 + (1.0 - s) * yAtXMin + s * yAtXMax);
173  for (int yOffset = -HALF_WIDTH; yOffset <= HALF_WIDTH; yOffset++) {
174  int y = yLine + yOffset;
175  image.setPixel (x, y, QColor(Qt::white).rgb());
176  }
177  gridHealer->addMutualPair (x, yLine - HALF_WIDTH - 1, x, yLine + HALF_WIDTH + 1);
178  }
179 
180  } else {
181 
182  // More vertical
183  GridHealerAbstractBase *gridHealer = new GridHealerVertical (m_gridLog,
184  modelGridRemoval);
185  gridHealers.push_back (gridHealer);
186 
187  int yMin = qMin (qFloor (pos1.y()), qFloor (pos2.y()));
188  int yMax = qMax (qFloor (pos1.y()), qFloor (pos2.y()));
189  int xAtYMin = (pos1.y() < pos2.y() ? qFloor (pos1.x()) : qFloor (pos2.x()));
190  int xAtYMax = (pos1.y() < pos2.y() ? qFloor (pos2.x()) : qFloor (pos1.x()));
191  for (int y = yMin; y <= yMax; y++) {
192  double s = double (y - yMin) / double (yMax - yMin);
193  int xLine = qFloor (0.5 + (1.0 - s) * xAtYMin + s * xAtYMax);
194  for (int xOffset = -HALF_WIDTH; xOffset <= HALF_WIDTH; xOffset++) {
195  int x = xLine + xOffset;
196  image.setPixel (x, y, QColor(Qt::white).rgb());
197  }
198  gridHealer->addMutualPair (xLine - HALF_WIDTH - 1, y, xLine + HALF_WIDTH + 1, y);
199  }
200 
201  }
202  }
203 }
204 
Pixels.h
DocumentModelGridRemoval::startX
double startX() const
Get method for x start.
Definition: DocumentModelGridRemoval.cpp:324
DocumentModelGridRemoval.h
GridHealerVertical
Subclass of GridHealerAbstractBase for vertical lines.
Definition: GridHealerVertical.h:19
DocumentModelGridRemoval::stepX
double stepX() const
Get method for x step.
Definition: DocumentModelGridRemoval.cpp:334
GridRemoval.h
Transformation::transformIsDefined
bool transformIsDefined() const
Transform is defined when at least three axis points have been digitized.
Definition: Transformation.cpp:335
Transformation::transformRawGraphToScreen
void transformRawGraphToScreen(const QPointF &pointRaw, QPointF &pointScreen) const
Transform from raw graph coordinates to linear cartesian graph coordinates, then to screen coordinate...
Definition: Transformation.cpp:434
GridHealerHorizontal
Subclass of GridHealerAbstractBase for horizontal lines.
Definition: GridHealerHorizontal.h:19
Transformation
Affine transformation between screen and graph coordinates, based on digitized axis points.
Definition: Transformation.h:30
EngaugeAssert.h
GridHealerAbstractBase::addMutualPair
void addMutualPair(int x0, int y0, int x1, int y1)
Add two points on either side of a gap. Later, after removal, the black points will be processed.
Definition: GridHealerAbstractBase.cpp:32
GridRemoval::remove
QPixmap remove(const Transformation &transformation, const DocumentModelGridRemoval &modelGridRemoval, const QImage &imageBefore)
Process QImage into QPixmap, removing the grid lines.
Definition: GridRemoval.cpp:53
Transformation.h
DocumentModelGridRemoval::stopX
double stopX() const
Get method for x stop.
Definition: DocumentModelGridRemoval.cpp:344
Logger.h
GridHealers
QList< GridHealerAbstractBase * > GridHealers
Storage of GridHealer instances.
Definition: GridRemoval.h:17
DocumentModelGridRemoval::stepY
double stepY() const
Get method for y step.
Definition: DocumentModelGridRemoval.cpp:339
LOG4CPP_INFO_S
#define LOG4CPP_INFO_S(logger)
Definition: convenience.h:18
GridRemoval::GridRemoval
GridRemoval(bool isGnuplot)
Single constructor.
Definition: GridRemoval.cpp:20
EPSILON
const double EPSILON
Definition: GridRemoval.cpp:18
GridHealerHorizontal.h
DocumentModelGridRemoval::countY
int countY() const
Get method for y count.
Definition: DocumentModelGridRemoval.cpp:123
mainCat
log4cpp::Category * mainCat
Definition: Logger.cpp:14
GridHealerAbstractBase::healed
void healed(QImage &image)
Return healed image after grid removal.
Definition: GridHealerAbstractBase.cpp:86
GridHealerVertical.h
DocumentModelGridRemoval::startY
double startY() const
Get method for y start.
Definition: DocumentModelGridRemoval.cpp:329
DocumentModelGridRemoval::removeDefinedGridLines
bool removeDefinedGridLines() const
Get method for removing defined grid lines.
Definition: DocumentModelGridRemoval.cpp:216
GridHealerAbstractBase
Class that 'heals' the curves after one grid line has been removed.
Definition: GridHealerAbstractBase.h:29
DocumentModelGridRemoval
Model for DlgSettingsGridRemoval and CmdSettingsGridRemoval. The settings are unstable until the user...
Definition: DocumentModelGridRemoval.h:16
DocumentModelGridRemoval::stopY
double stopY() const
Get method for y stop.
Definition: DocumentModelGridRemoval.cpp:349
ENGAUGE_ASSERT
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
Definition: EngaugeAssert.h:19
DocumentModelGridRemoval::countX
int countX() const
Get method for x count.
Definition: DocumentModelGridRemoval.cpp:118