Архив за месяц: Июнь 2014

Чтение Word файлов на python в windows

На самом деле библиотек для чтения doc, docx файлов превеликое множество, но как оказалось они все требуют другую библиотечку libxml2, и вот тут то у меня и возникла проблема — эта библиотечка никак не хотела ставиться… На StackOverflow нашел объяснение, что мол у меня стоит компилятор от VisualStudio 2010, а надо VisualStudio 2008 и тогда все получится. Ставить эту махину ну никак не хотелось и ещё небольшим гуглением наткнулся на универсальную штучку для windows - win32com.client. На сколько понял, эта штучку средствами Microsoft Office спокойно открывает любые файлы. Осталось только научиться этим инструментом пользоваться. Для этого конвертируем doc файлы в некоторой директории в TXT формат:

# -*- coding: utf-8 -*-
import win32com.client
import codecs
import re
import os
import pymysql

if False:
    #start app
    app = win32com.client.Dispatch('Word.Application')

    myDir = 'C:\okpd_classes'
    for subdir, dirs, files in os.walk(myDir):
        for file in files:
            file_path = subdir + os.path.sep + file
            if (file[-4:] != '.doc'):
                continue
            doc = app.Documents.Open(myDir + '/' + file)

            file = open('out_test/'+file+'.txt', 'w+')
            ttt = doc.Content.Text
            file.write(ttt.encode('utf-8'))
            file.close()

    #close app
    app.Quit()
    exit()

Казалось бы все отлично, дальше уже работаем с txt файлами. Но это же винда! И у меня возникли проблемы с кодировкой при чтении сгенерированных txt файлов. Поэтому пришлось импортировать codesc и считывать построчно так:

txtDir = 'out_test'
for subdir, dirs, files in os.walk(txtDir):
    for file_name in files:
        file_path = subdir + os.path.sep + file_name
        lines = [line.strip() for line in codecs.open(file_path, 'r', encoding='utf-8')]

Собственно все это делалось для того, чтобы распарсить коды ОКПД, которые удалось найти только в вордовском формате. Полный листинг привожу ниже.

# -*- coding: utf-8 -*-
import win32com.client
import codecs
import re
import os
import pymysql

if False:
    #start app
    app = win32com.client.Dispatch('Word.Application')

    myDir = 'C:\MyPrograms\python\crm-sib-ru\okpd_classes'
    for subdir, dirs, files in os.walk(myDir):
        for file in files:
            file_path = subdir + os.path.sep + file
            if (file[-4:] != '.doc'):
                continue
            doc = app.Documents.Open(myDir + '/' + file)

            file = open('out_test/'+file+'.txt', 'w+')
            ttt = doc.Content.Text
            file.write(ttt.encode('utf-8'))
            file.close()

    #close app
    app.Quit()
    exit()

#connecting to DB
conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='', db='crm-sib-ru')
cur = conn.cursor()
cur.execute("SET NAMES utf8")
txtDir = 'out_test'
for subdir, dirs, files in os.walk(txtDir):
    for file_name in files:
        file_path = subdir + os.path.sep + file_name

        #search tpl
        lines = [line.strip() for line in codecs.open(file_path, 'r', encoding='utf-8')]
        parsed_codes = []
        parsed_names = []
        print( len(lines))
        add_line = False


        for k, el in enumerate(lines):
            codes = re.findall("[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}\.[0-9]{,3}", el)
            if len(codes) > 0:
                parsed_codes.append(codes[0])
                add_line = True
            else:
                if add_line == True:
                    parsed_names.append( el.replace(u"\x07", "") )
                add_line = False

        for k, el in enumerate(parsed_names):
            cur = conn.cursor()
            cur.execute("INSERT INTO `okpd` (`id`, `file`, `num`, `desc`, `note`) VALUES (NULL, '"+file_name.encode('utf-8')+"', '"+parsed_codes[k].encode('utf-8')+"', '"+parsed_names[k].encode('utf-8')+"', '')")
            cur.close()

conn.close()

Здесь мы используем регулярные выражения для нахождения строк с кодами и pymysql для того, чтобы сохранить все в удобном SQL формате. Кстати коды ОКПД скачать в формате MySQL можно здесь okpd-full.sql

Детектирование креста на изображении с помощью 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, поэтому не следует воспринимать её всерьез, однако, надеюсь что новым участникам беспилотных аппаратов КРОК этот скрипт поможет быстрее стартовать и мягко приземлить свою машину.