You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
124 lines
4.1 KiB
124 lines
4.1 KiB
/* |
|
* Copyright 2016 Nu-book Inc. |
|
* Copyright 2016 ZXing authors |
|
* Copyright 2020 Axel Waggershauser |
|
*/ |
|
// SPDX-License-Identifier: Apache-2.0 |
|
|
|
#include "GridSampler.h" |
|
|
|
#ifdef PRINT_DEBUG |
|
#include "LogMatrix.h" |
|
#include "BitMatrixIO.h" |
|
#endif |
|
|
|
namespace ZXing { |
|
|
|
#ifdef PRINT_DEBUG |
|
LogMatrix log; |
|
#endif |
|
|
|
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix) |
|
{ |
|
return SampleGrid(image, width, height, {ROI{0, width, 0, height, mod2Pix}}); |
|
} |
|
|
|
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const ROIs& rois) |
|
{ |
|
#ifdef PRINT_DEBUG |
|
LogMatrix log; |
|
static int i = 0; |
|
LogMatrixWriter lmw(log, image, 5, "grid" + std::to_string(i++) + ".pnm"); |
|
#endif |
|
if (width <= 0 || height <= 0) |
|
return {}; |
|
|
|
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) { |
|
// Precheck the corners of every roi to bail out early if the grid is "obviously" not completely inside the image |
|
auto isInside = [&mod2Pix = mod2Pix, &image](int x, int y) { return image.isIn(mod2Pix(centered(PointI(x, y)))); }; |
|
if (!mod2Pix.isValid() || !isInside(x0, y0) || !isInside(x1 - 1, y0) || !isInside(x1 - 1, y1 - 1) || !isInside(x0, y1 - 1)) |
|
return {}; |
|
} |
|
|
|
BitMatrix res(width, height); |
|
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) { |
|
for (int y = y0; y < y1; ++y) |
|
for (int x = x0; x < x1; ++x) { |
|
auto p = mod2Pix(centered(PointI{x, y})); |
|
// Due to a "numerical instability" in the PerspectiveTransform generation/application it has been observed |
|
// that even though all boundary grid points get projected inside the image, it can still happen that an |
|
// inner grid points is not. See #563. A true perspective transformation cannot have this property. |
|
// The following check takes 100% care of the issue and turned out to be less of a performance impact than feared. |
|
// TODO: Check some mathematical/numercial property of mod2Pix to determine if it is a perspective transformation. |
|
if (!image.isIn(p)) |
|
return {}; |
|
|
|
#ifdef PRINT_DEBUG |
|
log(p, 3); |
|
#endif |
|
#if 0 |
|
int sum = 0; |
|
for (int dy = -1; dy <= 1; ++dy) |
|
for (int dx = -1; dx <= 1; ++dx) |
|
sum += image.get(p + PointF(dx, dy)); |
|
if (sum >= 5) |
|
#else |
|
if (image.get(p)) |
|
#endif |
|
res.set(x, y); |
|
} |
|
} |
|
|
|
#ifdef PRINT_DEBUG |
|
printf("width: %d, height: %d\n", width, height); |
|
// printf("%s", ToString(res).c_str()); |
|
#endif |
|
|
|
auto projectCorner = [&](PointI p) { |
|
for (auto&& [x0, x1, y0, y1, mod2Pix] : rois) |
|
if (x0 <= p.x && p.x <= x1 && y0 <= p.y && p.y <= y1) |
|
return PointI(mod2Pix(PointF(p)) + PointF(0.5, 0.5)); |
|
|
|
return PointI(); |
|
}; |
|
|
|
return {std::move(res), |
|
{projectCorner({0, 0}), projectCorner({width, 0}), projectCorner({width, height}), projectCorner({0, height})}}; |
|
} |
|
|
|
DetectorResult SampleGrid(const BitMatrix& image, int width, int height, const PerspectiveTransform& mod2Pix, |
|
Matrix<std::optional<PointF>>&& apP, const std::vector<int>& apMX, const std::vector<int>& apMY) |
|
{ |
|
const int W = Size(apMX) - 1, H = Size(apMY) - 1; |
|
|
|
// go over the set of alignment patters and fill any remaining gaps by a projection based on the fallback |
|
// mod2Pix projection. This works if the symbol is flat, which is a reasonable fall-back assumption. |
|
for (int y = 0; y <= H; ++y) |
|
for (int x = 0; x <= W; ++x) { |
|
if (apP(x, y)) { |
|
#ifdef PRINT_DEBUG |
|
log(*apP(x, y), 2); |
|
#endif |
|
} else { |
|
#ifdef PRINT_DEBUG |
|
printf("locate failed at %dx%d\n", x, y); |
|
#endif |
|
// project the alignment pattern at module coordinates x/y to pixel coordinate based on current mod2Pix |
|
apP.set(x, y, mod2Pix(centered(PointI(apMX[x], apMY[y])))); |
|
} |
|
} |
|
|
|
// assemble a list of region-of-interests based on the found alignment pattern pixel positions |
|
ROIs rois; |
|
for (int y = 0; y < H; ++y) |
|
for (int x = 0; x < W; ++x) { |
|
int x0 = apMX[x], x1 = apMX[x + 1], y0 = apMY[y], y1 = apMY[y + 1]; |
|
rois.push_back({x == 0 ? 0 : x0, (x == W - 1) ? width : x1, (y == 0) ? 0 : y0, (y == H - 1) ? height : y1, |
|
PerspectiveTransform{Rectangle(x0, x1, y0, y1, 0.5), |
|
{*apP(x, y), *apP(x + 1, y), *apP(x + 1, y + 1), *apP(x, y + 1)}}}); |
|
} |
|
|
|
return SampleGrid(image, width, height, rois); |
|
} |
|
|
|
} // ZXing
|
|
|