Наблюдение за табло. Часть 3. Поиск красного и обрезка


Итак, в прошлых частях мы получили картинку и перенесли ее на компьютер. Теперь надо работать с ней, извлекать цифры. Но перед распознаванием картинку желательно упростить, даже нейросети лучше скармливать изображения попроще, это человек может вычленить главное за доли секунды, компьютер тоже может, но у нас не сервера яндекса, так что лучше все же предварительно обработаем.

Содержание цикла

Задумка простая – движемся с правого верхнего угла, доходим до определенного цвета (в данном случае оттенок красного), отступаем некие размеры и вырезаем что получилось. Первый вопрос у вас наверно сразу будет: почему с правого верхнего? А посмотрите повнимательнее на исходник:



Есть еще одно табло слева ниже. Иногда его нет, но иногда есть. И оно нас совсем не интересует.

Вот скрипт с подробными комментариями

from PIL import Image
import math

def find_color_and_crop(input_path, output_path, target_hex="#C26577", tolerance=30):
    """
    Ищет цвет начиная от правого верхнего угла к центру и делает кроп.
    """
    
    # 1. Загрузка изображения
    try:
        img = Image.open(input_path).convert("RGB")
    except FileNotFoundError:
        print(f"Ошибка: Файл {input_path} не найден.")
        return
    except Exception as e:
        print(f"Ошибка при открытии файла: {e}")
        return

    width, height = img.size
    pixels = img.load()

    # Преобразование целевого цвета из HEX в RGB
    target_hex = target_hex.lstrip('#')
    target_r = int(target_hex[0:2], 16)
    target_g = int(target_hex[2:4], 16)
    target_b = int(target_hex[4:6], 16)
    target_color = (target_r, target_g, target_b)

    print(f"Размер изображения: {width}x{height}")
    print(f"Поиск цвета {target_color} с допуском {tolerance}...")

    # Координаты центра
    center_x, center_y = width / 2, height / 2
    
    # Координаты старта (правый верхний угол)
    start_x, start_y = width - 1, 0

    found_point = None
    best_diff = float('inf')
    best_point = None

    # Более эффективный поиск: идем по спирали или по диагонали от угла к центру
    # Но для простоты - проверим все пиксели, но с приоритетом направления
    
    # Создаем список координат, отсортированных по расстоянию от старта
    # и по направлению к центру
    coords_with_priority = []
    
    for y in range(height):
        for x in range(width):
            # Вычисляем приоритет: меньше расстояние до линии угол-центр = выше приоритет
            # Вектор от старта до точки
            dx = x - start_x
            dy = y - start_y
            
            # Вектор от старта до центра
            cdx = center_x - start_x
            cdy = center_y - start_y
            
            # Расстояние от точки до линии угол-центр
            if cdx != 0 or cdy != 0:
                # Перпендикулярное расстояние до линии
                line_len = math.sqrt(cdx**2 + cdy**2)
                if line_len > 0:
                    # Расстояние от точки до линии через векторное произведение
                    dist_to_line = abs(dx*cdy - dy*cdx) / line_len
                else:
                    dist_to_line = 0
            else:
                dist_to_line = 0
            
            # Расстояние от старта
            dist_from_start = math.sqrt(dx**2 + dy**2)
            
            # Приоритет: меньше расстояние до линии и меньше расстояние от старта
            priority = dist_from_start + dist_to_line * 5  # Вес для расстояния до линии
            
            coords_with_priority.append((x, y, priority, dist_from_start))

    # Сортируем по приоритету
    coords_with_priority.sort(key=lambda k: k[2])

    # Поиск цвета
    for x, y, priority, dist in coords_with_priority:
        r, g, b = pixels[x, y]
        
        # Вычисление разницы цветов (Манхэттенское расстояние)
        diff = abs(r - target_r) + abs(g - target_g) + abs(b - target_b)
        
        if diff <= tolerance:
            found_point = (x, y)
            print(f"Цвет найден в координатах: X={x}, Y={y}. Разница цвета: {diff}")
            print(f"Расстояние от старта: {dist:.1f} пикселей")
            break
        elif diff < best_diff:
            best_diff = diff
            best_point = (x, y)

    if not found_point:
        if best_point:
            x, y = best_point
            r, g, b = pixels[x, y]
            print(f"Точный цвет не найден. Ближайший цвет: RGB{target_color} -> найден RGB({r},{g},{b}) с разницей {best_diff}")
            print(f"Координаты ближайшего цвета: X={x}, Y={y}")
            found_point = best_point
        else:
            print("Цвет не найден в указанной области с заданным допуском.")
            return

    x, y = found_point

    # 3. Расчет координат для обрезки
    left = x - 200
    top = y - 50
    right = x + 50
    bottom = y + 100

    # Корректировка границ
    left = max(0, left)
    top = max(0, top)
    right = min(width, right)
    bottom = min(height, bottom)

    if left >= right or top >= bottom:
        print("Ошибка: Область обрезки получилась некорректной.")
        return

    print(f"Область обрезки: Left={left}, Top={top}, Right={right}, Bottom={bottom}")

    # 4. Создание нового изображения
    cropped_img = img.crop((left, top, right, bottom))
    cropped_img.save(output_path)
    print(f"Изображение успешно сохранено в {output_path}")

if __name__ == "__main__":
    input_file = "input.jpg" 
    output_file = "result_crop.jpg"
    
    # Увеличим допуск для JPEG изображений
    find_color_and_crop(input_file, output_file, tolerance=30)
И в итоге получается вот такая обрезанная картинка, распознавать цифры с которой уже гораздо проще

Автор этого материала - я - Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML - то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.

тегизаметки, распознавание, python



Занимательная постапокалиптическая сказка
Отправка сообщений на почту в Visual Studio 2013 C++
Отключение строгого режима MySQL