|
|
@@ -155,25 +155,41 @@ def extract_grid_colours(img, num_rows, num_cols, sampling_block_size_ratio):
|
|
155
|
155
|
def grid_colours_to_blocks(grid_colours, num_rows, num_cols, sampling_threshold):
|
|
156
|
156
|
grid = copy.deepcopy(grid_colours)
|
|
157
|
157
|
warning = False
|
|
158
|
|
- for row in range(round(num_rows / 2)):
|
|
|
158
|
+
|
|
|
159
|
+ midpoint = num_rows // 2 + (0 if num_rows % 2 == 0 else 1)
|
|
|
160
|
+ for row in range(midpoint):
|
|
159
|
161
|
for col in range(num_cols):
|
|
|
162
|
+ # If there is an odd number of rows then row and row2 will point to
|
|
|
163
|
+ # the same row when we reach the middle. Doesn't seem worth adding a
|
|
|
164
|
+ # special case.
|
|
160
|
165
|
row2 = num_rows - row - 1
|
|
161
|
166
|
col2 = num_cols - col - 1
|
|
162
|
167
|
delta1 = grid_colours[row][col] - sampling_threshold
|
|
163
|
168
|
delta2 = grid_colours[row2][col2] - sampling_threshold
|
|
164
|
169
|
|
|
165
|
170
|
if (delta1 > 0) and (delta2 > 0):
|
|
166
|
|
- block = 0
|
|
|
171
|
+ filled = False
|
|
167
|
172
|
elif (delta1 < 0) and (delta2 < 0):
|
|
168
|
|
- block = 1
|
|
|
173
|
+ filled = True
|
|
169
|
174
|
else:
|
|
170
|
175
|
warning = True
|
|
171
|
176
|
if abs(delta1) > abs(delta2):
|
|
172
|
|
- block = 1 if delta1 < 0 else 0
|
|
|
177
|
+ filled = delta1 < 0
|
|
173
|
178
|
else:
|
|
174
|
|
- block = 1 if delta2 < 0 else 0
|
|
|
179
|
+ filled = delta2 < 0
|
|
|
180
|
+
|
|
|
181
|
+ grid[row][col] = {'filled': filled}
|
|
|
182
|
+ grid[row2][col2] = {'filled': filled}
|
|
175
|
183
|
|
|
176
|
|
- grid[row][col] = grid[row2][col2] = block
|
|
|
184
|
+ number = 1
|
|
|
185
|
+ for row in range(num_rows):
|
|
|
186
|
+ for col in range(num_cols):
|
|
|
187
|
+ if (not grid[row][col]['filled'] and (
|
|
|
188
|
+ (((col == 0) or grid[row][col - 1]['filled']) and (col < num_cols - 1) and not grid[row][col + 1]['filled']) or
|
|
|
189
|
+ (((row == 0) or grid[row - 1][col]['filled']) and (row < num_rows - 1) and not grid[row + 1][col]['filled'])
|
|
|
190
|
+ )):
|
|
|
191
|
+ grid[row][col]['number'] = number
|
|
|
192
|
+ number += 1
|
|
177
|
193
|
|
|
178
|
194
|
return warning, grid
|
|
179
|
195
|
|
|
|
@@ -188,25 +204,18 @@ def draw_point(image, point, colour):
|
|
188
|
204
|
image[y, x] = colour
|
|
189
|
205
|
|
|
190
|
206
|
|
|
191
|
|
-def show_image(image):
|
|
192
|
|
- cv2.namedWindow('xword', cv2.WINDOW_NORMAL)
|
|
193
|
|
- cv2.imshow('xword', image)
|
|
194
|
|
- while cv2.waitKey() & 0xFF != ord('q'):
|
|
195
|
|
- pass
|
|
196
|
|
- cv2.destroyAllWindows()
|
|
197
|
|
-
|
|
198
|
|
-
|
|
199
|
207
|
def extract_crossword_grid(
|
|
200
|
208
|
file_name,
|
|
201
|
|
- filter_colours=False,
|
|
202
|
|
- colour_filter_threshold=48,
|
|
|
209
|
+ callback=None,
|
|
|
210
|
+ remove_colours=False,
|
|
|
211
|
+ colour_removal_threshold=48,
|
|
203
|
212
|
gaussian_blur_size=11,
|
|
204
|
213
|
adaptive_threshold_block_size=11,
|
|
205
|
214
|
adaptive_threshold_mean_adjustment=2,
|
|
206
|
215
|
square=True,
|
|
207
|
216
|
num_dilations=1,
|
|
208
|
217
|
contour_erosion_kernel_size=5,
|
|
209
|
|
- contour_erosion_iterations=6,
|
|
|
218
|
+ contour_erosion_iterations=5,
|
|
210
|
219
|
line_detector_element_size=51,
|
|
211
|
220
|
sampling_block_size_ratio=0.25,
|
|
212
|
221
|
sampling_threshold_quantile=0.3,
|
|
|
@@ -214,9 +223,13 @@ def extract_crossword_grid(
|
|
214
|
223
|
):
|
|
215
|
224
|
warnings = []
|
|
216
|
225
|
|
|
217
|
|
- original = load_image_as_greyscale(file_name, filter_colours, colour_filter_threshold)
|
|
|
226
|
+ original = load_image_as_greyscale(file_name, remove_colours, colour_removal_threshold)
|
|
|
227
|
+ if callback is not None:
|
|
|
228
|
+ callback('original', original)
|
|
218
|
229
|
|
|
219
|
230
|
img = preprocess_image(original, gaussian_blur_size, adaptive_threshold_block_size, adaptive_threshold_mean_adjustment, num_dilations)
|
|
|
231
|
+ if callback is not None:
|
|
|
232
|
+ callback('preprocessed', img)
|
|
220
|
233
|
|
|
221
|
234
|
biggest = find_biggest_contour(img)
|
|
222
|
235
|
biggest = erode_contour(img.shape, biggest, contour_erosion_kernel_size, contour_erosion_iterations)
|
|
|
@@ -224,6 +237,8 @@ def extract_crossword_grid(
|
|
224
|
237
|
top_left, top_right, bottom_right, bottom_left = get_contour_corners(img, biggest)
|
|
225
|
238
|
|
|
226
|
239
|
img = extract_square(img, top_left, top_right, bottom_right, bottom_left)
|
|
|
240
|
+ if callback is not None:
|
|
|
241
|
+ callback('pre-fft', img)
|
|
227
|
242
|
|
|
228
|
243
|
num_rows = get_line_frequency(img, line_detector_element_size, 1)
|
|
229
|
244
|
num_cols = get_line_frequency(img, line_detector_element_size, 0)
|
|
|
@@ -245,7 +260,7 @@ def extract_crossword_grid(
|
|
245
|
260
|
return warnings, grid, num_rows, num_cols, block_img
|
|
246
|
261
|
|
|
247
|
262
|
|
|
248
|
|
-def grid_to_png(
|
|
|
263
|
+def draw_grid(
|
|
249
|
264
|
grid,
|
|
250
|
265
|
num_rows,
|
|
251
|
266
|
num_cols,
|
|
|
@@ -261,9 +276,7 @@ def grid_to_png(
|
|
261
|
276
|
for row in range(num_rows):
|
|
262
|
277
|
y = row * step + grid_line_thickness + grid_border_size
|
|
263
|
278
|
for col in range(num_cols):
|
|
264
|
|
- if grid[row][col] == 0:
|
|
|
279
|
+ if not grid[row][col]['filled']:
|
|
265
|
280
|
x = col * step + grid_line_thickness + grid_border_size
|
|
266
|
281
|
cv2.rectangle(output, (x, y), (x + grid_square_size - 1, y + grid_square_size - 1), 255, -1)
|
|
267
|
|
-
|
|
268
|
|
- _, png = cv2.imencode('.png', output)
|
|
269
|
|
- return png.tobytes()
|
|
|
282
|
+ return output
|