| 38 | } |
| 39 | |
| 40 | std::optional<PointF> CenterOfRing(const BitMatrix& image, PointI center, int range, int nth, bool requireCircle) |
| 41 | { |
| 42 | // range is the approximate width/height of the nth ring, if nth>1 then it would be plausible to limit the search radius |
| 43 | // to approximately range / 2 * sqrt(2) == range * 0.75 but it turned out to be too limiting with realworld/noisy data. |
| 44 | int radius = range; |
| 45 | bool inner = nth < 0; |
| 46 | nth = std::abs(nth); |
| 47 | log(center, 3); |
| 48 | BitMatrixCursorI cur(image, center, {0, 1}); |
| 49 | if (!cur.stepToEdge(nth, radius, inner)) |
| 50 | return {}; |
| 51 | cur.turnRight(); // move clock wise and keep edge on the right/left depending on backup |
| 52 | const auto edgeDir = inner ? Direction::LEFT : Direction::RIGHT; |
| 53 | |
| 54 | uint32_t neighbourMask = 0; |
| 55 | auto start = cur.p; |
| 56 | PointF sum = {}; |
| 57 | int n = 0; |
| 58 | do { |
| 59 | log(cur.p, 4); |
| 60 | sum += centered(cur.p); |
| 61 | ++n; |
| 62 | |
| 63 | // find out if we come full circle around the center. 8 bits have to be set in the end. |
| 64 | neighbourMask |= (1 << (4 + dot(bresenhamDirection(cur.p - center), PointI(1, 3)))); |
| 65 | |
| 66 | if (!cur.stepAlongEdge(edgeDir)) |
| 67 | return {}; |
| 68 | |
| 69 | // use L-inf norm, simply because it is a lot faster than L2-norm and sufficiently accurate |
| 70 | if (maxAbsComponent(cur.p - center) > radius || center == cur.p || n > 4 * 2 * range) |
| 71 | return {}; |
| 72 | } while (cur.p != start); |
| 73 | |
| 74 | if (requireCircle && neighbourMask != 0b111101111) |
| 75 | return {}; |
| 76 | |
| 77 | return sum / n; |
| 78 | } |
| 79 | |
| 80 | std::optional<PointF> CenterOfRings(const BitMatrix& image, PointF center, int range, int numOfRings) |
| 81 | { |
no test coverage detected