Engauge Digitizer  2
ColorFilter.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 
7 #include "ColorConstants.h"
8 #include "ColorFilter.h"
10 #include "ColorFilterStrategyHue.h"
14 #include "EngaugeAssert.h"
15 #include "mmsubs.h"
16 #include <QDebug>
17 #include <qmath.h>
18 #include <QImage>
19 
21 {
22  createStrategies ();
23 }
24 
26 {
27  qDeleteAll (m_strategies);
28 }
29 
30 bool ColorFilter::colorCompare (QRgb rgb1,
31  QRgb rgb2) const
32 {
33  const long MASK = 0xf0f0f0f0;
34  return (rgb1 & MASK) == (rgb2 & MASK);
35 }
36 
37 void ColorFilter::createStrategies ()
38 {
40  m_strategies [COLOR_FILTER_MODE_HUE ] = new ColorFilterStrategyHue ();
43  m_strategies [COLOR_FILTER_MODE_VALUE ] = new ColorFilterStrategyValue ();
44 }
45 
46 void ColorFilter::filterImage (const QImage &imageOriginal,
47  QImage &imageFiltered,
48  ColorFilterMode colorFilterMode,
49  double low,
50  double high,
51  QRgb rgbBackground)
52 {
53  ENGAUGE_ASSERT (imageOriginal.width () == imageFiltered.width());
54  ENGAUGE_ASSERT (imageOriginal.height() == imageFiltered.height());
55  ENGAUGE_ASSERT (imageFiltered.format () == QImage::Format_RGB32);
56 
57  for (int x = 0; x < imageOriginal.width(); x++) {
58  for (int y = 0; y < imageOriginal.height (); y++) {
59 
60  QColor pixel = imageOriginal.pixel (x, y);
61  bool isOn = false;
62  if (pixel.rgb() != rgbBackground) {
63 
64  isOn = pixelUnfilteredIsOn (colorFilterMode,
65  pixel,
66  rgbBackground,
67  low,
68  high);
69  }
70 
71  imageFiltered.setPixel (x, y, (isOn ?
72  QColor (Qt::black).rgb () :
73  QColor (Qt::white).rgb ()));
74  }
75  }
76 }
77 
78 QRgb ColorFilter::marginColor(const QImage *image) const
79 {
80  // Add unique colors to colors list
81  ColorList colorCounts;
82  for (int x = 0; x < image->width (); x++) {
83  mergePixelIntoColorCounts (image->pixel (x, 0), colorCounts);
84  mergePixelIntoColorCounts (image->pixel (x, image->height () - 1), colorCounts);
85  }
86  for (int y = 0; y < image->height (); y++) {
87  mergePixelIntoColorCounts (image->pixel (0, y), colorCounts);
88  mergePixelIntoColorCounts (image->pixel (image->width () - 1, y), colorCounts);
89  }
90 
91  // Margin color is the most frequent color
92  ColorFilterEntry entryMax;
93  entryMax.count = 0;
94  for (ColorList::const_iterator itr = colorCounts.begin (); itr != colorCounts.end (); itr++) {
95  if ((*itr).count > entryMax.count) {
96  entryMax = *itr;
97  }
98  }
99 
100  return entryMax.color.rgb();
101 }
102 
103 void ColorFilter::mergePixelIntoColorCounts (QRgb pixel,
104  ColorList &colorCounts) const
105 {
106  ColorFilterEntry entry;
107  entry.color = pixel;
108  entry.count = 0;
109 
110  // Look for previous entry
111  bool found = false;
112  for (ColorList::iterator itr = colorCounts.begin (); itr != colorCounts.end (); itr++) {
113  if (colorCompare (entry.color.rgb(),
114  (*itr).color.rgb())) {
115  found = true;
116  ++(*itr).count;
117  break;
118  }
119  }
120 
121  if (!found) {
122  colorCounts.append (entry);
123  }
124 }
125 
126 bool ColorFilter::pixelFilteredIsOn (const QImage &image,
127  int x,
128  int y) const
129 {
130  bool rtn = false;
131 
132  if ((0 <= x) &&
133  (0 <= y) &&
134  (x < image.width()) &&
135  (y < image.height())) {
136 
137  // Pixel is on if it is closer to black than white in gray scale. This test must be performed
138  // on little endian and big endian systems, with or without alpha bits (which are typically high bits);
139  const int BLACK_WHITE_THRESHOLD = 255 / 2; // Put threshold in middle of range
140  int gray = qGray (pixelRGB (image, x, y));
141  rtn = (gray < BLACK_WHITE_THRESHOLD);
142 
143  }
144 
145  return rtn;
146 }
147 
149  const QColor &pixel,
150  QRgb rgbBackground,
151  double low0To1,
152  double high0To1) const
153 {
154  bool rtn = false;
155 
156  double s = pixelToZeroToOneOrMinusOne (colorFilterMode,
157  pixel,
158  rgbBackground);
159  if (s >= 0.0) {
160  if (low0To1 <= high0To1) {
161 
162  // Single valid range
163  rtn = (low0To1 <= s) && (s <= high0To1);
164 
165  } else {
166 
167  // Two ranges
168  rtn = (s <= high0To1) || (low0To1 <= s);
169 
170  }
171  }
172 
173  return rtn;
174 }
175 
177  const QColor &pixel,
178  QRgb rgbBackground) const
179 {
180  if (m_strategies.contains (colorFilterMode)) {
181 
182  // Ignore false positive cmake compiler warning about -Wreturn-stack-address in next line (bug #26396)
183  const ColorFilterStrategyAbstractBase *strategy = m_strategies [colorFilterMode];
184  return strategy->pixelToZeroToOne (pixel,
185  rgbBackground);
186 
187  } else {
188 
189  ENGAUGE_ASSERT (false);
190  return 0.0;
191 
192  }
193 }
194 
196  double s) const
197 {
198  if (m_strategies.contains (colorFilterMode)) {
199 
200  const ColorFilterStrategyAbstractBase *strategy = m_strategies [colorFilterMode];
201  return strategy->zeroToOneToValue (s);
202 
203  } else {
204 
205  ENGAUGE_ASSERT (false);
206  return 0;
207 
208  }
209 }
ColorFilter::pixelUnfilteredIsOn
bool pixelUnfilteredIsOn(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground, double low0To1, double high0To1) const
Return true if specified unfiltered pixel is on.
Definition: ColorFilter.cpp:148
ColorFilter::pixelToZeroToOneOrMinusOne
double pixelToZeroToOneOrMinusOne(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground) const
Return pixel converted according to the current filter parameter, normalized to zero to one.
Definition: ColorFilter.cpp:176
ColorFilter.h
ColorFilter::~ColorFilter
~ColorFilter()
Destructor deallocates memory.
Definition: ColorFilter.cpp:25
EngaugeAssert.h
ColorFilterStrategyIntensity.h
ColorFilterStrategyValue
Leaf class for value strategy for ColorFilter.
Definition: ColorFilterStrategyValue.h:12
ColorConstants.h
ColorFilterStrategySaturation.h
pixelRGB
QRgb pixelRGB(const QImage &image, int x, int y)
Get pixel method for any bit depth.
Definition: mmsubs.cpp:169
ColorFilter::marginColor
QRgb marginColor(const QImage *image) const
Identify the margin color of the image, which is defined as the most common color in the four margins...
Definition: ColorFilter.cpp:78
ColorFilterStrategyForeground.h
ColorFilter::pixelFilteredIsOn
bool pixelFilteredIsOn(const QImage &image, int x, int y) const
Return true if specified filtered pixel is on.
Definition: ColorFilter.cpp:126
ColorFilterStrategyHue
Leaf class for hue strategy for ColorFilter.
Definition: ColorFilterStrategyHue.h:12
ColorFilterStrategyAbstractBase::zeroToOneToValue
virtual int zeroToOneToValue(double s) const =0
Return the low value normalized to 0 to 1.
ColorFilterEntry::color
QColor color
Unique color entry.
Definition: ColorFilterEntry.h:17
ColorFilterStrategyValue.h
COLOR_FILTER_MODE_SATURATION
Definition: ColorFilterMode.h:18
ColorFilterEntry::count
unsigned int count
Number of times this color has appeared.
Definition: ColorFilterEntry.h:20
ColorFilter::zeroToOneToValue
int zeroToOneToValue(ColorFilterMode colorFilterMode, double s) const
Inverse of pixelToZeroToOneOrMinusOne.
Definition: ColorFilter.cpp:195
ColorFilterStrategyAbstractBase::pixelToZeroToOne
virtual double pixelToZeroToOne(const QColor &pixel, QRgb rgbBackground) const =0
Return a normalized value of 0 to 1 given input pixel.
ColorFilterStrategyIntensity
Leaf class for intensity strategy for ColorFilter.
Definition: ColorFilterStrategyIntensity.h:12
COLOR_FILTER_MODE_HUE
Definition: ColorFilterMode.h:16
ColorFilterStrategySaturation
Leaf class for saturation strategy for ColorFilter.
Definition: ColorFilterStrategySaturation.h:12
ColorFilterStrategyHue.h
ColorFilter::filterImage
void filterImage(const QImage &imageOriginal, QImage &imageFiltered, ColorFilterMode colorFilterMode, double low, double high, QRgb rgbBackground)
Filter the original image according to the specified filtering parameters.
Definition: ColorFilter.cpp:46
COLOR_FILTER_MODE_INTENSITY
Definition: ColorFilterMode.h:17
ColorFilterStrategyForeground
Leaf class for foreground strategy for ColorFilter.
Definition: ColorFilterStrategyForeground.h:12
COLOR_FILTER_MODE_VALUE
Definition: ColorFilterMode.h:19
mmsubs.h
ColorFilterMode
ColorFilterMode
Definition: ColorFilterMode.h:11
ColorFilter::ColorFilter
ColorFilter()
Single constructor.
Definition: ColorFilter.cpp:20
ColorFilterStrategyAbstractBase
Base class for strategy pattern whose subclasses process the different color filter settings modes (o...
Definition: ColorFilterStrategyAbstractBase.h:18
COLOR_FILTER_MODE_FOREGROUND
Definition: ColorFilterMode.h:15
ColorFilterEntry
Helper class so ColorFilter class can compute the background color.
Definition: ColorFilterEntry.h:12
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
ColorFilter::colorCompare
bool colorCompare(QRgb rgb1, QRgb rgb2) const
See if the two color values are close enough to be considered to be the same.
Definition: ColorFilter.cpp:30