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.
100 lines
3.2 KiB
100 lines
3.2 KiB
import cv2 |
|
import numpy as np |
|
import random |
|
import json |
|
|
|
# Загрузка изображения |
|
image = cv2.imread("a4keyboard_scan.jpg") |
|
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) |
|
|
|
# Применяем адаптивный порог |
|
thresh = cv2.adaptiveThreshold( |
|
gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 11, 2 |
|
) |
|
|
|
# Находим контуры и собираем размеры прямоугольников |
|
rectangles = [ |
|
cv2.boundingRect(contour) |
|
for contour in cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[ |
|
0 |
|
] |
|
if (cv2.boundingRect(contour)[2] > 20 and cv2.boundingRect(contour)[3] > 20) |
|
] |
|
|
|
# Вычисляем медианные размеры |
|
widths, heights = zip(*[(w, h) for _, _, w, h in rectangles]) |
|
median_width, median_height = np.median(widths), np.median(heights) |
|
|
|
# Фильтрация прямоугольников по размеру |
|
filtered_rectangles = [ |
|
rect |
|
for rect in rectangles |
|
if 0.5 * median_width <= rect[2] <= 10 * median_width |
|
and 0.5 * median_height <= rect[3] <= 10 * median_height |
|
] |
|
|
|
# Порог для тёмного фона |
|
darkness_threshold = 80 |
|
filtered_rectangles_dark = [ |
|
rect |
|
for rect in filtered_rectangles |
|
if cv2.mean(gray[rect[1] : rect[1] + rect[3], rect[0] : rect[0] + rect[2]])[0] |
|
< darkness_threshold |
|
] |
|
|
|
# Уменьшаем размеры и закрашиваем области |
|
padding = 8 |
|
for x, y, w, h in filtered_rectangles_dark: |
|
x_padded, y_padded = x + padding, y + padding |
|
w_padded, h_padded = max(w - 2 * padding, 1), max(h - 2 * padding, 1) |
|
roi = image[y_padded : y_padded + h_padded, x_padded : x_padded + w_padded] |
|
mean_color = cv2.mean(roi)[:3] |
|
|
|
# Генерация области с шумом |
|
noise = np.random.uniform(-10, 10, roi.shape).astype(np.int16) |
|
noisy_color = np.clip(np.array(mean_color, dtype=np.int16) + noise, 0, 255).astype( |
|
np.uint8 |
|
) |
|
image[y_padded : y_padded + h_padded, x_padded : x_padded + w_padded] = noisy_color |
|
|
|
# Масштабирование изображения и прямоугольников |
|
scale_factor = 0.65 |
|
# image = cv2.resize(image, (image.shape[1] // 2, image.shape[0] // 2)) |
|
image = cv2.resize( |
|
image, None, fx=scale_factor, fy=scale_factor, interpolation=cv2.INTER_LINEAR |
|
) |
|
scaled_rectangles = [ |
|
( |
|
int(x * scale_factor), |
|
int(y * scale_factor), |
|
int(w * scale_factor), |
|
int(h * scale_factor), |
|
) |
|
for x, y, w, h in filtered_rectangles_dark |
|
] |
|
|
|
# Сортируем прямоугольники построчно |
|
row_tolerance = 10 |
|
scaled_rectangles.sort(key=lambda rect: rect[1]) |
|
|
|
rows = [] |
|
current_row = [scaled_rectangles[0]] |
|
|
|
for rect in scaled_rectangles[1:]: |
|
if abs(rect[1] - current_row[-1][1]) <= row_tolerance: |
|
current_row.append(rect) |
|
else: |
|
rows.append(current_row) |
|
current_row = [rect] |
|
|
|
rows.append(current_row) |
|
|
|
sorted_rectangles = [ |
|
rect for row in rows for rect in sorted(row, key=lambda rect: rect[0]) |
|
] |
|
|
|
# Сохраняем изображение и данные прямоугольников |
|
cv2.imwrite("background_image.png", image) |
|
|
|
with open("rectangles.json", "w") as f: |
|
json.dump(sorted_rectangles, f)
|
|
|