I have created an object detection app that connects to a tello drone in python Kivy and I want to build an apk version with buidozer. The build was successful but whenever I run the app on my android phone, it terminates after the splash screen.
I will like to know what could be causing the problem that is not making my application run.
Below is my python code which I saved it as main.py.
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.button import Button
from djitellopy import tello
import cv2
import cvzone
from kivy.uix.image import Image
from kivy.clock import Clock
from kivy.graphics.texture import Texture
me = tello.Tello()
me.connect()
print(me.get_battery())
me.streamon()
class MainApp(App):
def build(self):
self.icon = "ss.png"
self.img1=Image(allow_stretch = True, keep_ratio=False)
self.video = BoxLayout(orientation='vertical',height='500dp',size_hint_y=None)
self.video.add_widget(self.img1)
Clock.schedule_interval(self.update, 1.0/33.0)
main_layout = BoxLayout(orientation = "vertical")
main_layout.add_widget(self.video)
buttons = [
["Start", "Stop"],
["Up","Down"],
["Backward", "Forward"],
["Left", "Right"],
["Rotate C", "Rotate AnC"]
]
for row in buttons:
h_layout = BoxLayout(height='70dp')
for label in row:
button = Button(
text = label, font_size=20, background_color="grey",
pos_hint={"center_x":0.5, "center_y": 0.5},
)
button.bind(on_press=self.on_button_press)
h_layout.add_widget(button)
main_layout.add_widget(h_layout)
return main_layout
def on_button_press(self, instance):
button_text = instance.text
self.speed = 50
self.lr, self.fb, self.ud, self.yv = 0, 0, 0, 0
if button_text == "Start": me.takeoff()
if button_text == "Stop": me.land()
if button_text == "Left": self.lr = -self.speed
elif button_text == "Right": self.lr = self.speed
if button_text == "Up": self.ud = self.speed
elif button_text == "Down": self.ud = -self.speed
if button_text == "Forward": self.fb = self.speed
elif button_text == "Backward": self.fb = -self.speed
if button_text == "RoRotate C": self.yv = self.speed
elif button_text == "RoRotate AnC": self.yv = -self.speed
self.vals = [self.lr, self.fb, self.ud, self.yv]
me.send_rc_control(self.vals[0], self.vals[1], self.vals[2], self.vals[3])
def update(self, dt):
self.img = me.get_frame_read().frame
self.thres = 0.55
self.nmsThres = 0.2
classNames = []
classFile = 'coco.names'
with open(classFile, 'rt') as f:
classNames = f.read().split('\n')
print(classNames)
configPath = 'ssd_mobilenet_v3_large_coco_2020_01_14.pbtxt'
weightsPath = "frozen_inference_graph.pb"
self.net = cv2.dnn_DetectionModel(weightsPath, configPath)
self.net.setInputSize(320, 320)
self.net.setInputScale(1.0 / 127.5)
self.net.setInputMean((127.5, 127.5, 127.5))
self.net.setInputSwapRB(True)
self.classIds, self.confs, self.bbox = self.net.detect(self.img, confThreshold=self.thres, nmsThreshold=self.nmsThres)
try:
for classId, conf, box in zip(self.classIds.flatten(), self.confs.flatten(), self.bbox):
cvzone.cornerRect(self.img, box)
cv2.putText(self.img, f'{classNames[classId - 1].upper()} {round(conf * 100, 2)}',
(box[0] + 10, box[1] + 30), cv2.FONT_HERSHEY_COMPLEX_SMALL,
1, (0, 255, 0), 2)
except:
pass
# convert it to texture
buf1 = cv2.flip(self.img, 0)
buf = buf1.tostring()
texture1 = Texture.create(size=(self.img.shape[1], self.img.shape[0]), colorfmt='bgr')
#if working on RASPBERRY PI, use colorfmt='rgba' here instead, but stick with "bgr" in blit_buffer.
texture1.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')
# display image from the texture
self.img1.texture = texture1
cv2.imshow("Image", self.img)
if __name__ == '__main__':
app = MainApp()
app.run()
And below is my buildozer.spec file:
[app]
# (str) Title of your application
title = ENSUC
# (str) Package name
package.name = myapp
# (str) Package domain (needed for android/ios packaging)
package.domain = org.test
# (str) Source code where the main.py live
source.dir = .
# (list) Source files to include (let empty to include all the files)
source.include_exts = py,png,jpg,kv,atlas,names,pb,pbtxt
# (list) List of inclusions using pattern matching
#source.include_patterns = assets/*,images/*.png
# (list) Source files to exclude (let empty to not exclude anything)
#source.exclude_exts = spec
# (list) List of directory to exclude (let empty to not exclude anything)
#source.exclude_dirs = tests, bin, venv
# (list) List of exclusions using pattern matching
# Do not prefix with './'
#source.exclude_patterns = license,images/*/*.jpg
# (str) Application versioning (method 1)
version = 0.1
# (str) Application versioning (method 2)
# version.regex = __version__ = ['"](.*)['"]
# version.filename = %(source.dir)s/main.py
# (list) Application requirements
# comma separated e.g. requirements = sqlite3,kivy
requirements = python3,Kivy==2.1.0,kivymd,pillow,djitellopy,cvzone
# (str) Custom source folders for requirements
# Sets custom source for any requirements with recipes
# requirements.source.kivy = ../../kivy
#ios.manifest.display_image_url =
# (str) URL pointing to a large icon (512x512px) to be used by iTunes
# This option should be defined along with `app_url` and `display_image_url` options.
#ios.manifest.full_size_image_url =
[buildozer]
# (int) Log level (0 = error only, 1 = info, 2 = debug (with command output))
log_level = 2
# (int) Display warning if buildozer is run as root (0 = False, 1 = True)
warn_on_root = 1
# (str) Path to build artifact storage, absolute or relative to spec file
# build_dir = ./.buildozer
# (str) Path to build output (i.e. .apk, .aab, .ipa) storage
# bin_dir = ./bin
# -----------------------------------------------------------------------------
# List as sections
#
# You can define all the "list" as [section:key].
# Each line will be considered as a option to the list.
# Let's take [app] / source.exclude_patterns.
# Instead of doing:
#
#[app]
#source.exclude_patterns = license,data/audio/*.wav,data/images/original/*
#
# This can be translated into:
#
#[app:source.exclude_patterns]
#license
#data/audio/*.wav
#data/images/original/*
#
# -----------------------------------------------------------------------------
# Profiles
#
# You can extend section / key with a profile
# For example, you want to deploy a demo version of your application without
# HD content. You could first change the title to add "(demo)" in the name
# and extend the excluded directories to remove the HD content.
#
#[app@demo]
#title = My Application (demo)
#
#[app:source.exclude_patterns@demo]
#images/hd/*
#
# Then, invoke the command line with the "demo" profile:
#
#buildozer --profile demo android debug
I have a coco.names file, frozeninferencegraph.pb and ssdmobilenet.pbtxt which I imported into my main.py.