Почему я получаю ошибку тайм-аута сокета в приложении kivy для Android ⇐ Python
-
Anonymous
Почему я получаю ошибку тайм-аута сокета в приложении kivy для Android
Я создаю приложение для обнаружения какого-либо объекта в реальном времени и определения того, когда он в последний раз перемещался. Я сделал обнаружение объектов Python и сделал его сервером, чтобы он отправлял кадры в мобильное приложение kivy, которое показывает видео в реальном времени с сервера и показывает, когда оно в последний раз перемещалось. Все работало, пока я не конвертировал это приложение в Android с помощью buildozer, оно выдает ошибку тайм-аута сервера. Вот серверная часть:
импортировать cv2 импортировать numpy как np импортировать ОС импортировать тензорный поток как tf импорт сокета импортный маринованный огурец структура импорта из object_detection.utils импортируйте label_map_util из object_detection.utils импортируйте Visualization_utils как viz_utils из object_detection.builders импортируйте model_builder из object_detection.utils импортировать config_util @tf.function защита обнаружения_fn (изображение): изображение, фигуры = обнаружение_модель.препроцесс (изображение) предсказание_дикт = обнаружение_модель.предсказание (изображение, фигуры) обнаружения =Detection_model.postprocess(prediction_dict, shape) обнаружение возврата server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) data_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) имя_хоста = сокет.getимя_хоста() хост_ip = сокет.gethostbyname(имя_хоста) print('IP-адрес хоста:', host_ip) порт = 777 порт2= 555 сокет_адрес = (IP-хост_хоста,порт) адрес_данных = (ip_хоста, порт2) server_socket.bind(адрес_сокета) data_socket.bind(адрес_данных) TF_RECORD_SCRIPT_NAME = 'generate_tfrecord.py' LABEL_MAP_NAME = 'label_map.pbtxt' пути = { 'CHECKPOINT_PATH': os.path.join('заврсени модели', 'модель jaje'), } файлы = { 'PIPELINE_CONFIG':os.path.join('заврсени модели', 'модель jaje', 'pipeline.config'), 'LABELMAP': os.path.join('zavrseni modeli', 'model jaje', 'label_map.pbtxt') } configs = config_util.get_configs_from_pipeline_file(files['PIPELINE_CONFIG']) Detection_model = model_builder.build(model_config=configs['model'], is_training=False) ckpt = tf.compat.v2.train.Checkpoint(модель=модель_обнаружения) ckpt.restore(os.path.join(paths['CHECKPOINT_PATH'], 'ckpt-31')).expect_partial() Category_index = label_map_util.create_category_index_from_labelmap (файлы['LABELMAP']) крышка = cv2.VideoCapture(0) ширина = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) высота = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) server_socket.listen(5) data_socket.listen(5) print("СЛУШИВАНИЕ: ",socket_address) print("СЛУШИВАНИЕ: ",data_address) client_socket, адрес = server_socket.accept() client_data, addr2 = data_socket.accept() print('ПОЛУЧИЛОСЬ СОЕДИНЕНИЕ ОТ:', адрес) print('ПОЛУЧИЛОСЬ СОЕДИНЕНИЕ ОТ:', addr2) ко=0 в то время как cap.isOpened(): ret, кадр = cap.read() image_np = np.array(frame) если image_np не имеет значения None: input_tensor = tf.convert_to_tensor(np.expand_dims(image_np, 0), dtype=tf.float32) обнаружения =Detect_fn(input_tensor) если обнаружение не имеет значения «Нет»: num_detections = int(detections.pop('num_detections')) обнаружения = {ключ: значение[0, :num_detections].numpy() для ключа, значение в обнаружении.items()} обнаружения['num_detections'] = num_detections обнаружения['detection_classes'] = обнаружения['detection_classes'].astype(np.int64) label_id_offset = 1 image_np_with_detections = image_np.copy() viz_utils.visualize_boxes_and_labels_on_image_array( image_np_with_detections, обнаружения['detection_boxes'], обнаружения['detection_classes'] + label_id_offset, обнаружения['detection_scores'], индекс_категории, use_normalized_coordinates=Истина, max_boxes_to_draw=1, min_score_thresh=.7, agnostic_mode = Ложь) a = Pickle.dumps(image_np_with_detections) сообщение = struct.pack("Q", len(a)) + a client_socket.sendall (сообщение) cv2.imshow("Обнаружения",image_np_with_detections) min_score_thresh = 0,7 порог = 0,5 max_scores = max(обнаружения['detection_scores']) для меня в диапазоне (num_detections): box = обнаружения['detection_boxes'] оценка = обнаружения['detection_scores'] если оценка > 0,7: ymin, xmin, ymax, xmax = box.tolist() pos = ymin + ymax + xmax + xmin print("POS:", pos) print("КО: ",ко) х = абс(поз-ко) распечатать(х) если х>0,05: print("Послато") client_data.send(str(5).encode()) ко = позиция еще : print("ниже послано") ко = позиция еще : client_data.send(str(0).encode()) если cv2.waitKey(10) & 0xFF == ord('q'): крышка.релиз() cv2.destroyAllWindows() client_socket.close() client_data.close() перерыв Вот клиент, сторона kivy:
из kivymd.app импортировать MDApp из kivymd.uix.boxlayout импортировать MDBoxLayout из kivymd.uix.button импортировать MDRaishedButton из kivy.uix.image импортировать изображение из kivymd.uix.label импорт MDLabel из kivy.clear импорт часов из kivy.graphics.texture импортировать текстуру с платформы импорта kivy импорт сокета импортировать cv2 импортный маринованный огурец структура импорта из даты и времени импорта даты и времени если платформа=="android": из android.permissions импортируйте request_permissions, Разрешение request_permissions([Разрешение.ИНТЕРНЕТ]) класс Ангелус (MDApp): защита сборки (сам): макет = MDBoxLayout (ориентация = «вертикальная») self.text= "ПОДКЛЮЧИТЬ" self.image = Изображение() макет.add_widget(self.image) self.button = MDRaishedButton() self.label = MDLabel(text=self.text, Adaptive_height = Истина, halign='центр', valign='средний') макет.add_widget(self.label) self.counter = 0 self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.data_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.host_name = socket.gethostname() #self.host_ip = socket.gethostbyname(self.host_name) self.host_ip = "192.168.56.1" self.port = 777 self.port2 = 555 self.client_socket.connect((self.host_ip, self.port)) self.data_socket.connect((self.host_ip, self.port2)) self.data = b"" self.payload_size = struct.calcsize("Q") Clock.schedule_interval(lambda dt: self.update_data(self.data_socket), 1/2) Clock.schedule_interval(lambda dt: self.update_frame(self.client_socket), 1.0/30.0) возвратный макет защита update_frame(self, dt): в то время как len(self.data) < self.payload_size: пакет = self.client_socket.recv(4 * 1024) если не пакет: сломать self.data += пакет package_msg_size = self.data[:self.payload_size] self.data = self.data[self.payload_size:] msg_size = struct.unpack("Q", package_msg_size)[0] в то время как len(self.data) < msg_size: self.data += self.client_socket.recv(4 * 1024) Frame_data = self.data[:msg_size] self.data = self.data[msg_size:] фрейм = Pickle.loads(frame_data) буфер = cv2.flip(frame, 0).tobytes() текстура = Texture.create(size=(frame.shape[1],frame.shape[0]), colorfmt='bgr') текстура.blit_buffer(buffer, colorfmt='bgr',ufferfmt='ubyte') self.image.texture = текстура защита update_data(self, dt): data2 = str(self.data_socket.recv(1024).decode()) self.counter= self.counter +1 печать(данные2[0]) сейчас = дата-время.сейчас() current_time = now.strftime("%H:%M:%S") если (self.counter==1): self.label.text="ПОДКЛЮЧЕНО" self.label.size_hint_x = Нет self.label.width = self.label.texture_size[0] данные2 = [] elif data2[0] == "5": self.label.text = "ПЕРЕМЕЩЕНО В: " + current_time self.label.size_hint_x = Нет self.label.width = self.label.texture_size[0] данные2 = [] Ангелус().запустить() а вот файл спецификации buildozer:
[приложение] # (str) Название вашего заявления титул = Ангелиус # (строка) Имя пакета package.name = Ангелиус # (str) Домен пакета (необходим для упаковки Android/iOS) пакет.домен = org.test # (str) Исходный код, в котором находится файл main.py исходный.каталог = . # (список) Исходные файлы для включения (оставьте пустым, чтобы включить все файлы) source.include_exts = py, png, jpg, kv, атлас # (список) Список включений с использованием сопоставления с образцом #source.include_patterns = assets/*,images/*.png # (список) Исходные файлы для исключения (оставьте пустым, чтобы ничего не исключать) #source.exclude_exts = спецификация # (список) Список каталогов для исключения (оставьте пустым, чтобы ничего не исключать) #source.exclude_dirs = тесты, bin, venv # (список) Список исключений с использованием сопоставления с образцом # Не добавляйте префикс './' #source.exclude_patterns = лицензия,изображения/*/*.jpg # (str) Управление версиями приложения (метод 1) версия = 0.1 # (str) Управление версиями приложения (метод 2) # version.regex = __version__ = ['"](.*)['"] # версия.имя_файла = %(source.dir)s/main.py # (список) Требования к заявке # через запятую, например. требования = sqlite3, киви требования = python3,kivy,kivymd,opencv,datetime # (str) Пользовательские исходные папки для требований # Устанавливает собственный источник для любых требований с рецептами # требования.source.kivy = ../../kivy # (str) Предварительная заставка приложения #presplash.filename = %(source.dir)s/data/presplash.png # (str) Значок приложения #icon.filename = %(source.dir)s/data/icon.png # (str) Поддерживаемая ориентация (одна из альбомной, сенсорной, книжной или всех) ориентация = портрет # (список) Список объявляемых услуг #services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY # # Специально для OSX # # # автор = © Информация об авторских правах # изменить основную версию Python, используемую приложением osx.python_version = 3 # Версия Kivy для использования osx.kivy_version = 1.9.1 # # Специально для Android # # (bool) Указывает, должно ли приложение быть полноэкранным или нет. полноэкранный режим = 0 # (строка) Цвет фона Presplash (для набора инструментов Android) # Поддерживаемые форматы: #RRGGBB #AARRGGBB или одно из следующих имен: # красный, синий, зеленый, черный, белый, серый, голубой, пурпурный, желтый, светло-серый, # темно-серый, серый, светло-серый, темно-серый, цвет морской волны, фуксия, салатовый, бордовый, темно-синий, # оливковый, фиолетовый, серебристый, бирюзовый. #android.presplash_color = #FFFFFF # (строка) Предварительная анимация в формате Lottie. # примеры см. на https://lottiefiles.com/ и на https://airbnb.design/lottie/. # для общей документации. # Файлы Lottie можно создавать с помощью различных инструментов, таких как Adobe After Effect или Synfig. #android.presplash_lottie = "путь/к/лотти/файлу.json" # (str) Адаптивная иконка приложения (используется, если уровень Android API 26+ во время выполнения) #icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png #icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png # (список) разрешений android.permissions = ИНТЕРНЕТ # (список) функций (добавляет теги use-feature в манифест) #android.features = android.hardware.usb.host Я использовал scrcpy и adb logcat -s python для просмотра журналов и выдал ошибку тайм-аута. Я также попробовал на двух разных телефонах одну и ту же ошибку, кстати, платформа — Android. Как вы можете видеть, IP и порты в порядке, и я также подумал, что в моем приложении kivy есть какая-то ошибка, поэтому я сделал одно тестовое приложение, которое просто подключает сокеты, и это была та же ошибка.
Я создаю приложение для обнаружения какого-либо объекта в реальном времени и определения того, когда он в последний раз перемещался. Я сделал обнаружение объектов Python и сделал его сервером, чтобы он отправлял кадры в мобильное приложение kivy, которое показывает видео в реальном времени с сервера и показывает, когда оно в последний раз перемещалось. Все работало, пока я не конвертировал это приложение в Android с помощью buildozer, оно выдает ошибку тайм-аута сервера. Вот серверная часть:
импортировать cv2 импортировать numpy как np импортировать ОС импортировать тензорный поток как tf импорт сокета импортный маринованный огурец структура импорта из object_detection.utils импортируйте label_map_util из object_detection.utils импортируйте Visualization_utils как viz_utils из object_detection.builders импортируйте model_builder из object_detection.utils импортировать config_util @tf.function защита обнаружения_fn (изображение): изображение, фигуры = обнаружение_модель.препроцесс (изображение) предсказание_дикт = обнаружение_модель.предсказание (изображение, фигуры) обнаружения =Detection_model.postprocess(prediction_dict, shape) обнаружение возврата server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) data_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM) имя_хоста = сокет.getимя_хоста() хост_ip = сокет.gethostbyname(имя_хоста) print('IP-адрес хоста:', host_ip) порт = 777 порт2= 555 сокет_адрес = (IP-хост_хоста,порт) адрес_данных = (ip_хоста, порт2) server_socket.bind(адрес_сокета) data_socket.bind(адрес_данных) TF_RECORD_SCRIPT_NAME = 'generate_tfrecord.py' LABEL_MAP_NAME = 'label_map.pbtxt' пути = { 'CHECKPOINT_PATH': os.path.join('заврсени модели', 'модель jaje'), } файлы = { 'PIPELINE_CONFIG':os.path.join('заврсени модели', 'модель jaje', 'pipeline.config'), 'LABELMAP': os.path.join('zavrseni modeli', 'model jaje', 'label_map.pbtxt') } configs = config_util.get_configs_from_pipeline_file(files['PIPELINE_CONFIG']) Detection_model = model_builder.build(model_config=configs['model'], is_training=False) ckpt = tf.compat.v2.train.Checkpoint(модель=модель_обнаружения) ckpt.restore(os.path.join(paths['CHECKPOINT_PATH'], 'ckpt-31')).expect_partial() Category_index = label_map_util.create_category_index_from_labelmap (файлы['LABELMAP']) крышка = cv2.VideoCapture(0) ширина = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) высота = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) server_socket.listen(5) data_socket.listen(5) print("СЛУШИВАНИЕ: ",socket_address) print("СЛУШИВАНИЕ: ",data_address) client_socket, адрес = server_socket.accept() client_data, addr2 = data_socket.accept() print('ПОЛУЧИЛОСЬ СОЕДИНЕНИЕ ОТ:', адрес) print('ПОЛУЧИЛОСЬ СОЕДИНЕНИЕ ОТ:', addr2) ко=0 в то время как cap.isOpened(): ret, кадр = cap.read() image_np = np.array(frame) если image_np не имеет значения None: input_tensor = tf.convert_to_tensor(np.expand_dims(image_np, 0), dtype=tf.float32) обнаружения =Detect_fn(input_tensor) если обнаружение не имеет значения «Нет»: num_detections = int(detections.pop('num_detections')) обнаружения = {ключ: значение[0, :num_detections].numpy() для ключа, значение в обнаружении.items()} обнаружения['num_detections'] = num_detections обнаружения['detection_classes'] = обнаружения['detection_classes'].astype(np.int64) label_id_offset = 1 image_np_with_detections = image_np.copy() viz_utils.visualize_boxes_and_labels_on_image_array( image_np_with_detections, обнаружения['detection_boxes'], обнаружения['detection_classes'] + label_id_offset, обнаружения['detection_scores'], индекс_категории, use_normalized_coordinates=Истина, max_boxes_to_draw=1, min_score_thresh=.7, agnostic_mode = Ложь) a = Pickle.dumps(image_np_with_detections) сообщение = struct.pack("Q", len(a)) + a client_socket.sendall (сообщение) cv2.imshow("Обнаружения",image_np_with_detections) min_score_thresh = 0,7 порог = 0,5 max_scores = max(обнаружения['detection_scores']) для меня в диапазоне (num_detections): box = обнаружения['detection_boxes'] оценка = обнаружения['detection_scores'] если оценка > 0,7: ymin, xmin, ymax, xmax = box.tolist() pos = ymin + ymax + xmax + xmin print("POS:", pos) print("КО: ",ко) х = абс(поз-ко) распечатать(х) если х>0,05: print("Послато") client_data.send(str(5).encode()) ко = позиция еще : print("ниже послано") ко = позиция еще : client_data.send(str(0).encode()) если cv2.waitKey(10) & 0xFF == ord('q'): крышка.релиз() cv2.destroyAllWindows() client_socket.close() client_data.close() перерыв Вот клиент, сторона kivy:
из kivymd.app импортировать MDApp из kivymd.uix.boxlayout импортировать MDBoxLayout из kivymd.uix.button импортировать MDRaishedButton из kivy.uix.image импортировать изображение из kivymd.uix.label импорт MDLabel из kivy.clear импорт часов из kivy.graphics.texture импортировать текстуру с платформы импорта kivy импорт сокета импортировать cv2 импортный маринованный огурец структура импорта из даты и времени импорта даты и времени если платформа=="android": из android.permissions импортируйте request_permissions, Разрешение request_permissions([Разрешение.ИНТЕРНЕТ]) класс Ангелус (MDApp): защита сборки (сам): макет = MDBoxLayout (ориентация = «вертикальная») self.text= "ПОДКЛЮЧИТЬ" self.image = Изображение() макет.add_widget(self.image) self.button = MDRaishedButton() self.label = MDLabel(text=self.text, Adaptive_height = Истина, halign='центр', valign='средний') макет.add_widget(self.label) self.counter = 0 self.client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.data_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.host_name = socket.gethostname() #self.host_ip = socket.gethostbyname(self.host_name) self.host_ip = "192.168.56.1" self.port = 777 self.port2 = 555 self.client_socket.connect((self.host_ip, self.port)) self.data_socket.connect((self.host_ip, self.port2)) self.data = b"" self.payload_size = struct.calcsize("Q") Clock.schedule_interval(lambda dt: self.update_data(self.data_socket), 1/2) Clock.schedule_interval(lambda dt: self.update_frame(self.client_socket), 1.0/30.0) возвратный макет защита update_frame(self, dt): в то время как len(self.data) < self.payload_size: пакет = self.client_socket.recv(4 * 1024) если не пакет: сломать self.data += пакет package_msg_size = self.data[:self.payload_size] self.data = self.data[self.payload_size:] msg_size = struct.unpack("Q", package_msg_size)[0] в то время как len(self.data) < msg_size: self.data += self.client_socket.recv(4 * 1024) Frame_data = self.data[:msg_size] self.data = self.data[msg_size:] фрейм = Pickle.loads(frame_data) буфер = cv2.flip(frame, 0).tobytes() текстура = Texture.create(size=(frame.shape[1],frame.shape[0]), colorfmt='bgr') текстура.blit_buffer(buffer, colorfmt='bgr',ufferfmt='ubyte') self.image.texture = текстура защита update_data(self, dt): data2 = str(self.data_socket.recv(1024).decode()) self.counter= self.counter +1 печать(данные2[0]) сейчас = дата-время.сейчас() current_time = now.strftime("%H:%M:%S") если (self.counter==1): self.label.text="ПОДКЛЮЧЕНО" self.label.size_hint_x = Нет self.label.width = self.label.texture_size[0] данные2 = [] elif data2[0] == "5": self.label.text = "ПЕРЕМЕЩЕНО В: " + current_time self.label.size_hint_x = Нет self.label.width = self.label.texture_size[0] данные2 = [] Ангелус().запустить() а вот файл спецификации buildozer:
[приложение] # (str) Название вашего заявления титул = Ангелиус # (строка) Имя пакета package.name = Ангелиус # (str) Домен пакета (необходим для упаковки Android/iOS) пакет.домен = org.test # (str) Исходный код, в котором находится файл main.py исходный.каталог = . # (список) Исходные файлы для включения (оставьте пустым, чтобы включить все файлы) source.include_exts = py, png, jpg, kv, атлас # (список) Список включений с использованием сопоставления с образцом #source.include_patterns = assets/*,images/*.png # (список) Исходные файлы для исключения (оставьте пустым, чтобы ничего не исключать) #source.exclude_exts = спецификация # (список) Список каталогов для исключения (оставьте пустым, чтобы ничего не исключать) #source.exclude_dirs = тесты, bin, venv # (список) Список исключений с использованием сопоставления с образцом # Не добавляйте префикс './' #source.exclude_patterns = лицензия,изображения/*/*.jpg # (str) Управление версиями приложения (метод 1) версия = 0.1 # (str) Управление версиями приложения (метод 2) # version.regex = __version__ = ['"](.*)['"] # версия.имя_файла = %(source.dir)s/main.py # (список) Требования к заявке # через запятую, например. требования = sqlite3, киви требования = python3,kivy,kivymd,opencv,datetime # (str) Пользовательские исходные папки для требований # Устанавливает собственный источник для любых требований с рецептами # требования.source.kivy = ../../kivy # (str) Предварительная заставка приложения #presplash.filename = %(source.dir)s/data/presplash.png # (str) Значок приложения #icon.filename = %(source.dir)s/data/icon.png # (str) Поддерживаемая ориентация (одна из альбомной, сенсорной, книжной или всех) ориентация = портрет # (список) Список объявляемых услуг #services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY # # Специально для OSX # # # автор = © Информация об авторских правах # изменить основную версию Python, используемую приложением osx.python_version = 3 # Версия Kivy для использования osx.kivy_version = 1.9.1 # # Специально для Android # # (bool) Указывает, должно ли приложение быть полноэкранным или нет. полноэкранный режим = 0 # (строка) Цвет фона Presplash (для набора инструментов Android) # Поддерживаемые форматы: #RRGGBB #AARRGGBB или одно из следующих имен: # красный, синий, зеленый, черный, белый, серый, голубой, пурпурный, желтый, светло-серый, # темно-серый, серый, светло-серый, темно-серый, цвет морской волны, фуксия, салатовый, бордовый, темно-синий, # оливковый, фиолетовый, серебристый, бирюзовый. #android.presplash_color = #FFFFFF # (строка) Предварительная анимация в формате Lottie. # примеры см. на https://lottiefiles.com/ и на https://airbnb.design/lottie/. # для общей документации. # Файлы Lottie можно создавать с помощью различных инструментов, таких как Adobe After Effect или Synfig. #android.presplash_lottie = "путь/к/лотти/файлу.json" # (str) Адаптивная иконка приложения (используется, если уровень Android API 26+ во время выполнения) #icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png #icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png # (список) разрешений android.permissions = ИНТЕРНЕТ # (список) функций (добавляет теги use-feature в манифест) #android.features = android.hardware.usb.host Я использовал scrcpy и adb logcat -s python для просмотра журналов и выдал ошибку тайм-аута. Я также попробовал на двух разных телефонах одну и ту же ошибку, кстати, платформа — Android. Как вы можете видеть, IP и порты в порядке, и я также подумал, что в моем приложении kivy есть какая-то ошибка, поэтому я сделал одно тестовое приложение, которое просто подключает сокеты, и это была та же ошибка.