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 "Logger.h"
16#include "mmsubs.h"
17#include <QDebug>
18#include <qmath.h>
19#include <QImage>
20
22{
23 createStrategies ();
24}
25
27{
28 qDeleteAll (m_strategies);
29}
30
32 QRgb rgb2) const
33{
34 const long MASK = 0xf0f0f0f0;
35 return (rgb1 & MASK) == (rgb2 & MASK);
36}
37
38void ColorFilter::createStrategies ()
39{
41 m_strategies [COLOR_FILTER_MODE_HUE ] = new ColorFilterStrategyHue ();
45}
46
47void ColorFilter::filterImage (const QImage &imageOriginal,
48 QImage &imageFiltered,
49 ColorFilterMode colorFilterMode,
50 double low,
51 double high,
52 QRgb rgbBackground)
53{
54 ENGAUGE_ASSERT (imageOriginal.width () == imageFiltered.width());
55 ENGAUGE_ASSERT (imageOriginal.height() == imageFiltered.height());
56 ENGAUGE_ASSERT (imageFiltered.format () == QImage::Format_RGB32);
57
58 for (int x = 0; x < imageOriginal.width(); x++) {
59 for (int y = 0; y < imageOriginal.height (); y++) {
60
61 QColor pixel = imageOriginal.pixel (x, y);
62 bool isOn = false;
63 if (pixel.rgb() != rgbBackground) {
64
65 isOn = pixelUnfilteredIsOn (colorFilterMode,
66 pixel,
67 rgbBackground,
68 low,
69 high);
70 }
71
72 imageFiltered.setPixel (x, y, (isOn ?
73 QColor (Qt::black).rgb () :
74 QColor (Qt::white).rgb ()));
75 }
76 }
77}
78
79QRgb ColorFilter::marginColor(const QImage *image) const
80{
81 // Add unique colors to colors list
82 ColorList colorCounts;
83 for (int x = 0; x < image->width (); x++) {
84 mergePixelIntoColorCounts (image->pixel (x, 0), colorCounts);
85 mergePixelIntoColorCounts (image->pixel (x, image->height () - 1), colorCounts);
86 }
87 for (int y = 0; y < image->height (); y++) {
88 mergePixelIntoColorCounts (image->pixel (0, y), colorCounts);
89 mergePixelIntoColorCounts (image->pixel (image->width () - 1, y), colorCounts);
90 }
91
92 // Margin color is the most frequent color
93 ColorFilterEntry entryMax;
94 entryMax.count = 0;
95 for (ColorList::const_iterator itr = colorCounts.begin (); itr != colorCounts.end (); itr++) {
96 if ((*itr).count > entryMax.count) {
97 entryMax = *itr;
98 }
99 }
100
101 return entryMax.color.rgb();
102}
103
104void ColorFilter::mergePixelIntoColorCounts (QRgb pixel,
105 ColorList &colorCounts) const
106{
107 ColorFilterEntry entry;
108 entry.color = pixel;
109 entry.count = 0;
110
111 // Look for previous entry
112 bool found = false;
113 for (ColorList::iterator itr = colorCounts.begin (); itr != colorCounts.end (); itr++) {
114 if (colorCompare (entry.color.rgb(),
115 (*itr).color.rgb())) {
116 found = true;
117 ++(*itr).count;
118 break;
119 }
120 }
121
122 if (!found) {
123 colorCounts.append (entry);
124 }
125}
126
127bool ColorFilter::pixelFilteredIsOn (const QImage &image,
128 int x,
129 int y) const
130{
131 bool rtn = false;
132
133 if ((0 <= x) &&
134 (0 <= y) &&
135 (x < image.width()) &&
136 (y < image.height())) {
137
138 // Pixel is on if it is closer to black than white in gray scale. This test must be performed
139 // on little endian and big endian systems, with or without alpha bits (which are typically high bits);
140 const int BLACK_WHITE_THRESHOLD = 255 / 2; // Put threshold in middle of range
141 int gray = qGray (pixelRGB (image, x, y));
142 rtn = (gray < BLACK_WHITE_THRESHOLD);
143
144 }
145
146 return rtn;
147}
148
150 const QColor &pixel,
151 QRgb rgbBackground,
152 double low0To1,
153 double high0To1) const
154{
155 bool rtn = false;
156
157 double s = pixelToZeroToOneOrMinusOne (colorFilterMode,
158 pixel,
159 rgbBackground);
160 if (s >= 0.0) {
161 if (low0To1 <= high0To1) {
162
163 // Single valid range
164 rtn = (low0To1 <= s) && (s <= high0To1);
165
166 } else {
167
168 // Two ranges
169 rtn = (s <= high0To1) || (low0To1 <= s);
170
171 }
172 }
173
174 return rtn;
175}
176
178 const QColor &pixel,
179 QRgb rgbBackground) const
180{
181 if (m_strategies.contains (colorFilterMode)) {
182
183 // Ignore false positive cmake compiler warning about -Wreturn-stack-address in next line (bug #26396)
184 const ColorFilterStrategyAbstractBase *strategy = m_strategies [colorFilterMode];
185 return strategy->pixelToZeroToOne (pixel,
186 rgbBackground);
187
188 } else {
189
190 LOG4CPP_ERROR_S ((*mainCat)) << "ColorFilter::pixelToZeroToOneOrMinusOne is missing color filter mode";
191 ENGAUGE_ASSERT (false);
192 return 0.0;
193
194 }
195}
196
198 double s) const
199{
200 if (m_strategies.contains (colorFilterMode)) {
201
202 const ColorFilterStrategyAbstractBase *strategy = m_strategies [colorFilterMode];
203 return strategy->zeroToOneToValue (s);
204
205 } else {
206
207 LOG4CPP_ERROR_S ((*mainCat)) << "ColorFilter::zeroToOneToValue is missing color filter mode";
208 ENGAUGE_ASSERT (false);
209 return 0;
210
211 }
212}
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.
log4cpp::Category * mainCat
Definition Logger.cpp:14
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.
#define LOG4CPP_ERROR_S(logger)
Definition convenience.h:12
QRgb pixelRGB(const QImage &image, int x, int y)
Get pixel method for any bit depth.
Definition mmsubs.cpp:209
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.