Как централизовать и изменить размер цифр с помощью opencv?

Я хочу распознать некоторые отсканированные формы (заполненные вручную). Это первый раз, когда я делаю что-то серьезное, используя компьютерное зрение. Пока я могу найти квадраты, содержащие цифры для поля даты:

введите здесь описание изображения

Глядя на пример набора данных рукописных цифр, который поставляется с OpenCV, я вижу, что цифры централизованы и изменены на (20, 20):

введите здесь описание изображения

Поскольку это может быть довольно распространенной проблемой, мне интересно, реализован ли алгоритм уже в OpenCV (или numpy, scipy и т. д.), поэтому мне не нужно изобретать велосипед.

Возникает вопрос: есть ли в Python встроенный конвейер для нормализации образцов?


person Paulo Scardine    schedule 04.07.2017    source источник
comment
Что вы имеете в виду под нормализацией? Вы имеете в виду изменение размера и центрирование? Вы уже сделали самую сложную часть — получили контуры! Просто найдите cv2.boundingRect() вокруг контуров, возможно, увеличьте размер поля на 1 или 2 пикселя в каждом направлении, если вы не хотите, чтобы белый цвет касался границы, а затем измените масштаб области интереса до нужного размера.   -  person alkasm    schedule 05.07.2017
comment
Что бы я сделал, так это нашел ограничивающую рамку для каждой цифры, обрезал эту часть, а затем изменил размер до желаемых пропорций.   -  person DarkCygnus    schedule 05.07.2017
comment
Да, найти ограничивающую рамку и масштабировать достаточно просто; есть также соотношение сторон, чтобы рассмотреть. Много лет назад я написал алгоритм для создания матрицы аффинного преобразования, которая могла бы одновременно заботиться о соотношении сторон и размере, но я забыл, как я это сделал (использовал его для обрезки загружаемых пользователем изображений до стандартного размера в PIL). К сожалению, этот код утерян.   -  person Paulo Scardine    schedule 05.07.2017


Ответы (2)


Встроенный конвейер не уверен, но вы можете реализовать свой собственный, если у вас уже есть контуры, выполнив следующие действия (на основе моего комментария):

Получите ограничивающий прямоугольник контура (следовательно, центрируясь на нем) и обрежьте эту часть:

x,y,w,h = cv2.boundingRect(cnt)
imgCrop = img[x:(x+w), y:(y+h)]

Измените размер изображения до нужного размера (скажем, 20 x 20):

imgResized = cv2.resize(imgCrop, (20,20))   

Вы также можете изменить размер осей в определенном соотношении, например:

imgResized = cv2.resize(imgCrop, (0,0), fx=0.5, fy=0.5)  

или с помощью scipy (как предложено в этомвопрос):

imgResized = scipy.misc.imresize(imgCrop, 0.5)  

Бонус: проверьте это отличный учебник по основам работы с изображениями с помощью Python и OpenCV, где они показывают другой способ изменения размера с учетом соотношения сторон и интерполяции для лучших результатов, извлекая из него :

imgResized = cv2.resize(imgCrop, (20,20), interpolation = cv2.INTER_AREA)
person DarkCygnus    schedule 04.07.2017
comment
Мой разум был зациклен на эстетике, но, наверное, я могу игнорировать это. Я думаю, у меня не будет проблем, если я изменю размер всех входных данных до 20 x 20 и проигнорирую соотношение сторон - пока я делаю это как с обучающим набором, так и с тестовым набором, все должно быть в порядке. Что вы думаете? - person Paulo Scardine; 05.07.2017
comment
Я думаю, что да, вы должны использовать одинаковые размеры входных изображений при обучении, перекрестной проверке и тестировании, поскольку большинство инструментов, таких как нейронные сети (вы делаете MNIST, я уверен), требуют входных данных одинакового размера. Однако вы не должны полностью игнорировать соотношение сторон. Если у вас сильная деформация, это может повлиять на вашу тренировочную эффективность в целом, так как некоторые цифры (например, цифра 1) уже других. - person DarkCygnus; 05.07.2017
comment
Чтобы предотвратить это, вы можете проверить, что полученное соотношение сторон ограничительной рамки (ш/в) близко к 1.0. если у вас значительно больше ширины или высоты, вы можете добавить больше пикселей при обрезке изображения (вместо img[x:(x+w), y:(y+h)] вы можете, скажем, добавить еще 4 пикселя ширины, выполнив img[((x-2):(x+w+2), y:(y+h)] ). С другой стороны, если вы хотите больше высоты, просто будьте осторожны, чтобы не выйти за пределы. - person DarkCygnus; 05.07.2017

В итоге я использовал эту функцию:

def norm_digit(im):
    h, w = im.shape
    if h > w:
        top, left = round(h * 0.1), round((1.2 * h - w) / 2)
    else:
        top, left = round(w * 0.1), round((1.2 * w - h) / 2)

    return cv2.resize(
        cv2.copyMakeBorder(im, top, top, left, left, cv2.BORDER_CONSTANT), 
        (20, 20)
    )

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

person Paulo Scardine    schedule 06.07.2017