Engauge Digitizer  2
 All Classes Functions Variables Typedefs Enumerations Friends Pages
ColorFilter.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 "ColorConstants.h"
8 #include "ColorFilter.h"
9 #include "ColorFilterStrategyForeground.h"
10 #include "ColorFilterStrategyHue.h"
11 #include "ColorFilterStrategyIntensity.h"
12 #include "ColorFilterStrategySaturation.h"
13 #include "ColorFilterStrategyValue.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 {
39  m_strategies [COLOR_FILTER_MODE_FOREGROUND] = new ColorFilterStrategyForeground ();
40  m_strategies [COLOR_FILTER_MODE_HUE ] = new ColorFilterStrategyHue ();
41  m_strategies [COLOR_FILTER_MODE_INTENSITY ] = new ColorFilterStrategyIntensity ();
42  m_strategies [COLOR_FILTER_MODE_SATURATION] = new ColorFilterStrategySaturation ();
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 
148 bool ColorFilter::pixelUnfilteredIsOn (ColorFilterMode colorFilterMode,
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 
176 double ColorFilter::pixelToZeroToOneOrMinusOne (ColorFilterMode colorFilterMode,
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 
195 int ColorFilter::zeroToOneToValue (ColorFilterMode colorFilterMode,
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 }
Leaf class for foreground strategy for ColorFilter.
virtual int zeroToOneToValue(double s) const =0
Return the low value normalized to 0 to 1.
double pixelToZeroToOneOrMinusOne(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground) const
Return pixel converted according to the current filter parameter, normalized to zero to one...
QColor color
Unique color entry.
Base class for strategy pattern whose subclasses process the different color filter settings modes (o...
int zeroToOneToValue(ColorFilterMode colorFilterMode, double s) const
Inverse of pixelToZeroToOneOrMinusOne.
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
ColorFilter()
Single constructor.
Definition: ColorFilter.cpp:20
unsigned int count
Number of times this color has appeared.
Leaf class for value strategy for ColorFilter.
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
Leaf class for intensity strategy for ColorFilter.
bool pixelFilteredIsOn(const QImage &image, int x, int y) const
Return true if specified filtered pixel is on.
Leaf class for hue strategy for ColorFilter.
virtual double pixelToZeroToOne(const QColor &pixel, QRgb rgbBackground) const =0
Return a normalized value of 0 to 1 given input pixel.
Helper class so ColorFilter class can compute the background color.
bool pixelUnfilteredIsOn(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground, double low0To1, double high0To1) const
Return true if specified unfiltered pixel is on.
Leaf class for saturation strategy for ColorFilter.
~ColorFilter()
Destructor deallocates memory.
Definition: ColorFilter.cpp:25
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