A small keyboard based on Python/Tkinter for inserting special characters. Dependencies: xclip, xdotool for clipboard handling and auto-insertion.
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

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)