Зйомка показів лічильника на телефоні з наступним визнанням



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

Можливо, не всі котли поводяться таким чином, але наш котел відключається навіть з короткостроковою перервою в газопостачанні і не повертається назад, якщо поставка відновлена. Якщо хтось в домашніх умовах, то це не проблема, натискаючи кнопку і котлоте далі прогрівається, але якщо раптом це сталося, що ми вирішили піти на відпочинок з цілою сім'єю, а на дворі це зима, така як -20 ° С, то наслідки можна розгорнути.

Розчин простий – залиште ключі до родичів/френдів/сусідів, щоб вони могли приїхати і включити котел, якщо виникають проблеми. Доброго дня, якщо все буде добре. І якщо ні? Або він збирається виїхати на вихідні?

Так, я вирішив встановити метрові читання десь в Інтернеті, так що я можу бути десь на тривалій поїздці, періодично перевірте, чи витрачається газ, і якщо він зупиняється, то терміново викликав родичів / друзів / сусідів (або які я залишила ключі), щоб прийти і натиснути кнопку.

Звичайно, після простого розміщення доказів в Інтернеті я вирішив не зупинитися і плутати визнання показань і графічного представлення, читайте про це в частині 2 цієї теми.

Частина 1. Тут необхідно зробити замовлення, щоб лічильники повністю відрізняються натурою, деякі з них мають спеціальні шини та інтерфейси для автоматизованих читання. Якщо у вас є один, то ви, ймовірно, не можете читати далі. Але у мене є найбільш поширена модель без таких інтерфейсів (принаймні я не знайшов, можливо, я не шукав її добре), GALLUS iV PSC. Тому існує лише один варіант – візуальний запис. Мережа пропонує готові рішення, але вони коштують багато грошей, а головне – це не спортивно, тому ми зробимо все самостійно.

Що потрібно? Щоб взяти читання з лічильника, а потім відправити ці читання в Інтернет, нам буде потрібно будь-який непотрібний смартфон Android. Для цього використовується Samsung Galaxy S III (SCH-I535). Так, напевно, не кожен читач має трійку галактик, що лежить навколо, але потрібно розуміти, що вимоги до смартфону не так великі:
  • вона повинна бути завантажена
  • камери повинні працювати
  • Для роботи з інтернетом
Це все, наявність робочого екрана, сенсорного екрана, мікрофона, динаміка тощо абсолютно не потрібно. Цей факт значно знижує вартість.

У мене є хобі, щоб купити різні розбиті телефони на eBay і збирати їх роботи, я легко знайшов в материнській платі зі зламаним мікрофоном (~$ 10), а також використовується камера (~$ 10) і китайська батарея (~300p). Також для зручності кріплення акумулятора дошки використовується каркас з розбитим дисплеєм.


Спочатку я думав, що це тільки з материнською платою і камерою, але виявилося, що навіть при підключенні до зарядки, дошка не виходить без акумулятора, тому мені довелося додати кадр і акумулятор. Але навіть в цьому випадку бюджет виявився близько $30, якщо ви використовуєте пристрої простіше, ніж sgs3, ви можете зустріти меншу суму.

Однак таке рішення має свої недоліки, смартфон без дисплея і сенсорний екран не так зручно налаштовувати, тому про те, як мені довелося вирішити цю проблему.

Ми проведемо з найгіршого сценарію справи. Припустимо, що немає дисплея або сенсорного екрана, немає кореня на смартфоні, відключення реклами відключається, прошивка невідомо.

РеанімаціяУВАГА! Для Samsung Galaxy S III (SCH-I535) підходять наступні інструкції:

Припустимо, що ви добре знайомі з такими поняттями, як реклама, прошивка тощо.

Щоб принести смартфон до більш-менш відомого стану для запуску прошивки віртуального БМб1 звідси, використовуючи Odin. Я не описую, як це робиться докладно, інтернет повний інструкції про те, як використовувати Odin. Odin в нашому випадку добре, тому що це легко працювати з ним без використання смартфона екрана, вам просто потрібно включити смартфон в режим завантаження (Vol Down + Home + Power - утримуйте кілька секунд, потім Vol Up, підключіть USB до Windows і все, потім Odin-a case).

Після того, як Odin стібається злив, телефон завантажить систему, від'єднайте її від usb і зніміть акумулятор, щоб вимкнути його. Ця операція повинна бути виконана з кожного разу після прошивки Odin, щоб розпочати наступну операцію з off- стану.

Після чого пришиваємо CWM відновлення і корінь відповідно до інструкцій. Скоро, це:
  • Через Odin флеш користувальницький bootchain VRALEC.bootchain.tar.md5
  • КВМ Відновлення через Odin
  • КВМ Завантажити SuperSU_Bootloader_FIXED.zip В інструкції зазначено, що zip слід кинути на sd-карту, але через відсутність екрана простіше робити це за допомогою бокового завантаження:
    Увімкніть тіло за допомогою затискання Vol Up + Home + Power - утримуйте кілька секунд, потім ще 5 секунд навантаження, встановіть в режим CWM-відновлення.
    Перевірити це, набравши в консолі в рекламних пристроях ubuntu (особа, звичайно, повинна бути підключена через USB і необхідно встановити adb - sudo apt-get встановити андроїд-tools-adb):
    sexfic@lepeshka:~$ рекламні пристрої Перелік пристроїв, прикріплених 64cb5c59 відновлення Якщо ми бачимо останній рядок, то все дрібно, натискаємо на пристрій Vol Down, Vol Down, Power - перейдіть до адресного навантаження (принаймні в версії CWM з інструкцій - друга лінія з вершини), залишається тільки набрати в консолі ubuntu:
    sexfic@lepeshka:~$ Пристрої бічного завантаження SuperSU_Bootloader_FIXED.zip посилка: 'завантаження' 100% і кореневих пластівців до пристрою, після чого не забудьте вимкнути пристрій, знімаючи акумулятор з нього.
  • Через Odin ми спалахуємо панчохіну, що відповідає раніше поставленим запасам прошивки VRBMB1_Bootchain.tar.md5

Далі ми повинні увімкнути USB-розгортання на смартфоні, для цього ми запустили смартфон в режимі CWM-відновлення, перевірте:
sexfic@lepeshka:~$ рекламні пристрої Перелік пристроїв, прикріплених 64cb5c59 відновлення Система обміну:
sexfic@lepeshka:~$ href="http://realtor.if.ua/" title="Агентство нерухомості Ріелтор" target="_blank"> Додати рядок до /system/build.prop:
sexfic@lepeshka:~$ оболонка оголошень "echo \"persist.service.adb.enable=1\" >> /system/build.prop" Завантажити:
sexfic@lepeshka:~$ перезавантажити оголошення Завантажити, перевірити статус рекламного оголошення в терміналі:
sexfic@lepeshka:~$ рекламні пристрої Перелік пристроїв, прикріплених 64cb5c59 пристрій Bingo! Вимкнено комутацію, побачимо, що відбувається на смартфоні, для цього ми запускаємо AndroidScreenCast за допомогою Java Web Start і див.:


Це екран активації SIM-карти Verizon, я не маю цього SIM, так що я просто пропускає активацію за наступними інструкціями:
на екрані вибору мови, послідовно доторкнувшись нижнього лівого кута (вище кнопка виклику надзвичайних ситуацій), нижнього правого кута, нижнього лівого, нижнього правого і об'єму +
sexfic@lepeshka:~$ adb оболонки вхідний кран 10 1150 malefic@lepeshka: ~ $ adb оболонки вхідний кран 710 1150 malefic@lepeshka: ~ $ adb оболонки вхідний кран 10 1150 malefic@lepeshka: ~ $ adb оболонки вхідний кран 710 1150 потім натисніть кнопку Vol Up на моєму смартфоні, тепер див.:


Перевірити коробку і натиснути OK:
sexfic@lepeshka:~$ adb оболонки вхідний кран 50 600 malefic@lepeshka: ~ $ JavaScript licenses API Веб-сайт Go1.13.8

Передайте, щоб розблокувати екран:
sexfic@lepeshka:~$ адб шкаралупа входу swipe 100 100 500 100

Тепер вам потрібно встановити сервер vnc для Android, наприклад, Android VNC Server. Встановіть його на смартфон:
sexfic@lepeshka: ~ $ adb встановити droid+VNC+server+v1.1RC0.apk 4055 KB/s (2084419 bytes in 0.501s) pkg: /data/local/tmp/droid+VNC+server+v1.1RC0.apk Успіх Ми, ймовірно, заглиблюємо, коли ми встановили сервер vnc і запускаємо, щоб розблокувати екран:
sexfic@lepeshka:~$ adb оболонки вхідних ключів 26 malefic@lepeshka: ~ $ введення оболонок мітки swipe 100 100 500 100 Запуск сервера vnc:
sexfic@lepeshka:~$ adb оболонки am start -a Android.intent.action. Головна -n org.onaips.vnc/.MainActivity

Натисніть OK:
sexfic@lepeshka:~$ JavaScript licenses API Веб-сайт Go1.13.8

Прес-центр:
sexfic@lepeshka:~$ JavaScript licenses API Веб-сайт Go1.13.8

Прес-реліз:
sexfic@lepeshka:~$ вхідний кран оболонки 600 1000

Всі права, тепер ми ведемо порти через рекламні оголошення:
sexfic@lepeshka:~$ adb вперед tcp:5801 tcp:5801 malefic@lepeshka: ~ $ adb вперед tcp:5901 tcp:5901 і доступ до смартфона через ваш браузер або улюблений vnc клієнт.


Далі ми працюємо як з постійним телефоном Android, тільки через комп'ютер, він зручний для негайного налаштування підключення до Інтернету, після чого ви можете отримати доступ до Інтернету через Wi-Fi, і не зберігати телефон, пов'язаний з комп'ютером (за допомогою лічильника газу не завжди знаходиться в безпосередній близькості від комп'ютера).

Тепер, щоб взаємодія з пристроєм була повністю встановлена, ви можете переключатися на налаштування фото та видавничих даних в Інтернеті.

Ми встановлюємо програму Завдання, створюємо тимчасовий профіль в ньому з 00:00 до 23:59 кожні 30 хвилин, щоб виконати дію - візьміть фото. Вибираємо параметри зйомки найбільш підходящі для розташування телефону і лічильника. У мене є макростріл з обов'язковою флешкою.

Це як я поставив свій телефон (перегляд):


Картонна коробка прив'язана до лічильника з мотузкою, смартфон живе в ній, пакет яєць для фіксації смартфона в вертикальному положенні. Потім я рафінував дизайн з стрічкою і картоном, щоб спалах не потрапив до діалі безпосередньо, що дає серйозне скло, яке заважає розпізнанню. Зверху закрийте все кришкою, щоб він був темним всередині, інакше, з яскравим зовнішнім освітленням, автофокус не завжди працює правильно.

У налаштуваннях смартфона в фондах розробника необхідно перевірити коробку так, щоб смартфон не закохався при зарядці, а потім в певній точці він перестає зніматися і продовжується тільки при порушенні.
, Україна

Щоб перемістити захоплені зображення лічильника в Інтернет, я використовував перший додаток, який я прийшов через - FolderSync Lite. Це може синхронізувати папку на смартфоні з папкою, наприклад, на диску Google.

Так я можу тепер піти в мій Google Диск з будь-якої точки світу, де є інтернет і перевірити, що газовий котел працює нормально.

Частина 2. Так, після відправки метрових читання в Інтернет, я зацікавила можливість автоматичного розпізнавання читання. Це дозволить:
Статистичний аналіз споживання газу автоматично відстежуйте викиди газу (з можливістю попередження по електронній пошті або см)
python був обраний як мову розробки, а бібліотека OpenCV була використана для роботи з зображеннями.

Ось код головної програми, яка працює на коронці раз на годину:
імпорт сису з імпортом моделей Зображення, sess з gdrive import отримати Зображення ВідGDrive, створення Відео ВідGDrive Об'єкт if __name____main_____': #receive список нових фотографій з зображень Google диска, http:// GetImagesЗ альбомуGDrive() # по черзі обробляти їх в циклі для img_info: #load img=ImageGUmage(Image)=ImageGImage(Image(Image(Image(Image)=create_info) в базі даних, #contextextextextextimbage, i.com, i.
Увійти ImagesЗ альбомуGDrive – функція, яка повертає список невизнаних зображень з Google Drive Створення ImageЗ альбомуGDrive Об'єкт - функція, яка завантажує зображення і перетворює його в формат OpenCV Увійти Зображення – функція виглядає для запису зображень в базі даних, якщо немає одного, то створює її. визначення Digits - метод, який розпізнає читання в цьому образі HTTP - авторизований клієнт для доступу до Google Диску, докладніше про доступ до API Drive sess - об'єкт з'єднання бази даних, SQL РусскийУкраїнськаБеларускаяOʻzbek tiliEnglish Перше, що ми робимо - це список зображень з Google Drive:
Імпорт os з дати імпорту tzinfo, timedelta, дата з датиutil. відноснадельта імпорт відноснадельта з апікіента. Збір імпортних моделей з імпорту getLastRecognized Image def getImagesЗ альбомуGDrive(): #id rubbish folder Google Drive, який містить зображення FOLDER_ID = '0B5mI3ROgk0mJKTm95Ri1mbVU' створює уповноважений об'єкт клієнта #LastRecognizedImage def getImage defImagesЗ альбомуGImagesЗ альбомуGU=f getImagesfromGUpate="(s) = #fffffffffffffffffffffffff="Dirmsimage"(films) = #Dirm=#ffilm/Dirm/Dirm/Dirm/Dirm/Dirm/Dirm/Dirt/Dirm= .list(q= q= q= q= q= q=r=r=file=file=file=file=file=file_file=file_file_file_file_file_file_file_file_file=1dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk0dk pt=image=notres результат, як диск створюється для наступного файлу (tosequest(to) файл(tosearch=reform)#fuse(toseparate(to)1000) для того, щоб файл(torepat(toseparate(toseparate(toseparate(toseparate(to)intoseparate(to)into(to)into)intotherepat(toseparate(to)1000)
імпорт httplib2 ConfigParser від oauth2client.client import OAuth2WebServer З oauth2client.file import Зберігання def getAuthorizedHttp(): #retrieve from config. Ini файл, який CLIENT_ID і CLIENT_SECRET config=ConfigParser.ConfigParser() config.read([f.path.dir willAuthorizedHtttp())) #CLIGET_CLig_CLIENT_CLI_CLig_Client_CLig_CLIED_CLIED_CLI_CLI_CLI_CLI_CLI_CLI_CLI_CLI_CLIG_CLIG_CLIED_CLIED_CLIED_CLIC_CLIC_C_CLIC_CLIC_CLICLIC_C_C_CLICLIC_CLICLICLIC_CLIC_C_CLIC_C_CLICLICLICLICLI_CLI_CLICLIC_C_CLI_C_CLI_CLICLICLI_CLI_CLI_CLI_CLICLICLICLICLI_CLI_C_CLI # Перевірка https://developers.google.com/drive/scopes для всіх доступних сфер. OAUTH_SCOPE = "https://www.googleapis.com/auth/drive" # Redirect URI для встановлених додатків REDIRECT_URI = 'urn:ietf:wg:oauth:2.0:oob' # клієнт_secrets.json зберігання = Зберігання (os.path.dircode) потік для встановлених додатків REDIRECT_URI = "urn: print.put.#f:wg:o:o:oauth:2, потім вам потрібно створити посилання для отримання #CLIT #CLIT_CLIT_CLIT_CLIT_Creattext2, від початкового коду #CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT_CLIT API та Auth до Регулятори до ОАута Преса КРЕАТИ НОВИЙ КЛІЄНТОбрати Встановлення додатка до Інше:


На першому старті скрипт буде писати на консоль виворіт, на якому потрібно перейти, щоб отримати токен, вставити його в адресний ряд браузера, дозволити програму доступу до Google Диску, копіювати код перевірки, виданий Google з браузера і дати скрипт. Після цього скрипт заощадить все, що потрібно для клієнта json файл і нічого не буде запитати в наступних запускух.

Функція завантаження зображень дуже проста:
Імпорт cv2 імпортний нуп, як np def завантажити ImageЗ альбомуGDrive (завантажити Url, http://None): якщо http://None: http=getAuthorizedHttp() # Завантажити переспів зображень, вміст=http.request(download Урл) #Drive Створити OpenCV img_array = np.asarray(bytearray(content), dtype=np.u8) повернення(Imfo) від GImblock(Imblock)
Перше, що нам потрібно зробити після того, як ми отримуємо фото, щоб знайти цифри на ній, які ми можемо розпізнати. Це робиться екстрактDigits Відповіді:
екстракт деф Опитування ВідImage (self): img = само.img
Фото оригінально виглядає так:
р.

Отже, перш за все, ми перетворюємо його навколо так, щоб він отримав праву спрямованість.
# обертати 90 градусів h, w, k = img.shape M = cv2.getRotationMatrix2D(w/2,h/2),270,1) Гим = cv2.warpAffine(img,M,(w,h)


# Обрізати чорні поля, які з'явилися після обертання img = img[0:h, (w-h)/2:h+(w-h)/2] h, w, k = img.shape
Тепер давайте подивимося на шматок зображення, обкладеного червоним каркасом. Дуже унікальна вся фото, ви можете використовувати її для пошуку диаль. Я поставив її в зразок. jpg і писав наступний код, щоб знайти його координати:
# завантажити потрібний шматок фото з файлу зразка = cv2.imread(os.path.dirname(__file____)+"/sample.jpg") зразок_h, зразок_w, зразок_k* зразок*pe # зразок. shape # дивитися для найкращого матчу з фото res = cv2.matchTemplate(img,sample,cv2.TM_CCORR_NORMED) min_val, max_loc, max_v2, якщо зразок_cent = immig*w = imm=0 хххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххх=хххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххххх


На малюнку зображено координати, які ми бажали. Далі ми запускаємо алгоритм пошуку кордонів, перед перекладом зображення в сірі тони. 100 і 200 є емпірично вибраними пороговими значеннями.
# перевести зображення в сірих градаціях сірий = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) # погляд на алгоритм меж Канни краї = cv2.Canny(сірий, 100, 200)


Тепер ми запускаємо алгоритм пошуку ліній в отриманому зображенні з кордонами. Крім самого зображення, метод HoughLines також бере в якості параметрів розмір кроків пошуку на відстані і кута обертання і порогу, відповідального за мінімальну кількість точок, які повинні формувати рядок. Знизити поріг, з'явиться більше рядків алгоритму.


З усіх знайдених ліній ми розглянемо тільки більш-менш горизонтальні і знаходимо два найближчих до обраного центру (одна зверху, інші знизу).

Обертайте зображення таким чином, щоб лінії були повністю горизонтальними:
# обертати M = cv2.getRotationMatrix2D(0,(line_below["rho"]-line_above["rho"])/2+line_above["rho"],line_above["theta"]/np.pi*180-90,1) Гим = cv2.pAffine(img,M,(w,h) Зрізаємо все, що за знайденими лініями:
# cropped img=img[line_above["rho"]:line_below["rho"], 0:w] h, w, k = img.shapeДалі потрібно знайти ліву і праву кромку набрати, перекласти зображення в чорно-білу:
#binarize зображення сірого = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) тягне = cv2.adaptiveThreshold(сірий,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2. THRESH_BINARY, 31, 2)
Ми шукаємо правильний край за допомогою тієї ж технології, як «центральна» точка, візерунок обмотується червоним каркасом:
JavaScript licenses API Веб-сайт Go1.13.8
# Видалення ядер шуму = np.ones(((7,7),np.uint8) трипс = cv2.морфологія Ex(три, cv2.MORPH_CLOSE, ядро)
Далі ми проходимо всі пікселики, починаючи з лівого, поки не зустрічаємо чорний, це буде лівий край:
# дивитися на лівий край x_left=0 в той час як x_left
Зрізаємо зображення на лівих і правих краях:
# лівий та правий img = img[:, x_left:x_right] h, w, k = img.shape Ми зробимо невеликий контроль, який знайдений зображення відповідає співвідношенню обличчя:
# перевірити, якщо float(w)/float(h)<6.5 або float(w)/float(h)>9.5: mylogger.warn("Image має поганий співвідношення: %f"% (float(w)/float(h))))) повернути False само.digits_img =g повернути true
Розмежування діал, виділеної попередньою функцією на окремі номери здійснюється методом splitDigits:
def splitDigits (self): # перевірити, якщо None ==self.digits_img: if not self.extractDigitsЗ альбомуImage(): повернення False img = само.digits_img h, w, k = img.shape По-перше, просто виріжте наш щоденник на 8 рівних частин:

Ми обробляємо тільки перші 7 частин, так як 8-а цифра постійно обертається, вона без зайвих зусиль розпізнати її.
Кожна частина перекладається в колір h/b за допомогою адаптивного Потрібний спосіб, параметри вибирають емпірично:
# розбити диал на 8 рівних частин і обробляти кожну частину для i в діапазоні(1,8): digit = img[0:h, (i-1)*w/8:i*w/8] dh, dw, dk = digit. JavaScript licenses API Веб-сайт Go1.13.8
Легко знімайте шум, перетворюючи отвір (зробіть ядро всього 2х2). Ви можете зробити без цього, але іноді це допомагає відрізати великі білі шматки, пов'язані тонкими lintels:
# видалити ядро шуму = np.ones((2,2),np.uint8) digit_bin = cv2.morphologyEx(digit_bin, cv2.MORPH_OPEN, ядро)
Запуск алгоритму пошуку контурів
# шукає контури інші, контури, ієрархія = cv2.findContours(digit_bin.copy() ,cv2. RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
Далі кидаємо всі занадто невеликі контури і контури по краях зображення, потім знайдіть найбільші контури залишилися:
i="python"> # аналіз контурів big_contour=None big_contour_area = 0 для cnt в контурах: M = cv2.moments(cnt) # пропустити контури з занадто невеликою зоною, якщо cv2.contourArea(cnt)<30: продовжити #скопувати контури з занадто малим периметром, якщо cv2.arcLength(cnt,True)<30: продовжити #four cx = M[m10's][cmoduct] в найбільший_contourc=text=Mext=00], якщо ви знайдете #ctourct=compairmex=com=com=com #
Найбільший контур - це наш номер, викинути все, що лежить зовні його маскою:
# видалити все, що лежить поза найбільшим контуром маски = np.zeros(digit_bin.shape,np.uint8) cv2.drawContours(mask,[biggest_contour],0,255,-1) digit_bin = cv2.bitwise_and(digit_bin,digit_bin,mask = маска)
Тепер описуйте кожну цифру прямокутника стандартного розміру з центром в центрі маси схеми:
# вказати параметри опису прямокутника rw = dw/2.0 rh = dh/1.4 # перевірити, що прямокутник не виходить за межі зображення, якщо найбільший_contour_cy-rh/2 < 0: найбільший_contour_cy = rh/2, якщо найбільший_contour_cx-rw/2 < 0: найбільший_contour_cx-rw/2 < 0: найбільший_contour_cx= rw/2
Вирізати зображення в прямокутник і масштабі до заданого розміру, у мене є digit_base_h = 24, digit_base_w = 16. Ми зберігаємо результат до бази даних.
# вирізати прямокутник digit_bin = digit_bin[int(biggest_contour_cy-rh/2):int(biggest_contour_cy+rh/2), int(biggest_contour_cx-rw/2):int(biggest_contour_cx+rw/2) #resize до стандартних digit_bin = cv2.resize(digit_bin,(digit_base_binh, digit_biggest_digit_base_binh)))))) THig2, повернення(v4, TH2, THIGDig2, NA, cv2.
Ось метод ідентифікаціїDigits, який називається від основної програми для кожного зображення:
def ідентифікуватиDigits(self): # якщо число вже визнано, то ми не робимо нічого, якщо самостійно. результат! Увійти Правда # якщо len(self.digits)=0: # якщо зображення не зазначено, то нічого не буває, якщо.img == Ні: повернення False # якщо не само.splitDigit(): повернення False # підтвердити зміни до бази, які зроблені, коли sess.commit() намагаються розпізнати кожен цифровий #digits повертає(digits) якщо ви не отримуєте номер #digits повернення(digits) від цифрових цифр, то ви також можете отримати один цифри в цифрах. Все тривіальне, крім ідентифікації Метод діджитал:
Визначення деф Опитування (сама): # якщо фігура вже визнана, то ми робимо нічого, якщо самостійно. результат: Увійти Правда, якщо не KNN.recognize(self): # якщо ви не можете розпізнати фігуру, то позначте його на ручній обробці. self-identifyForManualRecognize() # if it is the 7th digit, ми розглянемо його, щоб бути рівні "0", оскільки це остання цифра і не критична, і це часто трапляється, що вона не визнається, якщо self.i=7:.result K = повернення, інший алгоритм використовується в зворотному напрямку пошуку.
@staticmethod def розпізнати(dbdigit): #train if not KNN._trained: KNN.train() #check розмір зображення, якщо не виправити, то спробувати розпізнати h,w = dbdigit.body. JavaScript licenses API Веб-сайт Go1.13.8
@staticmethod def getKN(): # метод забезпечує єдиний ініціалізація об'єкта cv2. KNearest if KNN._knn=None: KNN._knn = cv2.KNearest() повертає KNN._knn @staticmethod def поїзд(): knn = KN.getKNNN() #Визначені номери для навчання_digits = ses.query(Digit) зразок = cv2.complextextext.complext.complext.complext.complext.complext.complext.complext.complext.complext.complext.complext.complext.complext.com
Я даю глухим з моделі. py файл, якщо читач все ще має питання про роботу деяких використовуваних, але не описаних функцій.
Абсент у статті опис функцій та методівColcoding class="python"> Імпортний час від sqlalchemy імпортної колони, Integer, String, Text, Boolean, ForeignKey, DateTime, Pickle Тип від sqlalchemy.orm від sql=self-image.com=self-image від sql.com=self-image iformation from sqlchemy.com, Foreigncoductum(sic)(Im, import_nex.comp.community) = digitalcodum(sim(Sig.commnage_informed.com/simage_informed.com/simage_informed.com/simage_informed.comm.com/simage_infig.com/s_infig.com/s_inf.com/simageinf

Щоб проаналізувати читання та ручне розпізнавання, я також написав невеликий веб-інтерфейс на Flask. Я не даю код тут, хто цікавиться, він може побачити його, а також всі інші коди на Github.

Інтерфейс має лише дві сторінки, один для перегляду читання у вигляді графіка, наприклад, дня або тижня:

Друга сторінка для ручного розпізнавання цифр. Після того, як я кладаю руки в перші 20-30 читання, робот почав розпізнати читання досить правильно. Зрозуміло, як і раніше виникають винятки, і не можна розпізнати фігуру, це найчастіше через обертання діала:

Потім потрібно ввести відсутні номери своїми руками:
Або ви можете просто ігнорувати такі показання, вони будуть пропущені на графіку, і нічого поганого буде.

У разі збігу декількох останніх читань планується заключити скрипт для надсилання електронної пошти.

Це все, що я хотів сказати, спасибі за читання через кінець.

Джерело: habrahabr.ru/post/220869/