Извлечение полуструктурированного текста из сложного пользовательского интерфейса (Golf Simulator) - PullRequest
0 голосов
/ 26 февраля 2019

Я довольно новичок в мире OCR, OpenCV, Tesseract и т. Д. И надеялся получить совет или толчок в правильном направлении для проекта, над которым я работаю.Для контекста я занимаюсь гольфом на симуляторе в помещении, который питается от Full Swing Golf .Моя цель - создать приложение (желательно iphone, но с настольным компьютером тоже хорошо), которое сможет собирать данные, предоставленные симулятором, и обрабатывать их, как мне бы хотелось.Общий рабочий процесс будет выглядеть примерно так:

  1. Настройка камеры iPhone или ноутбука для просмотра экрана симулятора.
  2. Удар по мячу
  3. Отображается экран статистики, который выглядит болееили меньше как:

enter image description here

Обнаружение отображения экрана статистики и получение всех соответствующих данных:
| Distance | Launch | Back Spin | Club Speed | Carry | To Pin | Direction | Ball Speed | Side Spin | Club Face | Club Path |
|----------|--------|-----------|------------|-------|--------|-----------|------------|-----------|-----------|-----------|
| 345      | 13     | 3350      | 135        | 335   | 80     | 2.4       | 190        | 350       | 4.3       | 1.6       |

5- ?: Сохранение данных в моем приложении, отслеживание их во времени и т. Д. ...

Пока что попыток:

Казалось, что OpenCV matchTemplate будет простым способом найти все заголовки на изображении (Расстояние, Запуск и т. Д.).Кажется, это работает, когда изображение и шаблон имеют идеальное разрешение.Однако, поскольку это будет приложение для iPhone, я не могу гарантировать качество (в пределах разумного).Кроме того, экран почти никогда не будет ровным, как показано выше.Скорее всего, камера будет смещена в сторону, и нам придется соответственно откинуть ее.Я попытался использовать следующее изображение для работы с моей логикой, но безрезультатно:

enter image description here

Нахождение контрольных точек для выкручивания через getPerspectiveTransform и warpPerspective оказались невероятно трудными из-за вышеуказанных проблем с соответствующими шаблонами.

Я также попытался динамически настроить масштабирование с помощью кода, подобного следующему:

def findTemplateLocation(image_path):
    template = cv2.imread(image_path)
    template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

    w, h = template.shape[::-1]
    threshold = 0.65
    loc = []

    for scale in np.linspace(0.1, 2, 20)[::-1]:
        resized = imutils.resize(template, width=int(template.shape[1] * scale))
        w, h = resized.shape[::-1]
        res = cv2.matchTemplate(image_gray, resized, cv2.TM_CCOEFF_NORMED)

        loc = np.where(res >= threshold)
        if len(list(zip(*loc[::-1]))) > 0:
            break

    if loc and len(list(zip(*loc[::-1]))) > 0:
        adjusted_w = int(w/scale)
        adjusted_h = int(h/scale)
        print(str(adjusted_w) + " " + str(adjusted_h) + " " + str(scale))

        ret = []
        for pt in zip(*loc[::-1]):
            ret.append({'width': w, 'height': h, 'location': pt})

        return ret

    return None

Это все еще возвращает тонну ложных срабатываний.

Я надеюсь получить совет о том, как подойти к этой проблеме с чистого листа.Я открыт для любого языка / рабочего процесса.

Если мне кажется, что я на правильном пути, мой текущий код - https://gist.github.com/naderhen/9ec8d45f13d92507131d5bce0e84fad8.Буду очень признателен за любые предложения по лучшим следующим шагам.

Спасибо за любую помощь, которую вы можете предоставить!

РЕДАКТИРОВАТЬ: Дополнительные ресурсы

Я имеюЯ загрузил несколько видео и фотографий с моего времени на симуляторе в эти выходные: https://www.dropbox.com/sh/5vub2mi4rvunyaw/AAAY1_7Q_WBV4JvmDD0dEiTDa?dl=0

Я попытался получить несколько разных ракурсов, с разным освещением и т. д. Пожалуйста, дайте мне знать, еслиЯ могу предоставить любые другие ресурсы, которые могут помочь.

1 Ответ

0 голосов
/ 28 февраля 2019

Итак, я попробовал два разных метода:

  1. Обнаружение контура - Это, казалось, самый очевидный метод, поскольку экран статистики является основным часть изображения и присутствует во всех ваших изображениях.Хотя он работает с двумя из трех изображений, он может быть не очень надежным с параметрами.Вот шаги, которые я попробовал для контура:

    Сначала получите изображение в оттенках серого или выберите один из каналов Значение в HSV .Затем пороговое значение изображения с помощью Otsu или Adaptive Thresholding .После игры с большим количеством связанных параметров я получил удовлетворительные результаты, которые в основном означали бы хороший целый экран статистики белого цвета на черном фоне.После этого сортируйте контуры следующим образом:

    contours = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)[1]
    # Sort the contours to avoid unnecessary comparison in the for loop below
    cntsSorted = sorted(contours, key=lambda x: cv2.contourArea(x), reverse=True)
    
    for cnt in cntsSorted[0:20]:
        peri = cv2.arcLength(cnt, True)
        approx = cv2.approxPolyDP(cnt, 0.04 * peri, True)
        if len(approx) == 4 and peri > 10000:
            cv2.drawContours(sorted_image, cnt, -1, (0, 255, 0), 10)
    
  2. Обнаружение и сопоставление элементов: поскольку использование контуров было недостаточно надежным, я попробовал другой метод, над которым работалпохожая проблема, как у вас.Этот метод довольно надежный, намного быстрее (я пробовал это на телефоне Android 2 года назад, и он сделал бы работу менее чем за секунду для изображения 1280 x 760).Однако, после примерки ваших рабочих кейсов, я понял, что ваши изображения довольно расплывчаты.Я имею в виду, что у вас есть два изображения в вашем вопросе, которые имеют довольно похожих праймериз , и это работает для этого, но изображения, которые вы разместили в комментариях, очень отличаются от этих и, следовательно,он не находит подходящего количества хороших совпадений (по крайней мере, 10 в моем случае).Если вы можете опубликовать хороший набор изображений, с которыми вы на самом деле столкнетесь, я обновлю этот ответ моими результатами в новом наборе.Что еще более важно, изображения сцены, очевидно, меняются в перспективе, что не должно вызывать проблем, если вы способны получить очень хорошее исходное изображение (как первое в вашем вопросе).Однако изменение условий освещения может быть болезненным.Я бы предложил использовать другие цветовые пространства, такие как HSV, Lab и Luv вместо BGR. Здесь , где вы можете найти рабочий пример того, как реализовать свой собственный сопоставитель функций.Существуют некоторые изменения кода, необходимые в зависимости от используемой версии OpenCV, но я уверен, что вы сможете найти решения (я сделал;)).

Хороший пример:

Matches

Found it

Некоторые предложения:

  1. Попробуйте получитьмаксимально чистое изображение для изображения, которое вы используете, чтобы соответствовать другим (ваше первое изображение в моем случае).Надеюсь, это потребует от вас меньше обработки.

  2. Попробуйте использовать нерезкую маску перед нахождением ключевых точек.

  3. Мои результаты получены при использовании ORB ,Вы также можете попробовать другие детекторы / дескрипторы, такие как SURF, SIFT и FAST.

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

Надеюсь, это поможет!Напишите комментарий, если у вас есть какие-либо дополнительные вопросы и / или когда у вас есть готовая хорошая картинка (потирает ладони).Ура!

Редактировать 1: это код, который я использовал для обнаружения и сопоставления функций в Opencv 3.4.3 и Python 3.4

def unsharp_mask(im):
    # This is used to sharpen images
    gaussian_3 = cv2.GaussianBlur(im, (3, 3), 3.0)
    return cv2.addWeighted(im, 2.0, gaussian_3, -1.0, 0, im)

def screen_finder2(image, source, num=0):
    def resize(im, new_width):
        r = float(new_width) / im.shape[1]
        dim = (new_width, int(im.shape[0] * r))
        return cv2.resize(im, dim, interpolation=cv2.INTER_AREA)
    width = 300
    source = resize(source, new_width=width)
    image = resize(image, new_width=width)

    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2LUV)
    image, u, v = cv2.split(hsv)

    hsv = cv2.cvtColor(source, cv2.COLOR_BGR2LUV)
    source, u, v = cv2.split(hsv)

    MIN_MATCH_COUNT = 10
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(image, None)
    kp2, des2 = orb.detectAndCompute(source, None)

    flann = cv2.DescriptorMatcher_create(cv2.DescriptorMatcher_FLANNBASED)
    # Without the below 2 lines, matching doesn't work
    des1 = np.asarray(des1, dtype=np.float32)
    des2 = np.asarray(des2, dtype=np.float32)

    matches = flann.knnMatch(des1, des2, k=2)

    # store all the good matches as per Lowe's ratio test
    good = []
    for m, n in matches:
        if m.distance < 0.7 * n.distance:
            good.append(m)

    if len(good) >= MIN_MATCH_COUNT:
        src_pts = np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1, 
                                                                         1, 2)
        dst_pts = np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1, 
                                                                         1, 2)

        M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
        matchesMask = mask.ravel().tolist()

        h,w = image.shape
        pts = np.float32([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]).reshape(-1, 
                                                                         1, 2)
        dst = cv2.perspectiveTransform(pts, M)
        source_bgr = cv2.cvtColor(source, cv2.COLOR_GRAY2BGR)
        img2 = cv2.polylines(source_bgr, [np.int32(dst)], True, (0,0,255), 3, 
                             cv2.LINE_AA)
        cv2.imwrite("out"+str(num)+".jpg", img2)
    else:
        print("Not enough matches." + str(len(good)))
        matchesMask = None

    draw_params = dict(matchColor=(0, 255, 0), # draw matches in green color
                       singlePointColor=None,
                       matchesMask=matchesMask, # draw only inliers
                       flags=2)
    img3 = cv2.drawMatches(image, kp1, source, kp2, good, None, **draw_params)
    cv2.imwrite("ORB"+str(num)+".jpg", img3)


match_image = unsharp_mask(cv2.imread("source.jpg"))
image_1 = unsharp_mask(cv2.imread("Screen_1.jpg"))
screen_finder2(match_image, image_1, num=1)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...