Engauge Digitizer 2
Loading...
Searching...
No Matches
Public Member Functions | Static Public Member Functions | List of all members
Pixels Class Reference

Utility class for pixel manipulation. More...

#include <Pixels.h>

Collaboration diagram for Pixels:
Collaboration graph

Public Member Functions

 Pixels ()
 Single constructor.
 
int countBlackPixelsAroundPoint (const QImage &image, int x, int y, int stopCountAt)
 Fill triangle between these three points.
 
void fillHole (QImage &image, int row, int col, int thresholdCount) const
 Fill white hole encompassing (row,col) if number of pixels in that hole is below the threshold.
 
void fillHoles (QImage &image, int thresholdCount)
 Fill in white holes, surrounded by black pixels, smaller than some threshold number of pixels.
 
void fillIsolatedWhitePixels (QImage &image)
 Fill in white pixels surrounded by more black pixels than white pixels.
 

Static Public Member Functions

static bool pixelIsBlack (const QImage &image, int x, int y)
 Return true if pixel is black in black and white image.
 

Detailed Description

Utility class for pixel manipulation.

Definition at line 31 of file Pixels.h.

Constructor & Destructor Documentation

◆ Pixels()

Pixels::Pixels ( )

Single constructor.

Definition at line 12 of file Pixels.cpp.

13{
14}

Member Function Documentation

◆ countBlackPixelsAroundPoint()

int Pixels::countBlackPixelsAroundPoint ( const QImage & image,
int x,
int y,
int stopCountAt )

Fill triangle between these three points.

Definition at line 16 of file Pixels.cpp.

20{
21 int count = 0;
23 HashLookup hashLookup; // Prevents reprocessing of already-processed pixels
24
25 // First point
26 if (pixelIsBlack (image, x, y)) {
27 queuedPoints.push_back (QPoint (x, y));
28 }
29
30 while (queuedPoints.count () > 0) {
31
32 // Pop off queue
33 QPoint p = queuedPoints.front ();
34 queuedPoints.pop_front ();
35
36 QString hash = hashForCoordinates (p.x(),
37 p.y());
38
39 // Skip if out of bounds, processed already or not black
40 bool inBounds = (0 <= p.x() &&
41 0 <= p.y() &&
42 p.x() < image.width () &&
43 p.y() < image.height ());
44 if (inBounds &&
45 !hashLookup.contains (hash) &&
46 pixelIsBlack (image, p.x(), p.y())) {
47
48 // Black pixel. Add to count, and remember to not reprocess later
49 ++count;
50 if (count == stopCountAt) {
51 return count; // Reached limit. Stop immediately (probably for speed)
52 }
53 hashLookup [hash] = true;
54
55 // Queue neighbors for processing
56 for (int dx = -1; dx <= 1; dx++) {
57 for (int dy = -1; dy <= 1; dy++) {
58 if (dx != 0 || dy != 0) {
59 queuedPoints.push_back (QPoint (p.x() + dx,
60 p.y() + dy));
61 }
62 }
63 }
64 }
65 }
66
67 return count; // Did not reach limit
68}
const int INNER_RADIUS_MIN
QMap< QString, bool > HashLookup
Quick lookup table for pixel coordinate hashes processed so far.
Definition Pixels.h:19
QQueue< QPoint > QueuedPoints
Definition Pixels.h:21
static bool pixelIsBlack(const QImage &image, int x, int y)
Return true if pixel is black in black and white image.
Definition Pixels.cpp:286

◆ fillHole()

void Pixels::fillHole ( QImage & image,
int row,
int col,
int thresholdCount ) const

Fill white hole encompassing (row,col) if number of pixels in that hole is below the threshold.

Definition at line 70 of file Pixels.cpp.

74{
75 // Square of 1 pixel is surrounded by 3x3 box with indexes -1 to +2
76 // 2-4 pixels 4x4 -2 to +2
77 // 5-9 5x5 -2 to +3
78 // 10-16 6x6 -3 to +3
79 int rowStart = qFloor (row - (1 + qSqrt (thresholdCount - 1))); // Inclusive
80 int colStart = qFloor (col - (1 + qSqrt (thresholdCount - 1))); // Inclusive
81 int rowStop = qFloor (row + (1 + qSqrt (thresholdCount))); // Exclusive
82 int colStop = qFloor (col + (1 + qSqrt (thresholdCount))); // Exclusive
83
84 // First pass is for counting
85 int countWhite = 0;
86 for (int rowIter = rowStart; rowIter < rowStop; rowIter++) {
87 for (int colIter = colStart; colIter < colStop; colIter++) {
88 if (!pixelIsBlack (image,
89 colIter,
90 rowIter)) {
91 ++countWhite;
92 }
93 }
94 }
95
96 // Second pass fills in the hole
98 for (int rowIter = rowStart; rowIter < rowStop; rowIter++) {
99 for (int colIter = colStart; colIter < colStop; colIter++) {
100 image.setPixel (colIter,
101 rowIter,
102 Qt::black);
103 }
104 }
105 }
106}

◆ fillHoles()

void Pixels::fillHoles ( QImage & image,
int thresholdCount )

Fill in white holes, surrounded by black pixels, smaller than some threshold number of pixels.

Originally this was recursive but the high recursion levels (for big regions) overflowed the stack

Definition at line 108 of file Pixels.cpp.

110{
111 int height = image.height();
112 int width = image.width();
113
114 // 2d matrix, indexed as 1d vector, of pixel states
115 QVector<PixelFillState> states (image.width() * image.height());
117
118 // Search for unprocessed pixels
119 for (int col = 0; col < width; col++) {
120 for (int row = 0; row < height; row++) {
121 if (states [indexCollapse (row, col, width)] == PIXEL_FILL_STATE_UNPROCESSED) {
122
123 // Found an unprocessed pixel so process it
124 if (pixelIsBlack (image, col, row)) {
125
126 // Black pixel needs no processing
127 states [indexCollapse (row, col, width)] = PIXEL_FILL_STATE_PROCESSED;
128
129 } else {
130
131 // Get this pixel and all of its white neighbors
132 int pixelsInRegion = fillPass (image,
133 states,
134 row,
135 col,
138 NO_FILL);
139
140 FillIt fillIt = (pixelsInRegion < thresholdCount) ? YES_FILL : NO_FILL;
141
142 fillPass (image,
143 states,
144 row,
145 col,
148 fillIt);
149 }
150 }
151 }
152 }
153}
@ PIXEL_FILL_STATE_UNPROCESSED
Definition Pixels.h:25
@ PIXEL_FILL_STATE_IN_PROCESS
Definition Pixels.h:26
@ PIXEL_FILL_STATE_PROCESSED
Definition Pixels.h:27

◆ fillIsolatedWhitePixels()

void Pixels::fillIsolatedWhitePixels ( QImage & image)

Fill in white pixels surrounded by more black pixels than white pixels.

This is much faster than fillHoles and effectively as good

Definition at line 155 of file Pixels.cpp.

156{
157 const int BORDER = 1;
158 const int HALF_NUMBER_NEIGHBORS = 4; // 8 neighbors in each direction from (col,row)
159
160 int height = image.height();
161 int width = image.width();
162
163 // 2d matrix, indexed as 1d vector, of neighbor counts
164 QVector<bool> pixelsAreBlack (image.width() * image.height());
165
166 // Replace slow QImage addressing by faster QVector addressing
167 for (int col = 0; col < width; col++) {
168 for (int row = 0; row < height; row++) {
169 pixelsAreBlack [indexCollapse (row, col, width)] = pixelIsBlack (image, col, row);
170 }
171 }
172
173 // Search for white pixels. Black pixels will be ignored, and also pixels along the four
174 // borders are ignored so we do not need to worry about going out of bounds
175 for (int col = BORDER; col < width - BORDER; col++) {
176 for (int row = BORDER; row < height - BORDER; row++) {
177 int count = 0;
178 count += pixelsAreBlack [indexCollapse (row - 1, col - 1, width)] ? 1 : 0;
179 count += pixelsAreBlack [indexCollapse (row - 1, col , width)] ? 1 : 0;
180 count += pixelsAreBlack [indexCollapse (row - 1, col + 1, width)] ? 1 : 0;
181 count += pixelsAreBlack [indexCollapse (row , col - 1, width)] ? 1 : 0;
182 count += pixelsAreBlack [indexCollapse (row , col + 1, width)] ? 1 : 0;
183 count += pixelsAreBlack [indexCollapse (row + 1, col - 1, width)] ? 1 : 0;
184 count += pixelsAreBlack [indexCollapse (row + 1, col , width)] ? 1 : 0;
185 count += pixelsAreBlack [indexCollapse (row + 1, col + 1, width)] ? 1 : 0;
186 if (count > HALF_NUMBER_NEIGHBORS) {
187 image.setPixel (col,
188 row,
189 Qt::black);
190 }
191 }
192 }
193}

◆ pixelIsBlack()

bool Pixels::pixelIsBlack ( const QImage & image,
int x,
int y )
static

Return true if pixel is black in black and white image.

Definition at line 286 of file Pixels.cpp.

289{
290 QRgb rgb = image.pixel (x, y);
291 return qGray (rgb) < 128;
292}

The documentation for this class was generated from the following files: