#python #opencv #kivy #initialization #kivymd
#питон #opencv #киви #инициализация #кивимд
Вопрос:
Это мой первый раз, когда я задаю здесь вопрос, так что, пожалуйста, потерпите меня. Я пытаюсь создать приложение для камеры, используя opencv и kivy. Мне удалось это сделать, но моя проблема в том, что камера вызывается сразу, даже если ее нет на нужном экране. Я подозреваю, что это из-за моего класса init в КивиКамере, но я не знаю никаких других методов для этого. Я хочу, чтобы камера инициализировалась только после нажатия кнопки.
вот мое главное приложение
from kivy.lang import Builder from kivymd.app import MDApp from kivymd.uix.list import OneLineIconListItem, IconLeftWidget from kivymd.uix.list import OneLineListItem, MDList from kivy.uix.screenmanager import ScreenManager, Screen from kivy.uix.scrollview import ScrollView from kivy.uix.image import Image from kivy.clock import Clock from kivy.graphics.texture import Texture import cv2 import random import string class OneLine(OneLineListItem): pass class Scr(ScrollView): pass class list(MDList): pass class MenuScreen(Screen): pass class CamScreen(Screen): # def show_cam(self): # self.capture = cv2.VideoCapture(0) def on_stop(self): self.capture.release() class SecondScreen(Screen): # def mama(self): # for i in range(20): # self.parent.parent.ids.container.add_widget( # OneLineListItem(text=f"Single-line item {i}") # ) def on_released(self): # items= # self.add_widget() for i in range(20): list = OneLine(text=f"dsafsd {i}", on_release= lambda list: print(list.text)) list.text = f"dsafsd {i}" # list.bind(on_release=lambda btn: self.dropdown.select(list.text)) self.ids['container'].add_widget(list) # pass class KivyCamera(Image): def __init__(self, capture = cv2.VideoCapture(0), fps=30, **kwargs): super(KivyCamera, self).__init__(**kwargs) self.capture = capture Clock.schedule_interval(self.update, 1.0/fps) def update(self, dt): ret, frame = self.capture.read() if ret: buf1 = cv2.flip(frame, 0) buf = buf1.tostring() image_texture =Texture.create( size = (frame.shape[1], frame.shape[0]), colorfmt='bgr' ) image_texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte') self.texture = image_texture sm = ScreenManager() sm.add_widget(MenuScreen(name='menu')) sm.add_widget(SecondScreen(name='second')) sm.add_widget(CamScreen(name='cam')) class Test(MDApp): def on_start(self): size= 0.5,0.5 # self.help.get_screen('cam').ids['cam'].add_widget(KivyCamera(size_hint = size, pos_hint = {"x":0.1,"top":1})) for i in range(20): self.help.get_screen('second').ids['container'].add_widget( OneLineListItem(text=f"Single-line item {i}") ) letters = string.ascii_lowercase random = random.choice(letters) def build(self): screen = Screen() self.help = Builder.load_file('tes.kv') screen.add_widget(self.help) return screen Test().run()
а вот файл kv
ScreenManager: MenuScreen: SecondScreen: CamScreen: lt;MenuScreengt;: name: 'menu' MDRectangleFlatButton: text: 'to second' pos_hint: {'center_x':0.1,'center_y':0.2} on_press: root.manager.current = 'second' MDRectangleFlatButton: text: 'to cam' pos_hint: {'center_x':0.1,'center_y':0.9} on_press: root.manager.current = 'cam' MDLabel: id:our_label text: " " pos_hint: {'center_x':0.5,'center_y':0.2} # on_parent: # app.samie() lt;SecondScreengt;: name: 'second' MDBoxLayout: orientaion: 'vertical' MDRectangleFlatButton: text: 'to menu' pos_hint: {'center_x':0.1,'center_y':0.95} on_press: root.manager.current = 'menu' # on_press: # app.please() # ScrollView: # root.on_released() # list.text = list.text "bobo" # root.manager.current = 'menu' # for i in range(4): print(f"bobo {i}") ScrollView: MDList: id:container # on_parent: # app.please() lt;CamScreengt;: name: 'cam' BoxLayout: orientation: 'vertical' KivyCamera: id: cam size_hint: 0.5,0.5 pos_hint: {"x":0.1,"top":1} MDRectangleFlatButton: text: 'show camera' pos_hint: {'center_x':0.1,'center_y':0.9} # on_parent: # app.show_cam()
Спасибо! Я действительно думаю, что мне просто нужно избавиться от init, но я не знаю другого способа
Ответ №1:
Вы можете запустить обновление камеры, используя on_enter()
метод Screen
. Попробуйте изменить __init__()
метод KivyCamera
на:
class KivyCamera(Image): def __init__(self, capture=cv2.VideoCapture(0), fps=30, **kwargs): super(KivyCamera, self).__init__(**kwargs) self.capture = capture self.fps = fps # Clock.schedule_interval(self.update, 1.0 / fps)
И добавить on_enter()
и on_leave()
методы к CamScreen
:
class CamScreen(Screen): # def show_cam(self): # self.capture = cv2.VideoCapture(0) def on_stop(self): self.capture.release() def on_enter(self, *args): cam = self.ids.cam self.clock_event = Clock.schedule_interval(cam.update, 1.0 / cam.fps) def on_leave(self, *args): self.clock_event.cancel()
На несвязанной ноте строки:
sm = ScreenManager() sm.add_widget(MenuScreen(name='menu')) sm.add_widget(SecondScreen(name='second')) sm.add_widget(CamScreen(name='cam'))
строим свой корень App
, но он никогда не используется. Эти линии могут быть устранены.
Линия:
self.help = Builder.load_file('tes.kv')
также создается один и тот же корень, и если нет какой-либо причины для создания другого Screen
, содержащего этот корень, вы можете упростить свой build()
метод до:
def build(self): self.help = Builder.load_file('tes.kv') return self.help
Комментарии:
1. Привет @john, спасибо, что мне удалось как-то заставить его работать с небольшими изменениями,
cam.fps
возникает ошибкаAttributeError: 'KivyCamera' object has no attribute 'fps'
, поэтому я просто жестко закодировал его до 30. Теперь я столкнулся с другой проблемой, когда я пытаюсь изменить экран, он все еще использует мою веб-камеру. Я попробовал использоватьcam.capture.release()
вместоself.clock_event.cancel()
внутреннегоdef on_leave
. Веб-камера сейчас закрывается, но когда вы возвращаетесь на экран камеры, на нем отображается только статическое изображение, может быть, обновление может помочь?2. обновление, я не заметил
self.fps = fps
строки, вот почему у меня возникла ошибка хе-хе 😀3. этот ответ не решает проблему, а именно, что видеокартинка инициализируется до создания
KivyCamera
объекта… потому что видеокартинка инициализируется во время определения этого класса, потому что она задана в качестве аргумента по умолчанию. эти выражения вычисляются во время определения. Я добавил ответ, который касается этого.
Ответ №2:
def __init__(self, capture = cv2.VideoCapture(0), fps=30, **kwargs):
Эта строка вызывает проблему.
Объект видеозаписи создается во время определения класса и его метода.
Ты, наверное, этого не хочешь.
Используйте capture=None
в списке параметров, а в теле __init__
вы должны проверить if capture is None: capture = cv2.VideoCapture...