Engauge Digitizer 2
Loading...
Searching...
No Matches
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"
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
31 QRgb rgb2) const
32{
33 const long MASK = 0xf0f0f0f0;
34 return (rgb1 & MASK) == (rgb2 & MASK);
35}
36
37void ColorFilter::createStrategies ()
38{
40 m_strategies [COLOR_FILTER_MODE_HUE ] = new ColorFilterStrategyHue ();
44}
45
46void 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
78QRgb 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
103void 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
126bool 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}
ColorFilterMode
@ COLOR_FILTER_MODE_FOREGROUND
@ COLOR_FILTER_MODE_VALUE
@ COLOR_FILTER_MODE_INTENSITY
@ COLOR_FILTER_MODE_SATURATION
@ COLOR_FILTER_MODE_HUE
#define ENGAUGE_ASSERT(cond)
Drop in replacement for Q_ASSERT if defined(QT_NO_DEBUG) && !defined(QT_FORCE_ASSERTS) define ENGAUGE...
Base class for strategy pattern whose subclasses process the different color filter settings modes (o...
virtual double pixelToZeroToOne(const QColor &pixel, QRgb rgbBackground) const =0
Return a normalized value of 0 to 1 given input pixel.
virtual int zeroToOneToValue(double s) const =0
Return the low value normalized to 0 to 1.
Leaf class for foreground strategy for ColorFilter.
Leaf class for hue strategy for ColorFilter.
Leaf class for intensity strategy for ColorFilter.
Leaf class for saturation strategy for ColorFilter.
Leaf class for value strategy for ColorFilter.
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...
bool pixelFilteredIsOn(const QImage &image, int x, int y) const
Return true if specified filtered pixel is on.
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.
~ColorFilter()
Destructor deallocates memory.
int zeroToOneToValue(ColorFilterMode colorFilterMode, double s) const
Inverse of pixelToZeroToOneOrMinusOne.
bool pixelUnfilteredIsOn(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground, double low0To1, double high0To1) const
Return true if specified unfiltered pixel is on.
double pixelToZeroToOneOrMinusOne(ColorFilterMode colorFilterMode, const QColor &pixel, QRgb rgbBackground) const
Return pixel converted according to the current filter parameter, normalized to zero to one.
ColorFilter()
Single constructor.
bool colorCompare(QRgb rgb1, QRgb rgb2) const
See if the two color values are close enough to be considered to be the same.
QRgb pixelRGB(const QImage &image, int x, int y)
Get pixel method for any bit depth.
Definition mmsubs.cpp:169
Helper class so ColorFilter class can compute the background color.
QColor color
Unique color entry.
unsigned int count
Number of times this color has appeared.