Введение
Если мне нужно написать о части, то это 4 -я часть или 4 -я версия Жестная система визуальной письменной системы Анкет Если вы прочитали мои предыдущие блоги по той же теме, то вы, вероятно, знаете, сколько у меня проги
Введение
Если мне нужно написать о части, то это 5 -я часть или 4 -я версия Жестная система визуальной письменной системы Анкет Если вы прочитали мои предыдущие блоги по той же теме, то вы, вероятно, знаете, сколько я перешел из ранней версии. Как только мой работодатель предложил мне на собеседовании, вы должны знать, как сделать ваш код пригодным для использования не кодировщиком/публикой, тогда ваш навык может быть полезным. Я применяю ту же концепцию. Он имел в виду, что я должен научиться развернуть мою систему в не кодировщик. Я не профессионал здесь, но я пишу этот код развертывания во время обучения, поэтому я ожидаю отзывов, предложений от читателей. На 4 -й версии системы я написал код, который был более организован концепцией ООП. Теперь я хочу, чтобы эта система была развернута. Все начинается с того, что, если и если вы пытаетесь работать над тем, что если, вы всегда будете изучать новые вещи. В этой версии я написал немного кода Flask для развертывания этого проекта в веб -приложении. Я постараюсь сделать это просто здесь как можно больше. Для лучшего понимания этого проекта требует Основная предпосылка для понимания концепций и алгоритмов, используемых в этом блоге, – это просмотреть мои предыдущие блоги . Потому что я мало объясняю на этот раз. Следуйте Прежде чем что -либо Раздел для получения дополнительной информации.
Что теперь?
- Я напишу код, используя колбу для развертывания предыдущей системы в веб -приложении.
Простой рабочее время будет чем -то вроде ниже.
Я напишу класс, который будет общаться с нашей существующей системой, а затем каждый раз возвращает рамки, холст и обнаруживает текст на маршруты.
Прежде чем что -либо
Прежде всего, я прошу вас просмотреть мои предыдущие блоги и только затем вернуться в этот блог. Потому что блоги ниже являются более ранней версией кодов и концепций. Я все еще использую те же концепции, а также вы будете поражены, увидев, сколько я прогрессировал. Я собираюсь описать только то, что здесь происходит на очень мало. Следовательно, если вы новичок в этом проекте, то лучше всего их просмотреть. Пожалуйста, просмотрите их последовательно.
- Система визуальной записи на основе жестов с использованием OpenCV и Python
- Жестная система на основе визуального письма: Добавление визуального пользовательского интерфейса
- Система визуальной записи на основе жестов: добавление виртуальной анимации, новой режима и нового VUI
- Жестная система на основе визуального письма: Добавить слайдер, больше цветов и оптимизированный код ООП
Почему?
Если вы здесь, то вам либо сыт по горло, либо любопытен с моими сообщениями в LinkedIn или Twitter о системе написания на основе жестов. Но как бы вы ни были здесь, я здесь из -за поддержки, которую мне дали люди. Многие люди предлагают мне поделиться этими блогами на Medium, Reditt, чтобы получить больше отзывов и поддержки. Но у меня есть очень ограниченное количество сотовых данных для использования, поэтому я публикую их на своем сайте, а затем делятся на LinkedIn и Twitter.
Кредиты
Я хочу отдать должное этим блогам всем на LinkedIn Кто отреагировал, поделился и прокомментировал мой предыдущий блог и на Twitter Также (большинство ретвитов были от ботов LOL). Я очень благодарен, что мой Этот пост LinkedIn о предыдущей версии Получил более 4K -реакции и почти 60 тыс. Просмотров. Я никогда не думал, что это привлечет столько внимания, и здесь я снова его улучшаю. Также мой друг Dip продолжает спрашивать меня о моей следующей работе, потому что он всегда поддерживает меня.
Мотивация
Я очень мотивирован поддержкой, которую люди дали мне LinkedIn .
Цитировать
А почему мы падаем, Брюс?
Чтобы мы могли научиться подбирать себя. – Томас Уэйн
Письменность контура: модификации
Для запуска этой системы мне пришлось немного изменить эту систему. Только необходимая модификация была в классе Контурное написание
И я включаю это ниже.
Инициализация
########################## ########################## self.final_window = cv2.imread("static/taking_avg.png") self.detected_text = "Nothing"
final_window
: Изображение, которое сообщит пользователю, что среднее значение принимается.DETED_TEXT
: Что было обнаружено путем передачи детектора? Последнее окно выглядит ниже:
Метод: __del__
Есть конструктор по умолчанию, созданный вместе с созданием объектов на Python и __del__
это деструктор, призванный, когда все ссылки объекта удаляются.
def __del__(self): self.cam.release() cv2.destroyAllWindows()
Когда объект нашего контурного написания удален, мы хотим закрыть нашу камеру и уничтожить все открытые окна OpenCV (если есть доступны).
Метод: детектор
def detector(self): img = self.canvas.canvas.astype(np.uint8) op = pytesseract.image_to_string(img, lang="eng", nice="1") self.detected_text = op
Здесь ничего странного не произошло, только обнаруженный текст назначается атрибуту.
Метод: Main
Я изменил этот метод только по некоторым линиям. Вы также можете увидеть коды здесь. Чтобы заставить нашу систему работать без каких -либо возможных ошибок, я попытался обернуть коды внутри Try/кроме блока. Основное внимание должно быть уделено на Try/кроме блока в нижней части ниже кода.
def main(self): try: while True: (ret, frame) = self.cam.read() if ret: self.key = cv2.waitKey(1) & 0xFF frame = imutils.resize(frame, width=self.size[1]) frame = cv2.flip(frame, 1) clone = frame.copy() gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY) self.set_grays(gray) self.size = frame.shape # if to take average and num frames on average taking is lesser than if self.num_framesself.roi_counts["vroi"] and fmode is not None: self.running_mode = fmode else: self.running_mode = self.vui.running_mode else: self.running_mode = fmode self.perform_mode() self.vui.running_mode=self.running_mode canvas = self.canvas.update_window(mode=self.running_mode, pointer=self.roi_pointer["droi"]).astype(np.uint8) self.final_window = self.get_window(canvas=canvas, vui=vui) self.roi_pointer["vroi"] = (-1, -1) clone = self.make_rectangles(clone) self.clone = clone if self.key==27: self.cam.release() cv2.destroyAllWindows() try: ret, clone_jpeg = cv2.imencode('.jpeg', self.clone) ret1, draw_jpeg = cv2.imencode('.jpeg', self.final_window) if ret: self.clone = clone_jpeg.tobytes() self.vui_frame=draw_jpeg.tobytes() return clone_jpeg.tobytes(), draw_jpeg.tobytes(), self.detected_text except: pass except: try: ret, clone_jpeg = cv2.imencode('.jpeg', self.clone) ret1, draw_jpeg = cv2.imencode('.jpeg', self.final_window) if ret: self.clone = clone_jpeg.tobytes() self.vui_frame=draw_jpeg.tobytes() return clone_jpeg.tobytes(), draw_jpeg.tobytes(), self.detected_text except: pass
- Возьмите изображение клона, где мы нарисовали прямоугольники, контуры, указатель и тексты. Кодируйте это JPEG.
- Возьмите окончательное окно Vui/Canvas и кодируйте его JPEG.
- Преобразуйте оба кодированных изображения в байты.
- Верните оба байтовых изображения и обнаруженный текст.
Первоначально наш Final_window будет настроен на наше изображение, говорящее на среднем. И когда средние значения были взяты, мы можем отправить его на наши маршруты колбы. Я также установил некоторые атрибуты тоже. Все коды внутри main.py
приведен ниже.
Коды: main.py
Пожалуйста, попробуйте игнорировать комментарии и длину кода.
import numpy as np import os import matplotlib.pyplot as plt import cv2 import imutils import pytesseract import time class VUI: """ A class for visual user interface. Recommended to use default parameters. """ def __init__(self, icons_dir="icons/", window_size=(525, 700, 3), vui_part=20, max_count=15): """ icons_dir: directory to use icons from. window_size: size of an vui window. Recommended to use equal of final window. vui_part: How much % of rows from top will be used to stack icons? """ self.idir = icons_dir self.window = np.zeros(window_size).astype(np.uint8) self.size = window_size self.vui_part = int(vui_part/100 * self.size[0]) self.dd_part = (self.vui_part, int(50/100 * self.size[0])) self.modes = [fname.split(".")[0] for fname in os.listdir(self.idir)] self.icon_size = (int(self.size[1]/len(self.modes)), self.vui_part) # c, r self.current_icons = [] self.anim_scale = 0.5 self.anim_color = [5, 15, 2] self.prev_mode = "move" self.current_mode = "move" self.running_mode = None self.hover=None self.mode_count = 1 self.max_count = max_count self.color_count = 1 self.max_color = 5 self.current_pointer = (100, 100) self.canvas_pointer = None self.draw_color = (0, 0, 255) self.previous_color = (0, 0, 255) self.current_color = (0, 0, 255) self.pointer_color = (100, 200, 200) self.point = (10, -3) self.colors=None self.icons = self.prepare_icons() self.get_window() def prepare_icons(self): """ A method to prepare icons on initial frame. Method sets 4 new attributes. cols: List to store (y1, y2) of icon. icon_position: Dictionary to store (y1, y2) as key and corresponding image as value current_icons: A dictionary initialized with initial icons. Changed on every frame when cursor lies above it. mode_pos: Mode as key and its icon's (y1, y2) as value. """ icons = [] cols = np.linspace(0, self.size[1]-1, len(self.modes)+1).astype(np.int64) cols = [(cols[i], cols[i+1]) for i in range(len(cols)-1)] icon_pos = {} mode_pos = {} for i, image_name in enumerate(os.listdir(self.idir)): img = cv2.imread(self.idir+image_name) img = cv2.resize(img, (cols[i][1]-cols[i][0], self.vui_part)) icon_pos[cols[i]] = img mode_pos[self.modes[i]] = cols[i] self.cols = cols self.icon_position = icon_pos self.current_icons = icon_pos self.mode_pos = mode_pos def set_colors(self, col=None, new_colors=None): """ A method to set colors when pointer lies above color icon. Initially used subset of {Red, Green, Blue} col:- column where current pointer lies. new_colors:- If to use other colors. Method returns list of available colors on dropdown menu. Changes the draw color, pointer color upon condition meet. """ # earlier pointer was clipped within the vui pointer = self.canvas_pointer pointer = (pointer[1], self.vui_part+ pointer[0]) if new_colors is None: r = np.array([0, 0, 255]) g = np.array([0, 255, 0]) b = np.array([255, 0, 0]) colors = [r, g, b] colors_new = [colors[i]+colors[i+1] for i in range(len(colors)-1)] colors.extend(colors_new) self.colors = colors else: self.colors = new_colors rows = np.linspace(self.dd_part[0], self.dd_part[1], len(self.colors)+1).astype(np.int64) rows = [(rows[i], rows[i+1]) for i in range(len(rows)-1)] self.color_pos = {} for row, color in zip(rows, colors): self.color_pos[row] = color if row[0]<=pointer[1]=self.max_color: self.draw_color=self.current_color self.pointer_color = (np.abs(np.array([200, 200, 100])-color).tolist()) self.current_window[row[0]:row[1], col[0]:col[1]] = color return self.colors def get_window(self): """ A method to return a VUI window upon called. Sets pointer on VUI canvas. """ self.current_window = np.zeros_like(self.window).astype(np.uint8) for col, img in self.current_icons.items(): self.current_window[:self.vui_part, col[0]:col[1]] = img if self.running_mode == "color": self.set_colors(col=self.cols[self.modes.index("color")]) if self.current_pointer is not None and self.current_pointer[0]>0: cv2.circle(self.current_window, (self.current_pointer[1], self.current_pointer[0]), self.point[0], self.pointer_color, self.point[1]) return self.current_window def update_vui(self, pointer=(100, 100), cpointer=(10, 100)): """ A method to update the entire VUI properties and state. pointer: Current pointer on VUI part. cpointer: Current pointer on Canvas. cpointer is useful when working with color mode. """ self.current_pointer = pointer self.canvas_pointer = cpointer #print(pointer, canvas_pointer) current_icons = {} self.hover=None if pointer[0]<=self.vui_part: for col, mode in zip(self.cols, self.modes): icon = self.icon_position[col].copy() ishape = icon.shape #print(mode) if col[0]
1: rd = int((r - ishape[0])/2) cd = int((c - ishape[1])/2) zeros_icon[:, :] = icon[rd:ishape[0]+rd, cd:ishape[1]+cd] else: rd = int((ishape[0] - r)/2) cd = int((ishape[1] - c)/2) rdd, cdd = 0, 0 if ishape[0]-rd-rd > r: rdd=1 if ishape[1]-cd-cd > c: cdd=1 #print(icon.shape, ishape, rd, abs(r-rd), cd, abs(c-cd)) zeros_icon[rd:ishape[0]-rd-rdd, cd:ishape[1]-cd-cdd] = icon[::] current_icons[col] = zeros_icon.astype(np.uint8) + np.uint8(np.array(self.anim_color)*self.mode_count) if self.prev_mode == self.current_mode: self.mode_count += 1 else: self.prev_mode = self.current_mode self.mode_count = 1 if self.mode_count >= self.max_count: self.running_mode = self.current_mode self.mode_count = 1 self.hover = True else: current_icons[col] = icon self.current_icons = current_icons else: self.mode_count = 1 return self.get_window() # vui = VUI() # #show(vui.window) # vui.update_vui() # vui.update_vui() # vui.update_vui(pointer=(200, 100)) # vui.update_vui(pointer=(200, 100)) class Canvas: def __init__(self, window_size=(525, 700, 3), draw_color=(100, 100, 100), pointer_color=(0, 0, 0), bg_color=(25, 25, 25), mode="move", point=(10, -3), vui=None, ssize=(300, 50, 3)): """ A method to initialize canvas. window_size: size of a canvas window. draw_color: drawing color in RGB. pointer_color: pointer color in RGB. bg_color: background color in RGB. mode: running mode. point: tuple of (pointer radius, thickness) vui: VUI object. ssize: Slider's size. """ self.size=window_size self.draw_color=draw_color self.pointer_color = pointer_color self.bg_color = bg_color self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+bg_color self.mode = mode self.pointer = None self.point = point self.current_window = self.window+self.canvas self.vui = vui self.ssize = ssize self.sregion = () def slider(self, size=(300, 30, 3), spoint=50, scolor=(100, 55, 100)): """ A method to change the pointer size by moving a slider. size: size of slider region. spoint: slider point, generally row position of pointer. scolor: slider color """ swidth=10 #swidth=int(5/50*spoint) #swidth = np.clip(swidth, 5, spoint) swindow=np.zeros(self.size).astype(np.uint8) swindow[:self.ssize[0], 0:self.ssize[1]] += np.uint8([255, 255, 255]) r1 = np.clip(spoint-swidth, swidth, self.ssize[0]-swidth) r2 = np.clip(spoint+swidth, swidth, self.ssize[0]-swidth) spoint = int(10/50 * spoint) #print(r1, r2, spoint) swindow[r1:r2, :self.ssize[1]] = scolor self.point=(spoint, self.point[1]) #cv2.imshow("slider", swindow.astype(np.uint8)) return swindow.astype(np.uint8) def clear(self): self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+self.bg_color def update_window(self, mode, pointer=(400, 100)): """ mode: running mode pointer: where is pointer now? """ self.mode = mode self.vui.mode=mode self.pointer = pointer self.draw_color=self.vui.draw_color self.pointer_color = self.vui.pointer_color #self.pointer = (np.clip(self.vui.vui_part, pointer[0], self.size[0]), pointer[1]) #print("c", self.draw_color) swindow = np.zeros(self.size).astype(np.uint8) #print(pointer) if 0 10: if rname!="mroi": pshape = self.size if rname=="vroi": pshape = (self.vui.vui_part, self.vui.size[1]) if rname=="droi": # make it self.canvas.shape #pshape = (self.canvas_shape[0], self.canvas_shape[1]-self.canvas.ssize[1]) pshape=self.canvas_shape h = bottom - top l = left - right m = (int((m[0]/l)*pshape[1]), int((m[1]/h)*pshape[0])) else: m = (right+m[0], top+m[1]) #print(m) #else: #m=(-1,-1) # pass #if rname=="droi": # m = (m[0]+self.canvas.ssize[1], m[1]) self.roi_pointer[rname]=(m[1], m[0]) return clone def get_window(self, canvas, vui): final_window = vui.copy() canvas_cpy = canvas.copy() vshape = vui.shape cshape = canvas.shape #print(self.running_mode) if self.running_mode == "color": # get part where color lies and make those part of canvas_bg black cp = self.vui.mode_pos[self.running_mode] canvas[:self.vui.dd_part[1]-self.vui.vui_part, cp[0]:cp[1]] = vui[self.vui.vui_part:self.vui.dd_part[1], cp[0]:cp[1]] #show(canvas) #else:# final_window[self.vui.vui_part:, :] = canvas cp = self.roi_pointer["droi"] cp = (cp[1], cp[0]+self.vui.vui_part) point = self.canvas.point cv2.circle(final_window, cp, point[0], self.canvas.pointer_color, point[1]) return final_window def check_force_mode(self): top, right, bottom, left = self.roi_boxes["mroi"] if self.force_modes is None: x=np.linspace(right, left, 4).astype(np.int64) x=[(x[i],x[i+1]) for i in range(len(x)-1)] force_modes = ["move", "draw", "erase"] force_modes = {x[i]:force_modes[i] for i in range(len(x))} #print(force_modes) self.force_modes = force_modes elif self.roi_pointer["mroi"][0]>0: mpointer = self.roi_pointer["mroi"] for col, mode in self.force_modes.items(): if col[0]<=mpointer[1] =self.fcount_mode: #print("f ", mode) #self.fcurrent_count=0 return mode def detector(self): img = self.canvas.canvas.astype(np.uint8) op = pytesseract.image_to_string(img, lang="eng", nice="1") self.detected_text = op #print("Detected: ", op) def perform_mode(self): if self.running_mode=="clear": self.canvas.clear() self.running_mode="move" if self.running_mode=="restart": self.take_average =True self.num_frames=0 self.running_mode="move" self.canvas.clear() if self.running_mode=="save": #cv2.imshow("canvas", self.canvas.canvas.astype(np.uint8)) cv2.imwrite(f"canvas {time.time()}.png", self.canvas.canvas.astype(np.uint8)) #cv2.destroyWindow("canvas") self.running_mode="move" if self.running_mode=="exit": self.key=27 if self.running_mode=="detect": self.running_mode="move" self.detector() def main(self): try: while True: (ret, frame) = self.cam.read() if ret: self.key = cv2.waitKey(1) & 0xFF frame = imutils.resize(frame, width=self.size[1]) frame = cv2.flip(frame, 1) clone = frame.copy() gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY) self.set_grays(gray) self.size = frame.shape #print(self.num_frames) # if to take average and num frames on average taking is lesser than if self.num_frames self.roi_counts["vroi"] and fmode is not None: self.running_mode = fmode else: self.running_mode = self.vui.running_mode else: self.running_mode = fmode self.perform_mode() self.vui.running_mode=self.running_mode canvas = self.canvas.update_window(mode=self.running_mode, pointer=self.roi_pointer["droi"]).astype(np.uint8) self.final_window = self.get_window(canvas=canvas, vui=vui) #cv2.imshow("CW", final_window) self.roi_pointer["vroi"] = (-1, -1) #print(vui.shape) #print(canvas.shape) #cv2.imshow("Canvas", canvas) #cv2.imshow("VUI", vui) clone = self.make_rectangles(clone) self.clone = clone #cv2.imshow("Feed", clone) if self.key==27: self.cam.release() cv2.destroyAllWindows() #self.__del__() #break try: ret, clone_jpeg = cv2.imencode('.jpeg', self.clone) ret1, draw_jpeg = cv2.imencode('.jpeg', self.final_window) #print(ret, ret1) if ret: self.clone = clone_jpeg.tobytes() self.vui_frame=draw_jpeg.tobytes() return clone_jpeg.tobytes(), draw_jpeg.tobytes(), self.detected_text except: pass except: try: ret, clone_jpeg = cv2.imencode('.jpeg', self.clone) ret1, draw_jpeg = cv2.imencode('.jpeg', self.final_window) #print(ret, ret1) if ret: self.clone = clone_jpeg.tobytes() self.vui_frame=draw_jpeg.tobytes() return clone_jpeg.tobytes(), draw_jpeg.tobytes(), self.detected_text except: pass #gw = ContourWriting(avg_frames=150, aweight=0.5) #gw.main()
Приложение
Я не знаю, как начать с Флбой, потому что я сделал лишь несколько проектов на колбе. Если вы не установили колбу, установите ее, используя свой установщик пакета Conda/PIP. Я постараюсь объяснить небольшие коды колбы, но если вы новичок в колбе, постарайтесь узнать некоторые основы об этом. Я изучаю колбу из электронных книг.
Структура проекта
- значки
- Изображения значков
- статический
- CSS
- CSS -файлы
- Ошибка/Среднее взятие изображений
- CSS
- шаблоны
- index.html
- Обнаружение.html
- app.py
- main.py
Импорт зависимости
from flask import Flask, render_template, Response import cv2 import numpy as np import matplotlib.pyplot as plt from main import * import time err_img=cv2.imread("static\error_frame.png") _, err_img_byte = cv2.imencode(".jpg", err_img) err_img_byte = err_img_byte.tobytes() draw=None
- Колба используется для создания объекта приложения.
- render_template – это отображать HTML -файл по разным маршруту.
- Ответ должен отправить объект ответа в HTTP -запрос.
- От нашего
main.py
Файл, мы импортируем все, но импортируем только классКонтурное написание
будет достаточно.
Изображение error_frame.png
Похоже, ниже.
Показывая все фреймы, взятые с камеры на веб -страницу, иногда возникали неожиданные ошибки, которые не могли получить кадр. В этих ситуациях это будет отличной идеей, чтобы показать изображение ошибки вместо того, чтобы показывать ошибку. Также это изображение преобразуется в байты, потому что мы отправляем этот контент в качестве контента ответа позже.
Класс: GestureWeb
Как указывалось ранее на схеме потока, этот класс будет общаться с нашим классом контурного написания и принимает текущие рамки, кадр VUI и обнаруженный текст. Мы инициализируем класс только с несколькими вещами.
- Установить
DETED_TEXT
кНичего
изначально. - Создать объект
Контурное написание
Анкет
class GestureWeb: def __init__(self, port=5005, debug=True): self.detected_text = "Nothing" self.camera= ContourWriting() def frame_gen(self, camera, kind="frame"): while True: frame = camera.main() if frame is None: frame = (None, None) if frame[0] is None: frame = err_img_byte elif frame[1] is None: draw = err_img_byte else: frame, draw, gw.detected_text = frame frame = (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'+bytearray(frame)+b'\r\n') draw = (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'+bytearray(draw)+b'\r\n') if kind =="frame": yield frame if kind =="draw": yield draw
Метод: frame_gen
Этот метод на самом деле является методом генератора. Этот метод вызывает Главный
Метод объекта контурного написания и возвращает его к вызову на каждом кадре. Поскольку мы должны показать все кадры из нашего живого канала в нашем приложении, нам, очевидно, нужен поток, чтобы выполнить эту задачу без каких -либо задержек выполнения. Чтобы вернуть каждый кадр в функцию вызова, мы должны использовать доход
вместо возврат
Анкет
- Позвоните в
Главный
МетодКонтурное написание
объект. - Если это
Нет
Затем мы передадим изображения ошибок в Frame и Vui часть нашего веб -приложения. - Иначе мы разделяем возвращенное значение от
Главный
Метод в 3 части,Рамка
,нарисовать
,DETED_TEXT
Анкет - Затем мы прикрепляем DETEDECTED_TEXT к атрибуту.
- Мы преобразуем кодированное изображение JPEG в Bytearray и прикрепляем его с помощью нашего типа контента.
- Затем мы даем соответствующее значение (кадр или VUI).
Индексный файл: index.html
Я хочу, чтобы этот файл отображал работающий подачу и VUI рядом на той же странице. Следовательно, я должен найти способ сложить, затем горизонтально. Я получил помощь из разделов комментариев Stackoverflow. Я не собираюсь объяснять здесь CSS/HTML, потому что я не очень хорош в них.
Tindex Contour Based Writing System
FramesCanvas
Чтобы просмотреть видео с камерой, мы будем использовать изображение вместо видео. Это может показаться видео с использованием, но правда в том, что комбинация фотографий также является видео. Поэтому я использовал изображение как кадр, мы не узнаем, что это изображение Но мы чувствуем себя видео в реальном времени. И скорость подачи в веб -приложение такая же, как и с камерой. video_feed
это маршрут, который возвращает кадр в качестве объекта ответа. Я не собираюсь много объяснять, потому что я тоже учусь. У нас есть отличный путь для различного изображения, для кадров или живого корма, который у нас есть video_feed
И для холста у нас есть get_canvas
И джинджа сделал это возможным. Таким образом, в основном у нас будет живая подача на левой стороне, а на нашем рисунке на правой стороне.
Чтобы показать обнаруженный текст, у нас есть обнаружение.html
файл. Мы показываем это в нижней части нашей страницы, и мы используем его на той же странице по iframe
ярлык.
Файл обнаружения: detection.html
В этом файле мы показываем только DETED_TEXT
Атрибут объекта класса GestureWeb
Анкет
detection Detected:
{{detected_text}}
Я хочу, чтобы эта страница обновлялась за каждые 5 секунд, чтобы она возвращала обнаруженный текст до 5 секунд.
Приготовьте декораторы/маршруты колбы
Я надеюсь, что кто -то придумает лучший способ выполнения после операций. Я нашел эти способы работы, искав в Интернете целый день, потому что это мой первый проект Flask с нуля.
Перед первым запросом
app = Flask(__name__) @app.before_first_request def before_first_request_func(): global gw gw = GestureWeb()
- Сначала создайте приложение Flask, используя класс Flask.
Используйте декоратор
перед_фирст_рекест
выполнить некоторые задачи перед первым запросом, сделанным в нашем приложении.- Мы сделали наш
GestureWeb
Объект по всему миру доступен. - Сделав наш
GestureWeb
Объект на ранней стадии создания приложений также делает нашу камеру готовой.
- Мы сделали наш
Главный маршрут
Что мы хотим показать, когда пользователь входит в основной URL?
app.route('/') def index(): return render_template('index.html')
Мы хотим отобразить index.html
Когда пользователь входит в основной URL. И мы хотим показать все на этой странице.
Маршрут: video_feed
Как я уже говорил ранее на index.html
Было 2 источника, определенных внутри A IMG
тег каждого изображения. Один из них video_feed
Анкет
@app.route('/video_feed') def video_feed(): fresp = gw.frame_gen(gw.camera, kind="frame") return Response(fresp, mimetype='multipart/x-mixed-replace; boundary=frame')
На вышеуказанном коде, gw.frame_gen (gw.camera,)
, GW
объект GestureWeb. Метод frame_gen
является генератором. Мы передаем объект Контурное написание
И такая вещь, которую мы ожидаем по возвращении. Наконец, мы возвращаем тип ответа через объект ответа.
Маршрут: get_canvas
Как я уже говорил ранее на index.html
Было 2 источника, определенных внутри A IMG
тег каждого изображения. Один из них get_canvas
Анкет
@app.route('/get_canvas') def get_canvas(): fresp = gw.frame_gen(gw.camera, "draw") return Response(fresp, mimetype='multipart/x-mixed-replace; boundary=frame')
На вышеуказанном коде, gw.frame_gen (gw.camera,)
, GW
объект GestureWeb. Метод frame_gen
является генератором. Мы передаем объект Контурное написание
И такая вещь, которую мы ожидаем по возвращении.
Маршрут: обнаружение.html
Теперь сделать наш iframe
работать над index.html
Файл, нам также необходимо сделать маршрут для нашего файла обнаружения. Во время рендеринга этого файла мы также отправим текущий обнаруженный текст. Пожалуйста, посмотрите еще раз на обнаружение.html
Файл для получения большего разрешения.
@app.route('/detection.html', methods=['GET', 'POST']) def detect(): return render_template("detection.html", detected_text=gw.detected_text)
Запрос на разрыв
Этот декоратор полезен, когда мы закрываем нашу вкладку или ошибку.
@app.teardown_request def teardown_request_func(error=None): global gw gw = GestureWeb() return "ok"
Запустить его
Стандартный способ запуска приложения Flask подобен ниже.
if __name__=='__main__': app.run(debug=True)
Коды: app.py
from flask import Flask, render_template, Response import cv2 import numpy as np import matplotlib.pyplot as plt from main import * import time err_img=cv2.imread("static\error_frame.png") _, err_img_byte = cv2.imencode(".jpg", err_img) err_img_byte = err_img_byte.tobytes() draw=None class GestureWeb: def __init__(self, port=5005, debug=True): self.detected_text = "Nothing" self.camera= ContourWriting() def frame_gen(self, camera, kind="frame"): while True: frame = camera.main() if frame is None: frame = (None, None) if frame[0] is None: frame = err_img_byte elif frame[1] is None: draw = err_img_byte else: frame, draw, gw.detected_text = frame frame = (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'+bytearray(frame)+b'\r\n') draw = (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n'+bytearray(draw)+b'\r\n') if kind =="frame": yield frame if kind =="draw": yield draw if kind=="text": yield detected_text app = Flask(__name__) @app.before_first_request def before_first_request_func(): global gw gw = GestureWeb() @app.route('/') def index(): return render_template('index.html') @app.route('/get_canvas') def get_canvas(): fresp = gw.frame_gen(gw.camera, "draw") return Response(fresp, mimetype='multipart/x-mixed-replace; boundary=frame') @app.route('/video_feed') def video_feed(): fresp = gw.frame_gen(gw.camera, kind="frame") return Response(fresp, mimetype='multipart/x-mixed-replace; boundary=frame') @app.route('/detection.html', methods=['GET', 'POST']) def detect(): return render_template("detection.html", detected_text=gw.detected_text) @app.teardown_request def teardown_request_func(error=None): global gw gw = GestureWeb() return "ok" if __name__=='__main__': app.run(debug=True)
Окончательно
Результат всего кода должен выглядеть ниже. Из любого терминала запустить app.py
файл. Я использую VS -код, и у меня есть R
Это было просто простое развертывание в веб -приложении. Существует множество систем, где пользователь может писать на своем холсте, нажав кнопку мыши и предсказывая то, что было написано. Кроме того, некоторые люди говорили мне, что мы можем сделать это простыми аддонами. Но я не пытаюсь доказать, что мои методы являются лучшими и должны широко использоваться. Я применил то, что думал, и внедрил идеи на коды. Теперь я развернул код реализации в веб -приложение. Есть еще количество проблем, которые я не нашел, и я надеюсь, что кто -то скоро его найдет. Я с нетерпением жду, чтобы ответить на все запросы здесь не только на LinkedIn.
Я собираюсь указать на функции и недостатки этой системы.
Коды
Коды для текущей версии системы доступны по ссылке ниже, а если это не так, нажмите на комментарий или оставьте мне сообщение (LinkedIn, Twitter или напишите мне).
Функции
- Развернуто в веб -приложении.
- Живой корм и область рисунка расположены рядом.
- Обнаруженный текст проверяется на 5 секунд.
Недостатки
Недостатки наиболее полезны, чтобы найти новую функцию в следующей версии. Ну вот их много.
- Иногда кадры не читаются должным образом, а предупреждение OpenCV продолжает появляться на консоли.
- Система пригодна для использования по контуру чего угодно. Следовательно, необходимо использовать какой -то жест, подтверждающий модель.
- Также на видео выше можно увидеть, как указатель быстро движется по регионам VUI. Это не всегда приемлемо пользователям. Следовательно, это должно быть устранено в ближайшее время.
Идеи
- Что если мы сможем создать модель, которая может классифицировать жест, и мы можем определить определенный жест для определенного режима?
- Что если мы сделаем модель рисования, которая может автоматически завершить наш рисунок.
Что дальше?
Я постараюсь решить недостатки в следующий раз. Но мне интересно заставить эту систему работать на мобильных телефонах. В настоящее время я подумываю взять рамки с камеры устройств и обработать ее. Затем используйте какой -нибудь вызов API, чтобы получить эти кадры. Я мог бы использовать единство. Я очень рад попытаться использовать LSTM и другие алгоритмы глубокого обучения искусства, чтобы сделать эту систему более удивительной Но у меня нет доступа в Интернет (кроме сотовых данных), чтобы провести широкие исследования.
Почему бы не прочитать больше?
- Система визуальной записи на основе жестов с использованием OpenCV и Python
- Жестная система на основе визуального письма: Добавление визуального пользовательского интерфейса
- Система визуальной записи на основе жестов: добавление виртуальной анимации, новой режима и нового VUI
- Жестная система на основе визуального письма: Добавить слайдер, больше цветов и оптимизированный код ООП
- Линейная регрессия с нуля
- Написание популярных оптимизаторов ML с нуля
- Поправить нейронную сеть вперед с нуля
- Сверточные нейронные сети с нуля
- Написание простого класса обработки изображений с нуля
- Развертывание чат -бота RASA на Android с использованием Unity3D
- Наивный байеса для текстовых классификаций: царапина в рамки
- Простой OCR для рукописного текста Деванагари
Редом от ранней версии. Первой лучшей версией всего будет улучшение недостатка. Я применяю ту же концепцию. В предыдущих версиях было много проблем, один из них – структурированный способ кода. Теперь, в этой версии, я использую объектно -ориентированный путь, любой концепции ООП будет достаточно, чтобы понять следующие коды. И незначительная концепция обработки изображений, а также знание начинающего OpenCV будет достаточно, чтобы узнать все в этом блоге. Но для лучшего понимания этого проекта требуется Основная предпосылка для понимания концепций и алгоритмов, используемых в этом блоге, – это просмотреть мои предыдущие блоги . Потому что я мало объясняю на этот раз. Следуйте Прежде чем что -либо Раздел для получения дополнительной информации.
Что теперь?
- Теперь в этом блоге я буду делиться новым и более оптимизированным и масштабируемым способом написания кодов для выполнения этой задачи. Я представляю путь ООП.
- Я также добавлю новый режим. И этот режим является наиболее важным. Ползунок, чтобы изменить размер указателя.
Вся система можно разделить на 3 части. И если мне придется сделать диаграмму, то это будет что -то вроде ниже.
Вуи
Класс, представляющий визуальный пользовательский интерфейс, который будет лежать над холстом и содержит значки и взаимодействующую панель. Некоторые общие методы записываются внутри коробки, а среда связи между классами – это объект VAI, а иногда и атрибуты. Одним из примеров связи является написание контуров класса посылает текущее местоположение указателя в VUI, а VUI показывает указатель в области VUI (если лежит). Затем представление применяет анимацию на значках и пытается установить режим. Затем текущее окно VUI отправляется обратно вместе с режимом работы.
Холст
Класс, представляющий место письма. Контурная запись отправляет местоположение указателя и режим работы, а операция происходит на холсте. Операции обычно рисуют. Стереть и двигаться. Наконец, письменный холст отправляется обратно.
Контурное письмо
Класс, который обрабатывает все другие классы и выполняет задачу извлечения контура с помощью OpenCV. Этот класс несет ответственность за многие вещи, и один из них является назначением указателя.
Прежде чем что -либо
Прежде всего, я прошу вас просмотреть мои предыдущие блоги и только затем вернуться в этот блог. Потому что блоги ниже являются более ранней версией кодов и концепций. Я все еще использую те же концепции, а также вы будете поражены, увидев, сколько я прогрессировал. Я собираюсь описать только то, что здесь происходит на очень мало. Следовательно, если вы новичок в этом проекте, то лучше всего их просмотреть. Пожалуйста, просмотрите их последовательно.
- Система визуальной записи на основе жестов с использованием OpenCV и Python
- Жестная система на основе визуального письма: Добавление визуального пользовательского интерфейса
- Система визуальной записи на основе жестов: добавление виртуальной анимации, новой режима и нового VUI
Кредиты
Я хочу отдать должное этим блогам всем на LinkedIn Кто отреагировал, поделился и прокомментировал мой предыдущий блог и на Twitter Также (большинство ретвитов были от ботов LOL). Я очень благодарен, что мой Этот пост LinkedIn о предыдущей версии Получил более 2,2 тыс. Реакций и почти 30 тыс. Просмотров. Я никогда не думал, что это привлечет столько внимания, и здесь я снова его улучшаю. Если мне нужно взять имя и причину для кредитов:-
- Сагар Дхунгель : Кто мотивирует меня писать больше.
- Сурадж Биста : Кто мотивирует меня писать больше.
- Pitambar Mahato : Кто читает мои все блоги и дает отзывы.
- Камаль Гаутам : Кто читает мои все блоги и дает отзывы.
- Индра Пудель : Кто помог мне найти проблемы в блогах.
Мотивация
Я очень мотивирован поддержкой, которую люди дали мне LinkedIn .
Зависимости
Как обычно, импорт зависимостей при более раннем запуске кода.
import numpy as np import os import matplotlib.pyplot as plt import cv2 import imutils def show(img, figsize=(10, 10)): figure = plt.figure(figsize=figsize) plt.imshow(img) plt.show()
VUI: визуальный пользовательский интерфейс
Я не знаю, используется ли термин VUI для визуального пользовательского интерфейса, и если этот термин прав сейчас, для использования. Но я все равно использую это на той цели, которую мы взаимодействуем с видео кадрами. На этот раз вместо простой переменной я использую новый и надежный способ определения VUI. Класс Vui.
Инициализировать класс
Класс инициализируется с помощью параметров по умолчанию и настройки полезных параметров перед запуском программы.
class VUI: """ A class for visual user interface. Recommended to use default parameters. """ def __init__(self, icons_dir="icons/", window_size=(525, 700, 3), vui_part=20): """ icons_dir: directory to use icons from. window_size: size of an vui window. Recommended to use equal of final window. vui_part: How much % of rows from top will be used to stack icons? """ self.idir = icons_dir self.window = np.zeros(window_size).astype(np.uint8) self.size = window_size self.vui_part = int(vui_part/100 * self.size[0]) self.dd_part = (self.vui_part, int(50/100 * self.size[0])) self.modes = [fname.split(".")[0] for fname in os.listdir(self.idir)] self.icon_size = (int(self.size[1]/len(self.modes)), self.vui_part) # c, r self.current_icons = [] self.anim_scale = 0.5 self.anim_color = [5, 15, 2] self.prev_mode = "move" self.current_mode = "move" self.running_mode = None self.hover=None self.mode_count = 1 self.max_count = 5 self.color_count = 1 self.max_color = 5 self.current_pointer = (100, 100) self.canvas_pointer = None self.draw_color = (0, 0, 255) self.previous_color = (0, 0, 255) self.current_color = (0, 0, 255) self.pointer_color = (100, 200, 200) self.point = (10, -3) self.colors=None self.icons = self.prepare_icons() self.get_window()
idir
: Удерживает каталог икон.окно
: держит окно Vui. Изначально пустое изображение.vui_part
: Сколько строк из верхнего ряда использовать для значков?dd_part
: Сколько рядов из верхнего ряда можно использовать для выпадения?моды
: Доступные режимы. Обычно название изображений под idir.icon_size
: Размер каждого значка на vui_part. Формат (Cols, ряды).current_icons
: Список для хранения значков на каждом кадре.aniste_scale
: Анимационная шкала. Как увеличить/уменьшить размер значка?ami_color
: Цвет анимации для значковprev_mode
: Предыдущий режим, по умолчанию двигаться.current_mode
: Текущий режим, по умолчанию перемещение.Rune_Mode
: Запуск режима, по умолчанию нет.прокачать
: Если курсор выше региона VUI.mode_count
: Подсчет количества раз, указатель непрерывно находится над значком тока. По умолчанию 1.max_count
: Сколько раз повторяется текущий режим, чтобы изменить режим выполнения в текущий режим?color_count
: То же, что и mode_count, но для цвета.max_count
: То же, что и max_count, но для цвета.current_pointer
: Где в настоящее время находится указатель? Значение, измененное другими классами.canvas_pointer
: Где сейчас указатель на холсте?draw_color
: Текущий цвет рисования. Красный дефолт.Предыдущий_Color
: Где курсор солгал на предыдущем раскрывающемся списке цвета?current_color
: Где курсор лежит на текущем выпадении цвета?pointer_color
: Какой цвет должен быть указатель.точка
: Значение, чтобы настроить размер указателя. (радиус, толщина)цвета
: Список цветов в RGB для выбора цветаЗначки
: Значение, установленное методомPRIPARE_ICONS
.
Каждый атрибут имеет свой вариант использования, и я старался сделать его меньше. Некоторые переменные, такие как количество, ток/предыдущий важны для изменения режимов/цветов Таким образом, они должны быть настроены должным образом.
Метод: подготовить значки
Метод подготовки значков и их соответствующих режимов. Это называется изначально при инициализации класса.
def prepare_icons(self): """ A method to prepare icons on initial frame. Method sets 4 new attributes. cols: List to store (y1, y2) of icon. icon_position: Dictionary to store (y1, y2) as key and corresponding image as value current_icons: A dictionary initialized with initial icons. Changed on every frame when cursor lies above it. mode_pos: Mode as key and its icon's (y1, y2) as value. """ cols = np.linspace(0, self.size[1]-1, len(self.modes)+1).astype(np.int64) cols = [(cols[i], cols[i+1]) for i in range(len(cols)-1)] icon_pos = {} mode_pos = {} for i, image_name in enumerate(os.listdir(self.idir)): img = cv2.imread(self.idir+image_name) img = cv2.resize(img, (cols[i][1]-cols[i][0], self.vui_part)) icon_pos[cols[i]] = img mode_pos[self.modes[i]] = cols[i] self.cols = cols self.icon_position = icon_pos self.current_icons = icon_pos self.mode_pos = mode_pos
Что здесь происходит?
- Метод начинается с создания массива столбцов для значков. Если у нас есть 9 значков, то, чтобы сложить их, сколько строк требуется? 10.
- Затем мы изменяем этот массив в список кортежей. Хранить (Y1, Y2) для каждого столбца.
- Инициализировать словари
icon_pos
иmode_pos
Анкет - Для каждого изображения в каталоге значков:
- Прочитайте изображение и измените его размер, чтобы сформировать это полностью подходит для Cols.
- Магазин изображение на
Иконовый проход
, ключ будет холодным, а значение будет изображением. - Магазин Cols на
mode_pos
, ключ как имя режима и значение как Cols.
- Сделайте атрибуты для
кольцо
,icon_position
,current_icons
,mode_pos
Анкет
Метод: установить цвета
Этот метод используется для изменения цвета указателя/рисовать, когда указатель находится над ним для некоторых кадров. Мы можем изменить цвет из раскрывающегося меню. Цвета либо предоставляются извне по умолчанию.
def set_colors(self, col=None, new_colors=None): """ A method to set colors when pointer lies above color icon. Initially used subset of {Red, Green, Blue} col:- column where current pointer lies. new_colors:- If to use other colors. Method returns list of available colors on dropdown menu. Changes the draw color, pointer color upon condition meet. """ # earlier pointer was clipped within the vui pointer = self.canvas_pointer pointer = (pointer[1], self.vui_part+ pointer[0]) if new_colors is None: r = np.array([0, 0, 255]) g = np.array([0, 255, 0]) b = np.array([255, 0, 0]) colors = [r, g, b] colors_new = [colors[i]+colors[i+1] for i in range(len(colors)-1)] colors.extend(colors_new) self.colors = colors else: self.colors = new_colors rows = np.linspace(self.dd_part[0], self.dd_part[1], len(self.colors)+1).astype(np.int64) rows = [(rows[i], rows[i+1]) for i in range(len(rows)-1)] self.color_pos = {} for row, color in zip(rows, colors): self.color_pos[row] = color if row[0]<=pointer[1]=self.max_color: self.draw_color=self.current_color self.pointer_color = (np.abs(np.array([200, 200, 100])-color).tolist()) self.current_window[row[0]:row[1], col[0]:col[1]] = color return self.colors
- Метод принимает указатель как указатель Canvas, потому что мы используем только указатель VUI на
vui_part
Только. - Мы отредактировали указатель, чтобы сделать его подходящим для нашего дела. Обмен требуется, потому что у нас в основном есть указатель (Y, X).
- Мы инициализируем
цвета
из проверки состояния. - Поскольку цвета закрыты в закрытом виде, цвета сложены сверху вниз. Поэтому найдите ряды, которые делят цвета.
- Если x координата текущего указателя находится в любой из строк цвета, то
color_count
увеличена. - Когда
color_count
достигаетmax_count
Мы меняем цвет указателя и рисоваем цвет. - Мы также должны изменить цвет окна в тех регионах, где находится позиция текущего цвета.
Метод: получить окно
Этот метод вызывается из других методов.
def get_window(self): """ A method to return a VUI window upon called. Sets pointer on VUI canvas. """ self.current_window = np.zeros_like(self.window).astype(np.uint8) for col, img in self.current_icons.items(): self.current_window[:self.vui_part, col[0]:col[1]] = img if self.running_mode == "color": self.set_colors(col=self.cols[self.modes.index("color")]) if self.current_pointer is not None and self.current_pointer[0]>0: cv2.circle(self.current_window, (self.current_pointer[1], self.current_pointer[0]), self.point[0], self.pointer_color, self.point[1]) return self.current_window
- Начинается с инициализации нового пустого изображения.
- Перевернуть через
current_icons
Ключ и значения установите изображение значка в точную позициюvui_part
. - Когда
Rune_Mode
Является ли цвет, мы должны показать выпадающую сторону, следовательно, вызовset_colors
Метод и передайте столбец, где ток указатель. - Наконец -то нарисуйте указатель на окно и верните.
Метод: обновление VUI
Этот метод вызывается из основного класса письма. Метод выполняет слишком много вещей здесь. Этот метод вызывает другие методы также для выполнения таких операций, как изменение цвета, изменение режима и т. Д.
def update_vui(self, pointer=(100, 100), cpointer=(10, 100)): """ A method to update the entire VUI properties and state. pointer: Current pointer on VUI part. cpointer: Current pointer on Canvas. cpointer is useful when working with color mode. """ self.current_pointer = pointer self.canvas_pointer = cpointer #print(pointer, canvas_pointer) current_icons = {} self.hover=None if pointer[0]<=self.vui_part: for col, mode in zip(self.cols, self.modes): icon = self.icon_position[col].copy() ishape = icon.shape #print(mode) if col[0]1: rd = int((r - ishape[0])/2) cd = int((c - ishape[1])/2) zeros_icon[:, :] = icon[rd:ishape[0]+rd, cd:ishape[1]+cd] else: rd = int((ishape[0] - r)/2) cd = int((ishape[1] - c)/2) rdd, cdd = 0, 0 if ishape[0]-rd-rd > r: rdd=1 if ishape[1]-cd-cd > c: cdd=1 #print(icon.shape, ishape, rd, abs(r-rd), cd, abs(c-cd)) zeros_icon[rd:ishape[0]-rd-rdd, cd:ishape[1]-cd-cdd] = icon[::] current_icons[col] = zeros_icon.astype(np.uint8) + np.uint8(np.array(self.anim_color)*self.mode_count) if self.prev_mode == self.current_mode: self.mode_count += 1 else: self.prev_mode = self.current_mode self.mode_count = 1 if self.mode_count >= self.max_count: self.running_mode = self.current_mode self.mode_count = 1 self.hover = True else: current_icons[col] = icon self.current_icons = current_icons else: self.mode_count = 1 return self.get_window()
- Возьми
vui_pointer
и установите наcurrent_pointer
. - Возьми
canvas_pointer
и установите тоже. - Инициализировать словарь для хранения
current_icons
. Мы храним здесь измененный значок вместе с неизменным. - Инициализировать
Hover
никто. - Продолжайте только тогда, когда тока x ось лежит ниже
vui_part
Анкет то есть, если указатель находится на панели значков. - Проверка через столбцы значков и режимов одновременно.
- Возьмите значок, лежащий на этой колонке.
- Если столбец тока указателя находится в столбцах:
- Установите текущий режим.
- Создать пустое изображение формы значка как
zeros_icon
Анкет - Найдите фактор масштаба по нескольким режимам.
- Найдите новую строку/столбец для этого значка. Мы масштабируем значок, чтобы он выглядел как анимирующий.
- Сделайте центр New Icon’s Center и Original Icon’s Centre и вставьте его в пустое изображение.
- Магазин
zeros_icon
наcurrent_icons
Анкет - Выполнить проверку счета. И изменить режим, когда количество режимов достигает макс. Установите навредить True.
- Еще:
- Значок магазина на
current_icon
. Потому что указатель не выше этого значка, так что оставьте его таким, каким есть.
- Значок магазина на
- Вернуть вывод
get_window
метод
Тестовый класс: VUI
Чтобы проверить класс, мы будем называть метод update_vui
передав это указатель. Чтобы убедиться, что наш выпадающий список работает нормально, я установим указатель на регион Color. И чтобы показать анимирующий эффект, я буду называть его несколько раз. Смотрите результат ниже.
vui = VUI() show(vui.window) fig = plt.figure(figsize=(30, 30)) ax = fig.subplots(2, 3, sharex='col', sharey='row') for i in range(2): for j in range(3): ax[i][j].imshow(vui.update_vui())
Сверху изображения ясно, что наше выпадение работает нормально, а значок также изменяет свой цвет и размер одновременно. Следовательно, тест прошел.
Холст
Кернат, который мы будем использовать, также будет классом. Это не очень сложно, чем класс VUI, потому что его задача состоит в том, чтобы записать/стирать только в той позиции, где находится указатель. Стереть намного проще, если мы указали цвет фона, и всякий раз, когда находится указатель, нарисуйте круг с цветом фона.
Инициализировать: холст
Инициализируйте класс с по умолчанию и основными параметрами. Следуйте за DocString для получения дополнительной информации.
class Canvas: def __init__(self, window_size=(525, 700, 3), draw_color=(100, 100, 100), pointer_color=(0, 0, 0), bg_color=(25, 25, 25), mode="move", point=(10, -3), vui=None, ssize=(300, 50, 3)): """ A method to initialize canvas. window_size: size of a canvas window. draw_color: drawing color in RGB. pointer_color: pointer color in RGB. bg_color: background color in RGB. mode: running mode. point: tuple of (pointer radius, thickness) vui: VUI object. ssize: Slider's size. """ self.size=window_size self.draw_color=draw_color self.pointer_color = pointer_color self.bg_color = bg_color self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+bg_color self.mode = mode self.pointer = None self.point = point self.current_window = self.window+self.canvas self.vui = vui self.ssize = ssize self.sregion = ()
- Холст инициализируется с цветом фона.
- Текущее окно должно включать указатель и настоящий холст.
Метод: окно обновления
Метод, который выполняет рисование/стирание/перемещение на холсте и показывает указатель.
def update_window(self, mode, pointer=(400, 100)): """ mode: running mode pointer: where is pointer now? """ self.mode = mode self.vui.mode=mode self.pointer = pointer self.draw_color=self.vui.draw_color self.pointer_color = self.vui.pointer_color #self.pointer = (np.clip(self.vui.vui_part, pointer[0], self.size[0]), pointer[1]) #print("c", self.draw_color) swindow = np.zeros(self.size).astype(np.uint8) #print(pointer) if 0
- Некоторые атрибуты первоначально устанавливаются из объекта VUI и параметров.
- Инициализировать ползунок к пустому изображению.
- Когда указатель находится в области ползунка, затем вызовите окно слайдера и измените режим для перемещения.
- Если режим рисуется,
- Нарисуйте круг по положению указателя с текущими свойствами рисования на холсте.
- Создайте текущее окно.
- Нарисуйте круг на текущем окне с цветом указателя.
- Если режим стирается,
- Нарисуйте круг по положению указателя с текущими свойствами рисования на холсте. I.G Цвет фона
- Создайте текущее окно.
- Нарисуйте круг на текущем окне с цветом указателя.
- Еще:
- Создайте текущее окно и нарисуйте круг в положении указателя с цветом указателя.
- Вернуть текущее окно.
Метод: слайдер
Это новая концепция в этой версии. Я использую слайдер, чтобы изменить размер указателя. Разве это не интересно?
def slider(self, size=(300, 30, 3), spoint=50, scolor=(100, 55, 100)): """ A method to change the pointer size by moving a slider. size: size of slider region. spoint: slider point, generally row position of pointer. scolor: slider color """ swidth=10 #swidth=int(5/50*spoint) #swidth = np.clip(swidth, 5, spoint) swindow=np.zeros(self.size).astype(np.uint8) swindow[:self.ssize[0], 0:self.ssize[1]] += np.uint8([255, 255, 255]) r1 = np.clip(spoint-swidth, swidth, self.ssize[0]-swidth) r2 = np.clip(spoint+swidth, swidth, self.ssize[0]-swidth) spoint = int(10/50 * spoint) #print(r1, r2, spoint) swindow[r1:r2, :self.ssize[1]] = scolor self.point=(spoint, self.point[1]) #cv2.imshow("slider", swindow.astype(np.uint8)) return swindow.astype(np.uint8)
- Метод начинается с определения новой переменной
Swidth
, который является шириной ползунка. - Новое пустое изображение сделано для хранения слайдера.
- Слайдер изготавливается, сделав прямоугольник на строке точки, мы добавляем ширину как в верхней/нижней части слайдера.
- Измените наш размер указателя по сравнению с положением ползунка.
- Вернуть окно ползунка.
Метод: чисто
Метод очистки окна и холста при включении режима очистки.
def clear(self): self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+self.bg_color
И холст, и окно возвращаются в их первоначальное состояние.
Тест: холст
Давайте протестируем наш холст. Вывод должен выглядеть ниже. Я тестирую это, чтобы работать с слайдером и рисую оба.
c = Canvas(vui=VUI()) print("Draw with normal pointer move.") # draw the color first. fig = plt.figure(figsize=(30, 30)) ax = fig.subplots(2, 3, sharex='col', sharey='row') p = 0 for i in range(2): for j in range(3): ax[i][j].imshow(c.update_window(mode="draw", pointer=(400, 100+p))) p+=20 plt.show() print("Draw with slider move.") # test the slider fig = plt.figure(figsize=(30, 30)) ax = fig.subplots(2, 3, sharex='col', sharey='row') p=0 for i in range(2): for j in range(3): ax[i][j].imshow(c.update_window(mode="draw", pointer=(50+p, 10))) p+=20 plt.show() print("Draw with new pointer.") # Draw again the slider has increased fig = plt.figure(figsize=(30, 30)) ax = fig.subplots(2, 3, sharex='col', sharey='row') p = 0 for i in range(2): for j in range(3): ax[i][j].imshow(c.update_window(mode="draw", pointer=(400, 100+p))) p+=20 plt.show() print("Erase with new pointer.") # erase it now fig = plt.figure(figsize=(30, 30)) ax = fig.subplots(2, 3, sharex='col', sharey='row') p = 0 for i in range(2): for j in range(3): ax[i][j].imshow(c.update_window(mode="erase", pointer=(400, 100+p))) p+=20 plt.show()
Draw with normal pointer move.
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Draw with slider move. Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Draw with new pointer.
Erase with new pointer.
Испытание выше выполняется в режиме рисования и стирает с различным размером указателя. Что я сделал?
- Сделал фигуру и сюжеты для 6 изображений.
- Нарисуйте цвет на холсте в положении указателя в разных положениях.
- Сделайте положение указателя в области слайдера и заставили его двигаться.
- Нарисуйте с новым размером указателя.
- Стереть с новым размером указателя.
Тест пройден.
Контурное письмо
Класс для выполнения общей работы с использованием контуров. Я упорно трудился, чтобы поддерживать здесь коды, обновляя предыдущие коды.
Инициализировать: Контурное письмо
Вместо того, чтобы использовать отдельную переменную для каждой ROI, я подумал об использовании словарей только для хранения только определенного свойства. Ключи словаря будет rOI -название и значение, и это будет тип данных для хранения.
class ContourWriting: """ A class to bind all other classes uses. """ def __init__(self, count_mode=5, avg_frames=100, rois={"droi":[250, 400, 480, 681], "mroi":[100, 10, 300, 225], "vroi":[150, 400, 240, 681]}, icons_dir="icons/", aweight=0.5): """ count_mode: max count avg_frames: number of frames to take average rois: types of ROIS. Keys draw, move, vui and values [top, right, bottom, left] icons_dir: icons's directory aweight: value of delta on running average. """ self.aweight = aweight self.avg_frames=avg_frames self.roi_boxes = rois self.roi_averages = {key:None for key in rois.keys()} self.roi_grays = {key:None for key in rois.keys()} self.roi_masks = {key:None for key in rois.keys()} self.roi_pointer = {key:None for key in rois.keys()} self.roi_counts = {key:None for key in rois.keys()} self.size = (525, 700) self.set_pointer() self.vui = VUI() self.canvas_shape = (self.size[0]-self.vui.vui_part, self.size[1], 3) self.canvas = Canvas(window_size=self.canvas_shape, vui=self.vui) self.running_mode = self.vui.running_mode self.force_modes=None self.fcount_mode=count_mode self.fcurrent_count=0 self.fprev_mode = "move" self.check_force_mode()
ROI_AVERAGES
: хранить средний изображение каждой рентабельности.roi_grays
: хранить изображение серого каждую рентабельность инвестиций.roi_masks
: хранить изображение маски каждого ROI.roi_pointer
: хранить позицию указателя на каждом ROI.roi_counts
: хранить количество точек контура на каждой ROI.set_pointer
: Метод для установки указателя в соответствующей позиции.vui
: атрибут к объекту хранитьVui
учебный класс.canvas_shape
: Должно быть нижеvui_part
Анкетforce_modes
: Имя режимы силы из силы илиMROI
Анкетcheck_force_mode
: Метод для выполнения проверки наMROI
Анкет
Метод: Main
Метод вызове извне и обрабатывает все задачи.
def main(self): cam = cv2.VideoCapture(0) self.num_frames = 0 self.take_average=True while True: (ret, frame) = cam.read() if ret: self.key = cv2.waitKey(1) & 0xFF frame = imutils.resize(frame, width=self.size[1]) frame = cv2.flip(frame, 1) clone = frame.copy() gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY) self.set_grays(gray) self.size = frame.shape #print(self.roi_grays) # if to take average and num frames on average taking is lesser than if self.num_framesself.roi_counts["vroi"] and fmode is not None: self.running_mode = fmode else: self.running_mode = self.vui.running_mode else: self.running_mode = fmode self.perform_mode() self.vui.running_mode=self.running_mode canvas = self.canvas.update_window(mode=self.running_mode, pointer=self.roi_pointer["droi"]).astype(np.uint8) final_window = self.get_window(canvas=canvas, vui=vui) cv2.imshow("CW", final_window) self.roi_pointer["vroi"] = (-1, -1) clone = self.make_rectangles(clone) cv2.imshow("Feed", clone) if self.key==27: break cam.release() cv2.destroyAllWindows()
- Начинается с инициализации камеры.
- Инициализировать
num_frames
к0
иtake_average
кВерно
Анкет - В то время как True выполните ниже:
- Прочитайте каждый кадр и прочитайте параметр успеха.
- Если кадр был прочитал, выполните ниже:
- Инициализировать проверку ключей.
- Измените размер рамы и переверните ее, чтобы сделать его зеркальным эффектом.
- Преобразовать раму в Graysclae и передать его
set_grays ()
Метод для поиска серого на каждом ROI. Если
num_frames
меньшеavg_frames
иtake_average
этоВерно
В- Позвоните
rong_average ()
Анкет - Поместите номер кадра на кадр.
- Увеличить
num_frames
Анкет
- Позвоните
- Еще:
- Установить
take_average
кЛОЖЬ
. - Позвоните
find_contours ()
Чтобы получить текущие контуры. - Позвоните
check_force_mode ()
Метод для проверкиMROI
Анкет - Позвоните
update_vui ()
о VUI, передав его ток, указатель vui roi и нарисуйте ROI. Если
Hover
неНет
:- Затем установите режим работы в режиме работы VUI.
- Если количество точек контура на
MROI
больше, чемvroi
Затем попробуйте установить режим работы в режиме принудительного принуждения. - Иначе установить running_mode в режим работы VUI.
- Наконец -то выполните действие режима.
- Установите режим работы VUI в режим работы.
- Позвоните
update_window ()
холста, передавая его режим работы и указатель розыгрыша. - Наконец -то позвоните
get_window ()
Метод, передавая текущий холст и VUI. - Покажите текущее окно письма.
- Установите ROI VUI в (-1, -1), чтобы предотвратить ложный указатель.
- Позвоните
make_rectangles ()
Чтобы сделать прямоугольник для каждой рентабельности.
- Установить
- Показать
клон
изображение. - Выходная петля в режиме выхода.
- Отпустите камеру и уничтожьте все окна.
Метод: установить серые
def set_grays(self, gray_frame): """ Takes current grayscale frame and sets roi_grays dictionary. """ for rname, box in self.roi_boxes.items(): top, right, bottom, left = box gray_roi = gray_frame[top:bottom, right:left] gray_roi=cv2.GaussianBlur(gray_roi, (7, 7), 0) self.roi_grays[rname] = gray_roi
- Проверьте каждую ограничивающую коробку каждого ROI.
- Возьмите верхнюю, справа, внизу, левые координаты коробки и обведите эту область из рамки серого.
- Добавьте немного размытия в эту обрезанную область и добавьте его в
roi_grays
к соответствующемуrname
ключ.
Метод: среднее время работы
Метод для выполнения среднего бега по каждой рентабельности.
def running_average(self): for rname, roi in self.roi_averages.items(): gimg = self.roi_grays[rname] if roi is None: roi = gimg.copy().astype("float") else: cv2.accumulateWeighted(gimg, roi, self.aweight) self.roi_averages[rname] = roi
- Возьмите средние значения каждого ROI и выполняйте среднее значение.
- Установить среднее значение в
ROI_AVERAGES
Соответствующийrname
ключ.
Метод: найти контуры
def find_contours(self, clone, threshold=10): """ A method to find contours on each ROIs, draw maximum contours and then set pointer relative to VUI and Canvas. clone: current clone of frame. threshold: thresholding value. """ self.roi_counts = {key:None for key in self.roi_counts.keys()} for rname, ravg in self.roi_averages.items(): # abs diff betn img and bg top, right, bottom, left = self.roi_boxes[rname] diff = cv2.absdiff(ravg.astype("uint8"), self.roi_grays[rname]) _, th = cv2.threshold(diff, threshold, 255, cv2.THRESH_BINARY) (cnts, _) = cv2.findContours(th.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) m = (-1, -1) if len(cnts)!=0: max_cnt = max(cnts, key=cv2.contourArea) cv2.drawContours(clone, [max_cnt+(right,top)], -1, (0, 0, 255)) sshape = max_cnt.shape new_segmented = max_cnt.reshape(sshape[0], sshape[-1]) m = new_segmented.min(axis=0) cv2.circle(clone, (right+m[0], top+m[1]), 15, self.vui.pointer_color, -3) self.roi_counts[rname] = len(max_cnt) # translate m of this roi to window shape #if len(max_cnt)>10: if rname!="mroi": pshape = self.size if rname=="vroi": pshape = (self.vui.vui_part, self.vui.size[1]) if rname=="droi": pshape = self.canvas_shape h = bottom - top l = left - right m = (int((m[0]/l)*pshape[1]), int((m[1]/h)*pshape[0])) else: m = (right+m[0], top+m[1]) self.roi_pointer[rname]=(m[1], m[0]) return clone
Метод для поиска контуров на каждом ROI, нарисуйте максимальные контуры, а затем установите указатель по сравнению с VUI и холстом.
- Инициализируйте roi_counts с ключами, как каждая рентабельность и значения, как нет.
- Перевернуть в среднем от каждой рентабельности.
- Возьмите текущий серый из этой рентабельности и найдите абсолютную разницу.
- Порог Разница изображения к двоичности и найти контуры.
- Установите m на (-1, -1) изначально.
- Если длина контуров больше, тогда:
- Найдите максимальный контур и нарисуйте его на клоне рамы.
- Найдите минимальную точку контура и нарисуйте указатель на эту точку.
- Вставьте количество контурных подсчетов в каждую ROI.
- Выполните перевод указателя по сравнению с MROI, Vroi и Droi.
- Наконец, установить
roi_pointer
к каждой рентабельности.
- Вернуть клон.
Метод: Проверьте режим силы
Обратите внимание, что у нас есть 2 ROI, которые могут изменить режим работы, но когда использовать что? Сильный режим используется из MROI
Раздел и другие режимы используются из vroi
раздел.
def check_force_mode(self): top, right, bottom, left = self.roi_boxes["mroi"] if self.force_modes is None: x=np.linspace(right, left, 4).astype(np.int64) x=[(x[i],x[i+1]) for i in range(len(x)-1)] force_modes = ["move", "draw", "erase"] force_modes = {x[i]:force_modes[i] for i in range(len(x))} #print(force_modes) self.force_modes = force_modes elif self.roi_pointer["mroi"][0]>0: mpointer = self.roi_pointer["mroi"] for col, mode in self.force_modes.items(): if col[0]<=mpointer[1]=self.fcount_mode: #print("f ", mode) #self.fcurrent_count=0 return mode
- Возьмите верх, вправо, внизу, слева от
MROI
Значение ключа наroi_boxes
Анкет Тогда
foce_modes
проверяется, если естьНет
или нет.- Разделите коробку ROI на части, равные
force_modes
Анкет По умолчанию использованиеПеремещать, нарисовать, стирай
Анкет - Создайте словарь с столбцом в качестве ключа и значения как имя режима.
- Установить
force_modes
атрибут.
- Разделите коробку ROI на части, равные
Иначе длина контуров на
MROI
больше 0, затем возьмите указатель.Проверка по каждому столбцам, режим на
force_modes
:- Если координата y указателя находится в диапазоне столбца для этого режима, установите, выполните проверку предыдущего и текущего режима.
Метод: выполнить режим
Я использовал всего 9 режимов. До сих пор, нарисуйте, стирайте, перемещаются, использовались цвет, но теперь для оставшихся режимов мы должны выполнить какое -то действие. Давайте определяем метод для этого тоже.
def perform_mode(self): if self.running_mode=="clear": self.canvas.clear() self.running_mode="move" if self.running_mode=="restart": self.take_average =True self.num_frames=0 self.running_mode="move" self.canvas.clear() if self.running_mode=="save": #cv2.imshow("canvas", self.canvas.canvas.astype(np.uint8)) cv2.imwrite("canvas.png", self.canvas.canvas.astype(np.uint8)) #cv2.destroyWindow("canvas") self.running_mode="move" if self.running_mode=="exit": self.key=27 if self.running_mode=="detect": self.running_mode="move" self.detector()
- Если режим работы ясен, то вызовите
clear ()
Методхолст
объект. - Если режим запуска перезапускается,
- Установить
брать
- Установить
num_frames
- Установить
rong_mode = "Move"
Анкет - Позвоните в
clear ()
Методхолст
объект.
- Установить
- Если запуск режима сохраняется:
- Сохраните текущий холст, нарисуйте на диск и измените режим работы для перемещения.
- Если режим запуска выходит, тогда измените текущую ключ на 27.
- Если режим запуска обнаруживается, тогда измените метод работы с запуском и метод детектора вызовов.
Метод: детектор
Метод, который принимает текущий холст и выполняет обнаружение с использованием модели обнаружения, такой как OCR. На данный момент я использую Tesseract.
def detector(self): img = self.canvas.canvas.astype(np.uint8) op = pytesseract.image_to_string(img, lang="eng", nice="1") print("Detected: ", op)
- Установить PytesserAct легко. Но вы должны установить еще один файл установки в Windows, чтобы он запустил.
- Если у вас есть какие -либо проблемы с установкой PytesserAct, оставьте мне почту или комментарий.
Метод: получить окно
Метод, который связывает окно VUI и холста, чтобы они выглядели как один и возвращают новое окно.
def get_window(self, canvas, vui): final_window = vui.copy() canvas_cpy = canvas.copy() vshape = vui.shape cshape = canvas.shape #print(self.running_mode) if self.running_mode == "color": # get part where color lies and make those part of canvas_bg black cp = self.vui.mode_pos[self.running_mode] canvas[:self.vui.dd_part[1]-self.vui.vui_part, cp[0]:cp[1]] = vui[self.vui.vui_part:self.vui.dd_part[1], cp[0]:cp[1]] final_window[self.vui.vui_part:, :] = canvas cp = self.roi_pointer["droi"] cp = (cp[1], cp[0]+self.vui.vui_part) point = self.canvas.point cv2.circle(final_window, cp, point[0], self.canvas.pointer_color, point[1]) return final_window
- Простой индексации будет достаточно для выполнения этой операции.
- Возьмите окно Canvas (не сами холст) и окно Vui.
- Если режим работы является цветным, то выпадается, чтобы показать.
- Подготовьте индексы там только раскрывающееся цвет.
- Назначьте холст частям ниже
vui_part
из VUI, потому что эти части включают значки. - Наконец -то нарисуйте указатель на новом окне. Это не обязательно, хотя.
- Вернуть последнее окно.
Метод: сделать прямоугольники
Метод создания прямоугольников на клоне рамы, чтобы мы могли заметить, где наш палец движется перед камерой. Также этот метод добавит текст в каждый прямоугольник.
def make_rectangles(self, clone): # make rectangle for everything, add text on middle of it for rname, box in self.roi_boxes.items(): cv2.putText(clone, f"Curr. Mode: {self.running_mode}", (400, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) top, right, bottom, left = box mid = int((top+bottom)/2), int((left+right)/2) if rname == "droi": cv2.rectangle(clone, (left, top), (right, bottom), (0, 255, 0), 2) cv2.putText(clone, rname, (mid[1], mid[0]), cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 2) if rname == "mroi": cv2.rectangle(clone, (left, top), (int((left + right)/3), bottom), (0, 255, 0), 2) cv2.rectangle(clone, (int((left + right)/3), top), (2*int((left + right)/3), bottom), (0, 255, 0), 2) cv2.rectangle(clone, (2*int((left + right)/3), top), (right, bottom), (0, 255, 0), 2) cv2.putText(clone, str("Mv"), (int((right)/1), int((top+bottom)/2)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.putText(clone, str("Dr"), (int((left + right)/3), int((top+bottom)/2)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) cv2.putText(clone, str("Er"), (2*int((left + right)/3), int((top+bottom)/2)), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2) if rname == "vroi": gb_indices = int((left-right)/len(self.vui.modes)) gb_indices = np.arange(right, left, gb_indices) gb_indices[-1] = gb_indices[-1]+1 for i in range(len(gb_indices)-1): _gleft = gb_indices[i] _gright = gb_indices[i+1] cv2.rectangle(clone, (_gleft, top), (_gright, bottom), (255, 0, 255), 3) cv2.putText(clone, self.vui.modes[i][:2], (_gleft+2, int((top+bottom)/2)), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (255, 0, 255), 2) return clone
Простая операция происходит здесь.
- Для каждого ROI:
- Возьмите верх, вправо, внизу, влево, затем найдите в середине этой коробки.
- Нарисуйте прямоугольник для каждой рентабельности. И разделите прямоугольник на части, если это необходимо.
Тест: Контурное письмо
Объедините все и давайте попробуем заставить нашу систему работать. Мы создадим и объект класса Контурное написание
и назовите его main ()
метод
gw = ContourWriting(avg_frames=150, aweight=0.9) gw.main()
Вывод должен выглядеть как видео ниже.
Я настоятельно рекомендую вам настроить ваш max_count
Переменные вокруг класса VUI и класса контурного написания, если вы работаете над более шумной комнатой. Выше видео было снято на ночь, поэтому на кадрах много мерцающих точек, но настройка значений подсчета работает нормально.
Тест пройден.
Окончательно
Окончательная версия кода должна выглядеть ниже.
class VUI: """ A class for visual user interface. Recommended to use default parameters. """ def __init__(self, icons_dir="icons/", window_size=(525, 700, 3), vui_part=20, max_count=15): """ icons_dir: directory to use icons from. window_size: size of an vui window. Recommended to use equal of final window. vui_part: How much % of rows from top will be used to stack icons? """ self.idir = icons_dir self.window = np.zeros(window_size).astype(np.uint8) self.size = window_size self.vui_part = int(vui_part/100 * self.size[0]) self.dd_part = (self.vui_part, int(50/100 * self.size[0])) self.modes = [fname.split(".")[0] for fname in os.listdir(self.idir)] self.icon_size = (int(self.size[1]/len(self.modes)), self.vui_part) # c, r self.current_icons = [] self.anim_scale = 0.5 self.anim_color = [5, 15, 2] self.prev_mode = "move" self.current_mode = "move" self.running_mode = None self.hover=None self.mode_count = 1 self.max_count = max_count self.color_count = 1 self.max_color = 5 self.current_pointer = (100, 100) self.canvas_pointer = None self.draw_color = (0, 0, 255) self.previous_color = (0, 0, 255) self.current_color = (0, 0, 255) self.pointer_color = (100, 200, 200) self.point = (10, -3) self.colors=None self.icons = self.prepare_icons() self.get_window() def prepare_icons(self): """ A method to prepare icons on initial frame. Method sets 4 new attributes. cols: List to store (y1, y2) of icon. icon_position: Dictionary to store (y1, y2) as key and corresponding image as value current_icons: A dictionary initialized with initial icons. Changed on every frame when cursor lies above it. mode_pos: Mode as key and its icon's (y1, y2) as value. """ icons = [] cols = np.linspace(0, self.size[1]-1, len(self.modes)+1).astype(np.int64) cols = [(cols[i], cols[i+1]) for i in range(len(cols)-1)] icon_pos = {} mode_pos = {} for i, image_name in enumerate(os.listdir(self.idir)): img = cv2.imread(self.idir+image_name) img = cv2.resize(img, (cols[i][1]-cols[i][0], self.vui_part)) icon_pos[cols[i]] = img mode_pos[self.modes[i]] = cols[i] self.cols = cols self.icon_position = icon_pos self.current_icons = icon_pos self.mode_pos = mode_pos def set_colors(self, col=None, new_colors=None): """ A method to set colors when pointer lies above color icon. Initially used subset of {Red, Green, Blue} col:- column where current pointer lies. new_colors:- If to use other colors. Method returns list of available colors on dropdown menu. Changes the draw color, pointer color upon condition meet. """ # earlier pointer was clipped within the vui pointer = self.canvas_pointer pointer = (pointer[1], self.vui_part+ pointer[0]) if new_colors is None: r = np.array([0, 0, 255]) g = np.array([0, 255, 0]) b = np.array([255, 0, 0]) colors = [r, g, b] colors_new = [colors[i]+colors[i+1] for i in range(len(colors)-1)] colors.extend(colors_new) self.colors = colors else: self.colors = new_colors rows = np.linspace(self.dd_part[0], self.dd_part[1], len(self.colors)+1).astype(np.int64) rows = [(rows[i], rows[i+1]) for i in range(len(rows)-1)] self.color_pos = {} for row, color in zip(rows, colors): self.color_pos[row] = color if row[0]<=pointer[1]=self.max_color: self.draw_color=self.current_color self.pointer_color = (np.abs(np.array([200, 200, 100])-color).tolist()) self.current_window[row[0]:row[1], col[0]:col[1]] = color return self.colors def get_window(self): """ A method to return a VUI window upon called. Sets pointer on VUI canvas. """ self.current_window = np.zeros_like(self.window).astype(np.uint8) for col, img in self.current_icons.items(): self.current_window[:self.vui_part, col[0]:col[1]] = img if self.running_mode == "color": self.set_colors(col=self.cols[self.modes.index("color")]) if self.current_pointer is not None and self.current_pointer[0]>0: cv2.circle(self.current_window, (self.current_pointer[1], self.current_pointer[0]), self.point[0], self.pointer_color, self.point[1]) return self.current_window def update_vui(self, pointer=(100, 100), cpointer=(10, 100)): """ A method to update the entire VUI properties and state. pointer: Current pointer on VUI part. cpointer: Current pointer on Canvas. cpointer is useful when working with color mode. """ self.current_pointer = pointer self.canvas_pointer = cpointer #print(pointer, canvas_pointer) current_icons = {} self.hover=None if pointer[0]<=self.vui_part: for col, mode in zip(self.cols, self.modes): icon = self.icon_position[col].copy() ishape = icon.shape #print(mode) if col[0]
1: rd = int((r - ishape[0])/2) cd = int((c - ishape[1])/2) zeros_icon[:, :] = icon[rd:ishape[0]+rd, cd:ishape[1]+cd] else: rd = int((ishape[0] - r)/2) cd = int((ishape[1] - c)/2) rdd, cdd = 0, 0 if ishape[0]-rd-rd > r: rdd=1 if ishape[1]-cd-cd > c: cdd=1 #print(icon.shape, ishape, rd, abs(r-rd), cd, abs(c-cd)) zeros_icon[rd:ishape[0]-rd-rdd, cd:ishape[1]-cd-cdd] = icon[::] current_icons[col] = zeros_icon.astype(np.uint8) + np.uint8(np.array(self.anim_color)*self.mode_count) if self.prev_mode == self.current_mode: self.mode_count += 1 else: self.prev_mode = self.current_mode self.mode_count = 1 if self.mode_count >= self.max_count: self.running_mode = self.current_mode self.mode_count = 1 self.hover = True else: current_icons[col] = icon self.current_icons = current_icons else: self.mode_count = 1 return self.get_window() # vui = VUI() # #show(vui.window) # vui.update_vui() # vui.update_vui() # vui.update_vui(pointer=(200, 100)) # vui.update_vui(pointer=(200, 100)) class Canvas: def __init__(self, window_size=(525, 700, 3), draw_color=(100, 100, 100), pointer_color=(0, 0, 0), bg_color=(25, 25, 25), mode="move", point=(10, -3), vui=None, ssize=(300, 50, 3)): """ A method to initialize canvas. window_size: size of a canvas window. draw_color: drawing color in RGB. pointer_color: pointer color in RGB. bg_color: background color in RGB. mode: running mode. point: tuple of (pointer radius, thickness) vui: VUI object. ssize: Slider's size. """ self.size=window_size self.draw_color=draw_color self.pointer_color = pointer_color self.bg_color = bg_color self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+bg_color self.mode = mode self.pointer = None self.point = point self.current_window = self.window+self.canvas self.vui = vui self.ssize = ssize self.sregion = () def slider(self, size=(300, 30, 3), spoint=50, scolor=(100, 55, 100)): """ A method to change the pointer size by moving a slider. size: size of slider region. spoint: slider point, generally row position of pointer. scolor: slider color """ swidth=10 #swidth=int(5/50*spoint) #swidth = np.clip(swidth, 5, spoint) swindow=np.zeros(self.size).astype(np.uint8) swindow[:self.ssize[0], 0:self.ssize[1]] += np.uint8([255, 255, 255]) r1 = np.clip(spoint-swidth, swidth, self.ssize[0]-swidth) r2 = np.clip(spoint+swidth, swidth, self.ssize[0]-swidth) spoint = int(10/50 * spoint) #print(r1, r2, spoint) swindow[r1:r2, :self.ssize[1]] = scolor self.point=(spoint, self.point[1]) #cv2.imshow("slider", swindow.astype(np.uint8)) return swindow.astype(np.uint8) def clear(self): self.window = np.zeros(self.size, dtype=np.uint8) self.canvas= self.window.copy()+self.bg_color def update_window(self, mode, pointer=(400, 100)): """ mode: running mode pointer: where is pointer now? """ self.mode = mode self.vui.mode=mode self.pointer = pointer self.draw_color=self.vui.draw_color self.pointer_color = self.vui.pointer_color #self.pointer = (np.clip(self.vui.vui_part, pointer[0], self.size[0]), pointer[1]) #print("c", self.draw_color) swindow = np.zeros(self.size).astype(np.uint8) #print(pointer) if 0 10: if rname!="mroi": pshape = self.size if rname=="vroi": pshape = (self.vui.vui_part, self.vui.size[1]) if rname=="droi": # make it self.canvas.shape #pshape = (self.canvas_shape[0], self.canvas_shape[1]-self.canvas.ssize[1]) pshape=self.canvas_shape h = bottom - top l = left - right m = (int((m[0]/l)*pshape[1]), int((m[1]/h)*pshape[0])) else: m = (right+m[0], top+m[1]) self.roi_pointer[rname]=(m[1], m[0]) return clone def get_window(self, canvas, vui): final_window = vui.copy() canvas_cpy = canvas.copy() vshape = vui.shape cshape = canvas.shape #print(self.running_mode) if self.running_mode == "color": # get part where color lies and make those part of canvas_bg black cp = self.vui.mode_pos[self.running_mode] canvas[:self.vui.dd_part[1]-self.vui.vui_part, cp[0]:cp[1]] = vui[self.vui.vui_part:self.vui.dd_part[1], cp[0]:cp[1]] #show(canvas) #else:# final_window[self.vui.vui_part:, :] = canvas cp = self.roi_pointer["droi"] cp = (cp[1], cp[0]+self.vui.vui_part) point = self.canvas.point cv2.circle(final_window, cp, point[0], self.canvas.pointer_color, point[1]) return final_window def check_force_mode(self): top, right, bottom, left = self.roi_boxes["mroi"] if self.force_modes is None: x=np.linspace(right, left, 4).astype(np.int64) x=[(x[i],x[i+1]) for i in range(len(x)-1)] force_modes = ["move", "draw", "erase"] force_modes = {x[i]:force_modes[i] for i in range(len(x))} #print(force_modes) self.force_modes = force_modes elif self.roi_pointer["mroi"][0]>0: mpointer = self.roi_pointer["mroi"] for col, mode in self.force_modes.items(): if col[0]<=mpointer[1] =self.fcount_mode: #print("f ", mode) #self.fcurrent_count=0 return mode def detector(self): img = self.canvas.canvas.astype(np.uint8) op = pytesseract.image_to_string(img, lang="eng", nice="1") print("Detected: ", op) def perform_mode(self): if self.running_mode=="clear": self.canvas.clear() self.running_mode="move" if self.running_mode=="restart": self.take_average =True self.num_frames=0 self.running_mode="move" self.canvas.clear() if self.running_mode=="save": #cv2.imshow("canvas", self.canvas.canvas.astype(np.uint8)) cv2.imwrite("canvas.png", self.canvas.canvas.astype(np.uint8)) #cv2.destroyWindow("canvas") self.running_mode="move" if self.running_mode=="exit": self.key=27 if self.running_mode=="detect": self.running_mode="move" self.detector() def main(self): cam = cv2.VideoCapture(0) self.num_frames = 0 self.take_average=True while True: (ret, frame) = cam.read() if ret: self.key = cv2.waitKey(1) & 0xFF frame = imutils.resize(frame, width=self.size[1]) frame = cv2.flip(frame, 1) clone = frame.copy() gray = cv2.cvtColor(clone, cv2.COLOR_BGR2GRAY) self.set_grays(gray) self.size = frame.shape # if to take average and num frames on average taking is lesser than if self.num_frames self.roi_counts["vroi"] and fmode is not None: self.running_mode = fmode else: self.running_mode = self.vui.running_mode else: self.running_mode = fmode self.perform_mode() self.vui.running_mode=self.running_mode canvas = self.canvas.update_window(mode=self.running_mode, pointer=self.roi_pointer["droi"]).astype(np.uint8) final_window = self.get_window(canvas=canvas, vui=vui) cv2.imshow("CW", final_window) self.roi_pointer["vroi"] = (-1, -1) clone = self.make_rectangles(clone) cv2.imshow("Feed", clone) if self.key==27: break cam.release() cv2.destroyAllWindows() gw = ContourWriting(avg_frames=150, aweight=0.5) gw.main()
Я собираюсь указать на функции и недостатки этой системы.
Коды
Коды для текущей версии системы доступны по ссылке ниже, а если это не так, нажмите на комментарий или оставьте мне сообщение (LinkedIn, Twitter или напишите мне).
Функции
- Слайдер можно использовать для изменения размера указателя.
- Код рерсируется и производится ООП.
- Двойная рука может использоваться для изменения режимов рисования/перемещения/стирания указателя.
- Цвет можно выбрать из раскрывающейся моды. Мы можем добавить столько цвета, сколько хотим.
- Всего доступно 9 режимов.
Недостатки
Недостатки наиболее полезны, чтобы найти новую функцию в следующей версии. Ну вот их много.
- Система пригодна для использования по контуру чего угодно. Следовательно, необходимо использовать какой -то жест, подтверждающий модель.
- Также на видео выше можно увидеть, как указатель быстро движется по регионам VUI. Это не всегда приемлемо пользователям. Следовательно, это должно быть устранено в ближайшее время.
Идеи
- Что если мы сможем создать модель, которая может классифицировать жест, и мы можем определить определенный жест для определенного режима?
Что дальше?
Я постараюсь решить недостатки в следующий раз. Но мне интересно заставить эту систему работать на мобильных телефонах. В настоящее время я подумываю взять рамки с камеры устройств и обработать ее. Затем используйте какой -нибудь вызов API, чтобы получить эти кадры. Я мог бы использовать единство. Я очень рад попытаться использовать LSTM и другие алгоритмы глубокого обучения искусства, чтобы сделать эту систему более удивительной Но у меня нет доступа в Интернет (кроме сотовых данных), чтобы провести широкие исследования.
Почему бы не прочитать больше?
- Развертывание чат -бота RASA на Android с использованием Unity3D
- Система визуальной записи на основе жестов с использованием OpenCV и Python
- Логистическая регрессия с нуля
- Линейная регрессия с нуля
- Написание популярных оптимизаторов ML с нуля
- Поправить нейронную сеть вперед с нуля
- Сверточные нейронные сети с нуля
- Написание простого класса обработки изображений с нуля
- Наивный байеса для текстовых классификаций: царапина в рамки
- Простой OCR для рукописного текста Деванагари
Оригинал: “https://dev.to/qviper/gesture-based-visually-writing-system-web-app-4gd4”