b452. 傻傻地幫人數錢錢

contents

  1. 1. Problem
  2. 2. Sample Input
  3. 3. Sample Output
  4. 4. Solution

Problem

給一個彩色影像上面有數個相同大小的硬幣,硬幣之間不會重疊,但會有部分碰觸,請問影像中有多少個硬幣。

Sample Input

1
2
3
4
5
6
7
8
9
10
10 9
159 138 133 150 129 124 153 132 127 154 133 128 151 132 126 151 132 126 152 133 129 152 133 129 143 125 125 136 118 118
147 126 121 163 142 137 146 125 120 86 65 60 60 41 35 57 38 32 68 49 45 111 92 88 143 125 123 138 120 118
145 125 118 152 132 125 63 44 37 53 34 27 54 35 28 55 36 29 56 39 32 50 32 28 105 87 83 132 114 110
150 130 123 106 86 79 53 34 27 57 38 31 53 34 27 64 45 38 55 38 31 56 39 32 63 45 41 131 113 109
151 132 125 87 68 61 52 33 26 56 37 30 58 41 33 49 32 24 57 40 33 53 36 29 43 28 23 131 116 111
136 117 111 103 84 78 56 37 31 59 40 34 54 37 30 57 40 33 47 30 23 53 35 31 54 39 34 141 126 121
142 124 120 140 122 118 52 34 30 54 36 32 51 33 29 53 35 31 53 38 33 46 31 28 82 67 64 136 121 118
145 127 127 125 107 107 116 98 98 54 36 36 53 35 33 54 36 34 46 30 30 65 49 49 138 122 122 145 129 129
135 119 120 137 121 122 137 121 122 133 117 118 103 87 88 95 79 80 111 95 96 138 122 123 132 118 118 132 118 118

Sample Output

1
1

Solution

若圓形彼此之間不相交,可以用二值化 + 灌水法 (flood fill) + 分團大小檢測。現在圓形有相連可能,出題者給我附前測代碼啊,咱們兩個比一下速度 … 總之步驟是

  1. 根據亮度二值化
  2. 搭配索貝爾運算 (sobel) 選定閥值後找到邊緣
  3. 接著窮舉圓半徑,使用霍夫轉換將邊緣點推到同一個圓心
  4. 掃描一個 $7 \times 7$ 的矩形,內部點個數要出現 56 個,同時要滿足 49 格至少出現 36 格或者其中一格出現大於 4 次。

關於霍夫轉換,其中$x_c, \; y_c$ 表示推向圓心的座標,而 $r$ 表示窮舉的圓半徑,$gx$ 表示 x 方向的 sobel 差分,同理 $gy$,而 $g = \sqrt{gx^2 + gy^2}$

$x_c = x - r \times (gx / g)$ $y_c = y - r \times (gy / g)$

當然閥值判定都還不到位,手動測試好幾個版本,OpenCV 的寫法值得去 trace 一下。代碼僅供玩玩,不代表在其他情況也能使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <vector>
#include <algorithm>
using namespace std;
class IMAGE {
public:
struct Pixel {
int r, g, b;
Pixel(int x = 0, int y = 0, int z = 0):
r(x), g(y), b(z) {}
void read() {
scanf("%d %d %d", &r, &g, &b);
}
Pixel operator-(const Pixel &x) const {
return Pixel(r-x.r, g-x.g, b-x.b);
}
Pixel operator+(const Pixel &x) const {
return Pixel(r+x.r, g+x.g, b+x.b);
}
Pixel operator*(const double x) const {
return Pixel(r*x, g*x, b*x);
}
Pixel operator/(const double x) const {
return Pixel(r/x, g/x, b/x);
}
bool operator==(const Pixel &x) const {
return r == x.r && g == x.g && b == x.b;
}
void print() {
printf("%3d", length());
}
int sum() {
return r + g + b;
}
int length() {
return abs(r) + abs(g) + abs(b);
}
int dist(Pixel x) {
return abs((r + g + b) - (x.r + x.g + x.b));
}
};
int W, H;
static const int MAXN = 256;
Pixel data[MAXN][MAXN];
void read() {
scanf("%d %d", &W, &H);
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
data[i][j].read();
}
void print() {
printf("%d %d\n", W, H);
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
data[i][j].print(), printf("%c", j == W-1 ? '\n' : ' ');
}
inline Pixel getPixel(int x, int y) {
if (x >= 0 && y >= 0 && x < H && y < W)
return data[x][y];
if (y < 0) return data[min(max(x, 0), H-1)][0];
if (y >= W) return data[min(max(x, 0), H-1)][W-1];
if (x < 0) return data[0][min(max(y, 0), W-1)];
if (x >= H) return data[H-1][min(max(y, 0), W-1)];
return Pixel(0, 0, 0);
}
void sobel(int i, int j, int &gx, int &gy) {
static int dx[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
static int dy[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
static int yw[] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
static int xw[] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
Pixel Dx(0, 0, 0), Dy(0, 0, 0);
for (int k = 0; k < 9; k++) {
if (xw[k])
Dx = Dx + getPixel(i+dx[k], j+dy[k]) * xw[k];
if (yw[k])
Dy = Dy + getPixel(i+dx[k], j+dy[k]) * yw[k];
}
gx = Dx.sum(), gy = Dy.sum();
}
int used[MAXN][MAXN];
int gx[MAXN][MAXN], gy[MAXN][MAXN];
double gxy[MAXN][MAXN];
int isValid(int x, int y) {
return x >= 0 && y >= 0 && x < H && y < W;
}
int hough_circle() {
int mxb = INT_MIN, mnb = INT_MAX;
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
int b = data[i][j].length();
mxb = max(mxb, b), mnb = min(mnb, b);
}
}
if (mxb - mnb < 300)
return 0;
int threshold = 250;
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
if (data[i][j].length() >= threshold)
data[i][j] = Pixel(1, 1, 1);
else
data[i][j] = Pixel(0, 0, 0);
}
}
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
sobel(i, j, gx[i][j], gy[i][j]), gxy[i][j] = hypot(gx[i][j], gy[i][j]);
}
}
int ret = 0;
for (double r = 4; r <= min(H, W)/2; r += 1) {
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
used[i][j] = 0;
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
if (gxy[i][j] > 4) {
int xc, yc;
xc = round(i - r * (gx[i][j] / gxy[i][j]));
yc = round(j - r * (gy[i][j] / gxy[i][j]));
if (isValid(xc, yc))
used[xc][yc]++;
}
}
}
int coins = 0;
const int C = 3;
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
if (used[i][j] < 3)
continue;
int sum = 0, mx = 0, has = 0;
for (int p = -C; p <= C; p++) {
for (int q = -C; q <= C; q++) {
if (isValid(i+p, j+q))
sum += used[i+p][j+q], mx = max(mx, used[i+p][j+q]), has += used[i+p][j+q] > 0;
}
}
if (sum > 56 && (has > 36 || mx > 4)) {
coins++;
int cx = i, cy = j;
for (int p = -r-1; p <= r+1; p++) {
for (int q = -r-1; q <= r+1; q++) {
if (isValid(cx+p, cy+q))
used[cx+p][cy+q] = 0;
}
}
}
}
}
if (coins < ret - 2)
break;
ret = max(ret, coins);
}
return ret;
}
} image;
int main() {
image.read();
printf("%d\n", image.hough_circle());
return 0;
}