admin管理员组

文章数量:1332339

I’m building an Android APK using Buildozer on Ubuntu (running in a virtual machine) for a personal project, using Python and Kivy for both the backend and frontend. However, my app crashes immediately upon launch when OpenCV is included. The crash logs show an ImportError related to a missing configuration file (config.py) in OpenCV. This issue occurs regardless of whether I use the opencv-contrib-python-headless or the regular opencv package.

requirements.txt:


appdirs==1.4.4
build==1.2.2.post1
buildozer==1.5.0
certifi==2024.8.30
charset-normalizer==3.4.0
colorama==0.4.6
Cython==0.29.33
distlib==0.3.9
docutils==0.21.2
filelock==3.16.1
idna==3.10
Jinja2==3.1.4
Kivy==2.3.0
Kivy-Garden==0.1.5
MarkupSafe==3.0.2
numpy==2.1.3
opencv-contrib-python-headless==4.10.0.84
packaging==24.2
pexpect==4.9.0
pillow==10.4.0
platformdirs==4.3.6
plyer==2.1.0
ptyprocess==0.7.0
Pygments==2.18.0
pyproject_hooks==1.2.0
requests==2.32.3
sh==1.14.3
toml==0.10.2
urllib3==2.2.3
virtualenv==20.27.1

LogCat:

2024-11-20 13:46:01.530  6584-6606  python                  .test.myapp                       I  ImportError: OpenCV loader: missing configuration file:` \['config.py'\].` Check OpenCV installation.
2024-11-20 13:46:01.530  6584-6606  python                  .test.myapp                       I  Python for android ended.
2024-11-20 13:46:01.578  6584-6608  libc                    .test.myapp                       A  FORTIFY: pthread_mutex_lock called on a destroyed mutex (0x7cf271402ab8)
2024-11-20 13:46:01.578  6584-6609  libc                    .test.myapp                       A  FORTIFY: pthread_mutex_lock called on a destroyed mutex (0x7cf271402ab8)
2024-11-20 13:46:01.578  6584-6608  libc                    .test.myapp                       A  Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 6608 (hwuiTask0), pid 6584 (SDLActivity)
2024-11-20 13:46:01.698   597-970   WindowManager           system_server                        I  WIN DEATH: Window{c44b65 u0 .test.myapp/.kivy.android.PythonActivity}
2024-11-20 13:46:01.699   597-1113  ActivityManager         system_server                        I  Process .test.myapp (pid 6584) has died: fg  TOP

python file imports and relevant code:


import os #Python module for operating system interactions

import numpy as np #Used for handling image data from OpenCV's numpy arrays


import cv2

from kivy.config import Config

Config.set('kivy', 'camera', 'opencv')





from kivy.app import App 

from kivy.lang import Builder #building files correctly

from kivy.uix.button import Button #for the buttons

from kivy.uix.boxlayout import BoxLayout # for the layout (there are different types of layouts in kivy this is just the most basic one)

from kivy.uix.floatlayout import FloatLayout # for non dynamic layout so the buttons dont move for camera page

from kivy.uix.screenmanager import ScreenManager, Screen #for the screen manager to track which screen is being shown 

from kivy.uix.label import Label # for the label like headers 

from kivy.uix.widget import Widget # adds widget for each of the classes

from kivy.uix.dropdown import DropDown # adds dropdown widgets

from kivy.graphics import Color, Ellipse, Rectangle # adds color to the circle

from kivy.graphics.texture import Texture #used for OpenCV image data

from kivy.core.window import Window # Sets the background color for app

from kivy.uix.popup import Popup #For dialog window for popups when needed

from kivy.uix.image import Image #Widgets for displaying selected images

from kivy.properties import NumericProperty, StringProperty # control text values

from kivy.utils import get_color_from_hex # color translating from hex to rgba

from plyer import filechooser #From plyer library for image selection

from PIL import Image as PILImage #Python Image Library

from PIL import ImageDraw, ImageFont #Python Image Library

from kivy.clock import Clock #Used to capture frames form OpenCV Camera



class CameraPage(Screen):`

    def __init__(self, **kwargs):

        super(CameraPage, self).__init__(**kwargs)

        self.cap = None  # OpenCV VideoCapture

        self.image_cache = None

        self.frame_event = None

        self.current_frame = None # Captures the current frame



    # Start OpenCV camera when user is in the camera page

    def on_enter(self, *args):

        self.start_opencv_camera()



    # Stop OpenCV camera when user exits the camera page

    def on_leave(self, *args):

        if self.cap:

            self.cap.release()  # Release the camera

            cv2.destroyAllWindows()

            print("Camera has been released.")



        if self.frame_event:

            self.frame_event.cancel()  # Cancel frame update



    # Start OpenCV camera

    def start_opencv_camera(self):

        self.cap = cv2.VideoCapture(0)  # Useing camera index 0 for default camera

        if not self.cap.isOpened():

            print("Error: Could not open OpenCV camera")

        else:

            print("OpenCV camera started.")

            # Schedule the update of camera frames by 30 FPS

            self.frame_event = Clock.schedule_interval(self.update_camera_feed, 1.0 / 30.0)



    # Update the Image widget with the camera feed

    def update_camera_feed(self, dt):

        if self.cap:

            ret, frame = self.cap.read()

            if ret:

                self.current_frame = frame  # Store the current frame

                # Convert BGR frame to RGB

                buf = cv2.flip(frame, 0).tostring()

                texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')

                texture.blit_buffer(buf, colorfmt='bgr', bufferfmt='ubyte')

                self.ids.camera_feed.texture = texture



    # Capture image using the current frame from OpenCV

    def capture_opencv_image(self):

        if self.current_frame is not None:

            # Convert OpenCV frame (BGR) to PIL Image (RGB)

            pil_image = PILImage.fromarray(cv2.cvtColor(self.current_frame, cv2.COLOR_BGR2RGB))



            # Save to cache directory

            cache_dir = './image_cache'

            if not os.path.exists(cache_dir):

                os.mkdir(cache_dir)

            cached_image_path = os.path.join(cache_dir, 'captured_image.png')

            pil_image.save(cached_image_path)

            self.image_cache = cached_image_path



            print(f"Image captured and cached at: {cached_image_path}")

        else:

            print("Error: No frame available to capture")



    # Function that opens gallery works for windows and should work for android

    def open_gallery(self, *args):

        # Opens the file chooser to select images

        filechooser.open_file(on_selection=self.display_image)



    # Display the selected image in a popup

    def display_image(self, selection):

        if selection:

            # Cache for the selected image

            self.image_cache = selection[0]

            # Create/open popup

            image_popup = ImagePopup()

            image_popup.ids.img.source = self.image_cache

            image_popup.open()



    # Cache the selected image

    def cache_image(self, instance):

        # Checks if cache location exists

        if self.image_cache:

            cache_dir = './image_cache'  # Cache directory

            if not os.path.exists(cache_dir):

                os.mkdir(cache_dir)  # Create cache location if does not existing



            # Select a location/path for the cached image

            cached_image_path = os.path.join(cache_dir, 'cached_image.png')

            try:

                with open(self.image_cache, 'rb') as source_file:

                    with open(cached_image_path, 'wb') as dest_file:

                        dest_file.write(source_file.read())



                print(f"Image cached at: {cached_image_path}")

            except Exception as e:

                print(f"Error caching the image: {e}")

        else:

            print("Error: No image selected for caching")



buildozer.spec:


`\[app\]`

# (str) Title of your application

title = My Application

# (str) Package name

package.name = myapp

# (str) Package domain (needed for android/ios packaging)

package.domain = .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

# (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 './'

# If you have videos or other assets in a directory like "assets" or "videos", add them here

source.include_patterns = assets/*,videos/*.mp4

# (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 = kivy, plyer, numpy, pillow, opencv-contrib-python-headless, requests

# (str) Custom source folders for requirements

# Sets custom source for any requirements with recipes

# requirements.source.kivy = ../../kivy

# (str) Presplash of the application

#presplash.filename = %(source.dir)s/data/presplash.png

# (str) Icon of the application

#icon.filename = %(source.dir)s/data/icon.png

# (list) Supported orientations

# Valid options are: landscape, portrait, portrait-reverse or landscape-reverse

orientation = portrait

# (list) List of service to declare

#services = NAME:ENTRYPOINT_TO_PY,NAME2:ENTRYPOINT2_TO_PY

# 

# OSX Specific

# 

# 

# author = © Copyright Info

# change the major version of python used by the app

osx.python_version = 3

# Kivy version to use

osx.kivy_version = 1.9.1

# 

# Android specific

# 

# (bool) Indicate if the application should be fullscreen or not

fullscreen = 0

# (string) Presplash background color (for android toolchain)

# Supported formats are: #RRGGBB #AARRGGBB or one of the following names:

# red, blue, green, black, white, gray, cyan, magenta, yellow, lightgray,

# darkgray, grey, lightgrey, darkgrey, aqua, fuchsia, lime, maroon, navy,

# olive, purple, silver, teal.

#android.presplash_color = #FFFFFF

# (string) Presplash animation using Lottie format.

# see / for examples and /lottie/

# for general documentation.

# Lottie files can be created using various tools, like Adobe After Effect or Synfig.

#android.presplash_lottie = "path/to/lottie/file.json"

# (str) Adaptive icon of the application (used if Android API level is 26+ at runtime)

#icon.adaptive_foreground.filename = %(source.dir)s/data/icon_fg.png
#icon.adaptive_background.filename = %(source.dir)s/data/icon_bg.png

# (list) Permissions

# (See  for all the supported syntaxes and properties)

android.permissions = CAMERA, READ_EXTERNAL_STORAGE, WRITE_EXTERNAL_STORAGE #(name=android.permission.WRITE_EXTERNAL_STORAGE;maxSdkVersion=18)

# (list) features (adds uses-feature -tags to manifest)

I’ve experimented with different OpenCV packages, such as opencv-python, opencv-headless, and opencv-contrib-python-headless. My goal is to eventually implement live photo capture functionality, similar to a barcode scanner, in my app. However, no matter which OpenCV package I use, the app crashes on launch with the same error related to missing configuration files for OpenCV.

The only thing that allows the app to run without crashing is commenting out the camera and image cache functions in my code, and removing the opencv dependencies from the buildozer.spec file. After doing this, the app launches and works as expected, except for the camera-related features.

本文标签: pythonAPK built with Buildozer crashes on launch due to OpenCV loader errorHow to resolveStack Overflow