27 queuedPoints.push_back (QPoint (x, y));
30 while (queuedPoints.count () > 0) {
33 QPoint p = queuedPoints.front ();
34 queuedPoints.pop_front ();
36 QString hash = hashForCoordinates (p.x(),
40 bool inBounds = (0 <= p.x() &&
42 p.x() < image.width () &&
43 p.y() < image.height ());
45 !hashLookup.contains (hash) &&
50 if (count == stopCountAt) {
53 hashLookup [hash] =
true;
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,
73 int thresholdCount)
const
79 int rowStart = qFloor (row - (1 + qSqrt (thresholdCount - 1)));
80 int colStart = qFloor (col - (1 + qSqrt (thresholdCount - 1)));
81 int rowStop = qFloor (row + (1 + qSqrt (thresholdCount)));
82 int colStop = qFloor (col + (1 + qSqrt (thresholdCount)));
86 for (
int rowIter = rowStart; rowIter < rowStop; rowIter++) {
87 for (
int colIter = colStart; colIter < colStop; colIter++) {
97 if (countWhite < thresholdCount) {
98 for (
int rowIter = rowStart; rowIter < rowStop; rowIter++) {
99 for (
int colIter = colStart; colIter < colStop; colIter++) {
100 image.setPixel (colIter,
111 int height = image.height();
112 int width = image.width();
115 QVector<PixelFillState> states (image.width() * image.height());
119 for (
int col = 0; col < width; col++) {
120 for (
int row = 0; row < height; row++) {
132 int pixelsInRegion = fillPass (image,
140 FillIt fillIt = (pixelsInRegion < thresholdCount) ? YES_FILL : NO_FILL;
157 const int BORDER = 1;
158 const int HALF_NUMBER_NEIGHBORS = 4;
160 int height = image.height();
161 int width = image.width();
164 QVector<bool> pixelsAreBlack (image.width() * image.height());
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);
175 for (
int col = BORDER; col < width - BORDER; col++) {
176 for (
int row = BORDER; row < height - BORDER; row++) {
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) {
195int Pixels::fillPass (QImage &image,
196 QVector<PixelFillState> &states,
203 int height = image.height();
204 int width = image.width ();
206 QList<QPoint> applicablePoints;
209 applicablePoints.append (QPoint (colIn, rowIn));
211 while (applicablePoints.count() > 0) {
213 QPoint p = applicablePoints.front();
214 applicablePoints.pop_front();
220 PixelFillState stateGot = states [indexCollapse (row, col, width)];
221 if (stateGot == stateFrom &&
230 if (fillit == YES_FILL) {
231 image.setPixel (col, row, Qt::black);
236 states [indexCollapse (row, col, width)] = stateTo;
239 for (
int dx = -1; dx <= 1; dx++) {
241 if (0 <= colD && colD < width) {
243 for (
int dy = -1; dy <= 1; dy++) {
245 if (0 <= rowD && rowD < height) {
247 if (dx != 0 || dy != 0) {
249 PixelFillState stateGot = states [indexCollapse (rowD, colD, width)];
250 if (stateGot == stateFrom &&
256 applicablePoints.append (QPoint (colD, rowD));
269QString Pixels::hashForCoordinates (
int x,
272 const int FIELD_WIDTH = 6;
274 return QString (
"%1/%2")
275 .arg (x, FIELD_WIDTH)
276 .arg (y, FIELD_WIDTH);
279int Pixels::indexCollapse (
int row,
283 return row * width + col;
290 QRgb rgb = image.pixel (x, y);
291 return qGray (rgb) < 128;
QMap< QString, bool > HashLookup
Quick lookup table for pixel coordinate hashes processed so far.
PixelFillState
Each pixel transitions from unprocessed, to in-process, to processed.
@ PIXEL_FILL_STATE_UNPROCESSED
@ PIXEL_FILL_STATE_IN_PROCESS
@ PIXEL_FILL_STATE_PROCESSED
QQueue< QPoint > QueuedPoints
void fillIsolatedWhitePixels(QImage &image)
Fill in white pixels surrounded by more black pixels than white pixels.
static bool pixelIsBlack(const QImage &image, int x, int y)
Return true if pixel is black in black and white image.
int countBlackPixelsAroundPoint(const QImage &image, int x, int y, int stopCountAt)
Fill triangle between these three points.
void fillHoles(QImage &image, int thresholdCount)
Fill in white holes, surrounded by black pixels, smaller than some threshold number of pixels.
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.
Pixels()
Single constructor.