Архив метки: OpenCV

Определение угла наклона текста на изображении

В данном посте поделюсь итеративным методом определения угла наклона сканированного текста.
Суть метода заключается в следующем: постепенно меняя угол наклона — считаем некоторую характеристику-функцию от изображения (о ней чуть дальше). При каком угле наклона получим максимум — значит это и есть оптимальный угол наклона.
Теперь о том, что считать, для этого внимательно взглянем на то как выглядит текст на сканированном документе. Если представим изображение только в виде черных и белых пикселей и потом спроецируем их все на ось X, то в местах где есть строчки гистограмма будет выдавать пики по черным пикселям, а где междустрочный интервал — по белым пикселям. Пики будут максимально выраженными при правильном угле. Но мы хотим получить наиболее простой алгоритм, поэтому будет считать только кол-во черных и белых пикселей при проекции на ось X. Т.к. строки текста парралельны друг другу, то при лучшем угле кол-во белых пикселей будет максимальным, а кол-во черных минимальным. Этим и воспользуемся:

import numpy as np
import cv2


def get_angle(img):
    # сперва переведём изображение из RGB в чёрно серый
    # значения пикселей будут от 0 до 255
    img_gray = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY)

    # а теперь из серых тонов, сделаем изображение бинарным
    th_box = int(img_gray.shape[0] * 0.007) * 2 + 1
    img_bin_ = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, th_box, th_box)

    img_bin = img_bin_.copy()
    num_rows, num_cols = img_bin.shape[:2]

    best_zero, best_angle = None, 0
    # итеративно поворачиваем изображение на пол градуса
    for my_angle in range(-20, 21, 1):
        rotation_matrix = cv2.getRotationMatrix2D((num_cols/2, num_rows /2 ), my_angle/2, 1)
        img_rotation = cv2.warpAffine(img_bin, rotation_matrix, (num_cols*2, num_rows*2),
                                      borderMode=cv2.BORDER_CONSTANT,
                                      borderValue=255)

        img_01 = np.where(img_rotation > 127, 0, 1)
        sum_y = np.sum(img_01, axis=1)
        th_ = int(img_bin_.shape[0]*0.005)
        sum_y = np.where(sum_y < th_, 0, sum_y)

        num_zeros = sum_y.shape[0] - np.count_nonzero(sum_y)

        if best_zero is None:
            best_zero = num_zeros
            best_angle = my_angle

        # лучший поворот запоминаем
        if num_zeros > best_zero:
            best_zero = num_zeros
            best_angle = my_angle

    return best_angle * 0.5


img = cv2.imread('singapore_01.jpg')
best_angle = get_angle(img)
print(best_angle)

cv2.imshow('Result', img)
cv2.waitKey()

Наложение текста на изображение с помощью python

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

1. Пример наложения текста с использованием библиотеки python-OpenCV

import numpy as np
import cv2

# создадим белое изображение
# или можно считать изобрежние с помощью cv2.imread("path_to_file")
img = np.zeros((256, 512, 3), np.uint8)
img[:, :, :] = 255

font = cv2.FONT_HERSHEY_COMPLEX
# вставка текста красного цвета
cv2.putText(img, 'наш произвольный текст', (10, 150), font, 1, color=(0, 0, 255), thickness=2)

cv2.imshow('Result', img)
cv2.waitKey()

# есть ограниченное кол-во вариантов выбора шрифта
# FONT_HERSHEY_COMPLEX
# FONT_HERSHEY_COMPLEX_SMALL
# FONT_HERSHEY_DUPLEX
# FONT_HERSHEY_PLAIN
# FONT_HERSHEY_SCRIPT_COMPLEX
# FONT_HERSHEY_SCRIPT_SIMPLEX
# FONT_HERSHEY_SIMPLEX
# FONT_HERSHEY_TRIPLEX
# FONT_ITALIC

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

Для более продвинутого добавления надписей (можно сказать даже художественного) давайте сформулируем требования, предъявляемые к скрипту.
1. Использование произвольного шрифта из файла ttf
2. Возможность задать угол наклона текста
3. Возможность задать прозрачность текста
4. Выравнивание текста по центру
Четвёртый пункт очень важен, ведь используя произвольный шрифт мы не можем расчитать ширину надписи, и было бы здорово передать эту предобработку библиотеке.
Теперь перейдём к коду:

2. Пример наложение текста с использованием библиотеки PIL

import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont

# создадим белое изображение
# или можно считать изобрежние с помощью cv2.imread("path_to_file")
img = np.zeros((256, 512, 3), np.uint8)
img[:, :, :] = 255


# для простоты и совместимости возьмем пустое изображение из первого примера
# Чтобы не использовать opencv, а только PIL используйте функцию Image.open()
def put_text_pil(img: np.array, txt: str):
    im = Image.fromarray(img)

    font_size = 24
    font = ImageFont.truetype('LibreFranklin-ExtraBold.ttf', size=font_size)

    draw = ImageDraw.Draw(im)
    # здесь узнаем размеры сгенерированного блока текста
    w, h = draw.textsize(txt, font=font)

    y_pos = 50
    im = Image.fromarray(img)
    draw = ImageDraw.Draw(im)

    # теперь можно центрировать текст
    draw.text((int((img.shape[1] - w)/2), y_pos), txt, fill='rgb(0, 0, 0)', font=font)

    img = np.asarray(im)

    return img


img = put_text_pil(img, 'Some Styled Black Text Here')
cv2.imshow('Result', img)
cv2.waitKey()

Пример кода с альфа каналом и наклоном будет чуть позже

сферизация изображения на python

Сферизация изображения или эффект рыбий глаз на python

Изначально думал что сделаю эффект рыбьего глаза буквально за 5 минут, найдя нужный скрипт в библиотеке openCV. Но на деле оказалось все не так просто и просто одновременно. И так, для сферизации нам нужно сделать матрицу преобразований, которая будет показывать в какое место передвигать каждый пиксель. И после этого применить функцию cv2.remap . На вход этой функции подается «карта исходных точек» и «карта точек куда их надо переместить», а так как все точки изображения передать невозможно, то функция cv2.remap произведет сглаживание остальных точек, в нашем случае cv2.INTER_LINEAR. Для ручной генерации матрицы преобразований нужно переходить в полярную систему координат, делать какие-то циклы итд итп. Поэтому решено было обойтись функциями openCV. А именно initUndistortRectifyMap про которую лучше почитать на официальном сайте opencv. На вход достаточно передать матрицу 3 на 3 и искажающие коэффициенты. Данный вариант и продемонстрирован в коде.

import sys
import cv2
import numpy as np

def main(argv):
    src = cv2.imread('img/cells.png')
    h, w = src.shape[0:2]
    # получаем высоту и ширину изображения для 
    print(h, w)

    # заполняем матрицу преобразования. сначала все нулями
    intrinsics = np.zeros((3, 3), np.float64)

    # матрица intrinsics
    intrinsics[0, 0] = 3500
    intrinsics[1, 1] = 3500
    intrinsics[2, 2] = 1.0
    intrinsics[0, 2] = w/2.
    intrinsics[1, 2] = h/2.
    print(intrinsics)

    newCamMtx = np.zeros((3, 3), np.float64)
    newCamMtx[0, 0] = 3500
    newCamMtx[1, 1] = 3500
    newCamMtx[2, 2] = 1.0
    newCamMtx[0, 2] = w/2.
    newCamMtx[1, 2] = h/2.

    dist_coeffs = np.zeros((1, 4), np.float64)
    dist_coeffs[0, 0] = -40.0
    dist_coeffs[0, 1] = 0.0
    dist_coeffs[0, 2] = 0.0
    dist_coeffs[0, 3] = -0.0
    print dist_coeffs

    map1, map2 = cv2.initUndistortRectifyMap(intrinsics, dist_coeffs, None, newCamMtx, src.shape[:2], cv2.CV_16SC2)
    res = cv2.remap(src, map1, map2, cv2.INTER_LINEAR)

    cv2.imshow("Image_res", res)
    cv2.imshow("Image_origin", src)
    cv2.waitKey(0)

if __name__ == '__main__':
    main(sys.argv)

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

сферизация изображения на python

Эффект рыбий глаз на python

Установка opencv 3 python на windows, инструкция

Чтобы понять как установить opencv на windows для использования c python, проделайте следующие шаги:
1) установка python 2.7 32 бита https://www.python.org/downloads/windows/
2) Устанавливаем numpy библиотеку — http://sourceforge.net/projects/numpy/files/NumPy/1.10.2/numpy-1.10.2-win32-superpack-python2.7.exe/download
3) Теперь надо добавить файл cv2.pyd в директорию с питоном. Для этого скачиваем opencv и разархивируем, затем идем в папку opencv/build/python/2.7 и скопировать файл cv2.pyd в директорию с питоном C:/Python27/lib/site-packages
4) Теперь создадим простую тестовую программу, которая откроет изображение и отобразит его в новом окне.

import cv2

img = cv2.imread("IMG.jpg")

cv2.imshow("Image", img)
cv2.waitKey(0)

Таким образом мы проверили корректность установки. Рекомендуется использовать все 32 битное для большей совместимости.

Детектирование креста на изображении с помощью OpenCV

Это одна из самых первых моих работ на Python и OpenCV. Для участия в конкурсе летательных аппаратов КРОК 2013 необходимо было научить квадрокоптер приземляться на метку диаметром 3 метра, на которой черным по белому нарисован крест. На рисунке ниже изображен крест и его линейные размеры:

размеры креста для распознавания и посадки

Таким образом на квадрокоптер была установлена нижняя веб-камера для детектирования метки креста. Как только под летающей машиной окажется центр пересечения, то он должен стабилизироваться и приземлиться. Стоит отметить тот факт, что высота полета не должна была превышать 3 метра, а со средней высоты в 1.5 метра метка целиком не видна (угол обзора стандартной веб-камеры где-то градусов 60-70). Исходя из этих простых расчетов решено было сосредоточиться именно на распознавании пересекающихся прямых. В OpenCV есть стандартная функция

cv2.HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) → lines

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

img_gray = cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY)
    img_blur = cv2.blur(img_gray, (3, 3))
    img_bin = cv2.threshold(img_blur, 127, 255, cv2.THRESH_OTSU)[1]

2) выделить контуры и пройтись по каждому из них

contours, hierarchy = cv2.findContours(img_bin.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    for cnt in contours:
        approx = cv2.approxPolyDP(cnt, 0.02*cv2.arcLength(cnt,True),True)
        approx_area = cv2.contourArea(approx)

3) проверить базовые вещи контура (площадь, соотношение длины и высоты)
4) найти обрамляющий квадрат для контура и из вершин квадрата провести пересекающиеся прямые.
5) если вся наша область окажется одним цветом, то тогда считаем область искомым крестом
6) ищем и отображаем центр

В итоге получилось примерно так:

распознанная метка-крестраспознанная метка-крест для посадки КРОК
С полной программой можно ознакомиться на гитхабе. Как говорил — это моя одна из первых работ на питоне и opencv, поэтому не следует воспринимать её всерьез, однако, надеюсь что новым участникам беспилотных аппаратов КРОК этот скрипт поможет быстрее стартовать и мягко приземлить свою машину.

Как определить HSV цвет в OpenCV (python)

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

cv2.inRange(src, lowerb, upperb[, dst])

позволяет выделить маску области. Однако резко встает вопрос корректного определения верхней и нижней границы (lowerb и upperb). А в этом как раз и прелесть и сложность пространства HSV. Кодировка цвета в модели HSV расшифровывается так: H — цветовой тон, S — насыщенность, V — яркость. Поэтому для выделения по цвету, достаточно указать диапазон H, а насыщенность и яркость будет сильно варьироваться (из-за разной освещенности, углов скоса объекта и.т.д.) На практике обычно делают примерно так: если H соcтавляющая 80 (зеленый цвет), берут нижнюю границу H=80-10 [70, 50, 50], а верхнюю H=80+10 [90,255,255]. Кажется что вот, все просто, посмотрим HSV цвет в фотошопе или пейнте и установим нужную границу… Но, в отличии от цветовой модели RGB, диапазон значений H в разных программах может задаваться по разному и его необходимо пересчитывать. Чтобы облегчить и на лету определять HSV цвет в OpenCV написал следующий небольшой скрипт:

import cv2
import cv2.cv as cv

cap = cv2.VideoCapture(0)

#set camera width and height
CAM_WIDTH = 640
CAM_HEIGHT = 480
cap.set(cv.CV_CAP_PROP_FRAME_WIDTH, CAM_WIDTH)
cap.set(cv.CV_CAP_PROP_FRAME_HEIGHT, CAM_HEIGHT)

selected_color = None
image_origin = None


def onmouse(event, x, y, flags, param):
    global image_origin, selected_color
    if flags & cv2.EVENT_FLAG_LBUTTON:
        #taking squire cur 4x4 and scale it to 1x1
        cut = image_origin[y-1:y+2, x-1:x+2]
        cut = cv2.pyrDown(cut)
        cut = cv2.pyrDown(cut)

        selected_color = (int(cut[0][0][0]), int(cut[0][0][1]), int(cut[0][0][2]))
        #conveting to HSV and printing result
        selected_color_HSV = cv2.cvtColor(cut, cv2.COLOR_BGR2HSV)
        print(selected_color_HSV)

while(1):

    # Take each frame
    _, frame = cap.read()
    image_origin = frame.copy()


    #drawing selected colors
    if selected_color is not None:
        cv2.circle(frame, (CAM_WIDTH-20,20), 20, selected_color, -1)

    #show image and set callback
    cv2.imshow('img', frame)
    cv2.setMouseCallback('img', onmouse)

    k = cv2.waitKey(5) & 0xFF
    if k == 27:
        break

cv2.destroyAllWindows()

В данном пример изображение берется из видео-потока. Идея состоит в том, что после клика по видео изображению, выделяется квадрат 4 на 4, этот квадрат уменьшаем до размера 1 на 1 (усредняем значения цвета) и с помощью функции cv2.cvtColor(img, cv2.COLOR_BGR2HSV) переводим изображение в режим HSV.

Надеюсь этот скрипт поможет вам для работы в приложениях с OpenCV.