Search code examples
pythonopencvkivybuildozer

PY to APK conversion with kivy and buildozer - (ImportError: OpenCV loader: missing configuration file: ['config.py']. Check OpenCV installation )


I have converted a .py file into an apk file where the camera of the mobile device is used, for that I am using the kivy library, and compiling all this with buildozer in the pycharm editor

To compile the application, use the command:

buildozer -v android debug

the problem is that once installed on my mobile device, when opening the application it closes automatically, so to see the error use the log cat command

adb -s 1573273300008C8 logcat *:S python:D

and the error generated is the following:

(venv) acardenas@acardenas-VirtualBox:~/PycharmProjects/ArucoProject/bin$ adb -s 1573273300008C8 logcat *:S python:D
--------- beginning of main
06-24 02:50:24.781 21748 21817 I python  : Initializing Python for Android
06-24 02:50:24.781 21748 21817 I python  : Setting additional env vars from p4a_env_vars.txt
06-24 02:50:24.781 21748 21817 I python  : Changing directory to the one provided by ANDROID_ARGUMENT
06-24 02:50:24.781 21748 21817 I python  : /data/user/0/org.test.myapparuco/files/app
06-24 02:50:24.781 21748 21817 I python  : Preparing to initialize python
06-24 02:50:24.781 21748 21817 I python  : _python_bundle dir exists
06-24 02:50:24.781 21748 21817 I python  : calculated paths to be...
06-24 02:50:24.781 21748 21817 I python  : /data/user/0/org.test.myapparuco/files/app/_python_bundle/stdlib.zip:/data/user/0/org.test.myapparuco/files/app/_python_bundle/modules
06-24 02:50:24.782 21748 21817 I python  : set wchar paths...
06-24 02:50:24.849 21748 21817 I python  : Initialized python
06-24 02:50:24.849 21748 21817 I python  : AND: Init threads
06-24 02:50:24.850 21748 21817 I python  : testing python print redirection
06-24 02:50:24.852 21748 21817 I python  : Android path ['.', '/data/user/0/org.test.myapparuco/files/app/_python_bundle/stdlib.zip', '/data/user/0/org.test.myapparuco/files/app/_python_bundle/modules', '/data/user/0/org.test.myapparuco/files/app/_python_bundle/site-packages']
06-24 02:50:24.853 21748 21817 I python  : os.environ is environ({'PATH': '/product/bin:/apex/com.android.runtime/bin:/apex/com.android.art/bin:/system_ext/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin', 'ANDROID_BOOTLOGO': '1', 'ANDROID_ROOT': '/system', 'ANDROID_ASSETS': '/system/app', 'ANDROID_DATA': '/data', 'ANDROID_STORAGE': '/storage', 'ANDROID_ART_ROOT': '/apex/com.android.art', 'ANDROID_I18N_ROOT': '/apex/com.android.i18n', 'ANDROID_TZDATA_ROOT': '/apex/com.android.tzdata', 'EXTERNAL_STORAGE': '/sdcard', 'ASEC_MOUNTPOINT': '/mnt/asec', 'DOWNLOAD_CACHE': '/data/cache', 'BOOTCLASSPATH': '/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar:/apex/com.android.art/javalib/okhttp.jar:/apex/com.android.art/javalib/bouncycastle.jar:/apex/com.android.art/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/framework-graphics.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/apex/com.android.i18n/javalib/core-icu4j.jar:/system/framework/vivo-framework.jar:/system/framework/vivo-media.jar:/system/framework/framework-adapter.jar:/system/framework/soc-framework.jar:/system/framework/vivo-telephony-common.jar:/system/framework/vivo-vgcclient.jar:/system/framework/mediatek-telephony-base.jar:/system/framework/mediatek-telephony-common.jar:/system/framework/mediatek-common.jar:/system/framework/mediatek-framework.jar:/system/framework/mediatek-ims-common.jar:/system/framework/mediatek-ims-base.jar:/system/framework/mediatek-telecom-common.jar:/apex/com.android.appsearch/javalib/framework-appsearch.jar:/apex/com.android.conscrypt/javalib/conscrypt.jar:/apex/com.android.ipsec/javalib/android.net.ipsec.ike.jar:/apex/com.android.media/javalib/updatable-media.jar:/apex/com.android.mediaprovider/javalib/framework-mediaprovider.jar:/apex/com.android.os.statsd/javalib/framework-statsd.jar:/apex/com.android.permission/javalib/framework-permission.jar:/apex/com.android.permission/javalib/framework-permission-s.jar:/apex/com.android.scheduling/javalib/framework-scheduling.jar:/apex/com.android.sdkext/javalib/framework-sdkextensions.jar:/apex/com.android.tethering/javalib/framework-connectivity.jar:/apex/com.android.tethering/javalib/framework-tethering.jar:/apex/com.android.wifi/javalib/framework-wifi.jar', 'DEX2OATBOOTCLASSPATH': '/apex/com.android.art/javalib/core-oj.jar:/apex/com.android.art/javalib/core-libart.jar:/apex/com.android.art/javalib/okhttp.jar:/apex/com.android.art/javalib/bouncycastle.jar:/apex/com.android.art/javalib/apache-xml.jar:/system/framework/framework.jar:/system/framework/framework-graphics.jar:/system/framework/ext.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/apex/com.android.i18n/javalib/core-icu4j.jar:/system/framework/vivo-framework.jar:/system/framework/vivo-media.jar:/system/framework/framework-adapter.jar:/system/framework/soc-framework.jar:/system/framework/vivo-telephony-common.jar:/system/framework/vivo-vgcclient.jar:/system/framework/mediatek-telephony-base.jar:/system/framework/mediatek-telephony-common.jar:/system/framework/mediatek-common.jar:/system/framework/mediatek-framework.jar:/system/framework/mediatek-ims-common.jar:/system/framework/mediatek-ims-base.jar:/system/framework/mediatek-telecom-common.jar', 'SYSTEMSERVERCLASSPATH': '/system/framework/com.android.location.provider.jar:/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/vivo-services.jar:/system/framework/soc-services.jar:/system/framework/services-adapter.jar:/apex/com.android.appsearch/javalib/service-appsearch.jar:/apex/com.android.media/javalib/service-media-s.jar:/apex/com.android.permission/javalib/service-permission.jar', 'STANDALONE_SYSTEMSERVER_JARS': '', 'ANDROID_SOCKET_zygote_secondary': '21', 'ANDROID_SOCKET_usap_pool_secondary': '23', 'ANDROID_ENTRYPOINT': 'main.pyc', 'ANDROID_ARGUMENT': '/data/user/0/org.test.myapparuco/files/app', 'ANDROID_APP_PATH': '/data/user/0/or
06-24 02:50:24.853 21748 21817 I python  : Android kivy bootstrap done. __name__ is __main__
06-24 02:50:24.853 21748 21817 I python  : AND: Ran string
06-24 02:50:24.853 21748 21817 I python  : Run user program, change dir and execute entrypoint
06-24 02:50:25.049 21748 21817 I python  : Traceback (most recent call last):
06-24 02:50:25.049 21748 21817 I python  :   File "/home/acardenas/PycharmProjects/ArucoProject/.buildozer/android/app/main.py", line 1, in <module>
06-24 02:50:25.049 21748 21817 I python  :   File "/home/acardenas/PycharmProjects/ArucoProject/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/myappAruco/armeabi-v7a/cv2/__init__.py", line 181, in <module>
06-24 02:50:25.049 21748 21817 I python  :   File "/home/acardenas/PycharmProjects/ArucoProject/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/myappAruco/armeabi-v7a/cv2/__init__.py", line 111, in bootstrap
06-24 02:50:25.050 21748 21817 I python  :   File "/home/acardenas/PycharmProjects/ArucoProject/.buildozer/android/platform/build-armeabi-v7a/build/python-installs/myappAruco/armeabi-v7a/cv2/__init__.py", line 109, in load_first_config
06-24 02:50:25.050 21748 21817 I python  : ImportError: OpenCV loader: missing configuration file: ['config.py']. Check OpenCV installation.
06-24 02:50:25.050 21748 21817 I python  : Python for android ended.

It refers to the fact that I am missing the config.py file

Look for the config.py file and it has the following code, but I don't understand why it generates the previous error, I need help with that please.

import os

BINARIES_PATHS = [
    os.path.join(os.path.join(LOADER_DIR, '../../'), 'lib64')
] + BINARIES_PATHS

My project main.py:

import cv2
#import cv2.aruco as aruco
import numpy as np
import os # buscara todos los marcadores  
from kivy.app import App
from kivy.lang import Builder
from kivy.clock import Clock
from kivy.graphics.texture import Texture
from kivy.uix.camera import Camera
from kivy.uix.boxlayout import BoxLayout

Builder.load_string('''
<CameraClick>:
    orientation: 'vertical'
    Image:
        id: Image
        allow_stretch:True
        keep_ratio:True
        canvas.before:
            PushMatrix
            Rotate:
                angle: -90
                origin: self.center
        canvas.after:
            PopMatrix
    Button:
        text: 'Activar Deteccion de Aruco'
        size_hint_y: None
        height:'48dp'
        on_press: root.capture()

''')


class CameraClick(BoxLayout):

    def capture(self):
        self.augDics = self.loadAugImages("Markers")
        self.cam = Camera(play=True,resolution=(1280, 720))
        Clock.schedule_interval((self.update2, 1.0/30.0))

    def update2(self,dt):

        texture = self.cam.texture
        size = texture.size
        pixels = texture.pixels

        image_object = self.cam.export_as_image(scale=round((400 / int(self.cam.height)), 2))
        w, h = image_object.texture.size
        frame = np.frombuffer(image_object.texture.pixels, 'uint8').reshape(h, w, 4)

        arucoFound = self.FindArucoMarkers(frame)

        if len(arucoFound[0]) != 0:
            for bbox, id in zip(arucoFound[0], arucoFound[1]):
                if id == 10:
                    if int(id) in self.augDics.keys():
                        frame = self.augmentAruco(bbox, id, frame, self.augDics[int(id)])

        buffer = cv2.flip(frame, 0).tostring()
        texture1 = Texture.create(size =(frame.shape[1],frame.shape[0]),colorfmt='rgba')
        texture1.blit_buffer(buffer, colorfmt='rgba', bufferfmt='ubyte')
        self.ids['image'].texture = texture1

    def findArucoMarkers(self, frame, markerSize=6, totalMarkers=250, draw=True):
        # param img : basicamente es la imagen en donde se va a encontrar los arucos markers
        # param markersize : el tamaño de los marcadores
        # param totalmarkers : el limite maximo de marcadores posibles que componen en diccionario
        # param draw : es un flag que se usa para dibujar el cuadro delimitador al rededor de los marcadores detectados
        # return : Se devuelve cuadros delimitadores  y numeros de ID de marcadores detectados

        imgGray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # pasamos el color a blanco y negro
        #key = getattr(cv2.aruco, f'DICT_{markerSize}X{markerSize}_{totalMarkers}')
        #arucoDict = aruco.Dictionary_get(key)
        arucoDict = cv2.aruco.Dictionary_get(cv2.aruco.DICT_6X6_250)
        arucoParam = cv2.aruco.DetectorParameters_create()
        bboxs, ids, rejected = cv2.aruco.detectMarkers(imgGray, arucoDict, parameters=arucoParam)

        if (len(bboxs) != 0):
            cv2.aruco.drawDetectedMarkers(imgGray, bboxs)

        return [bboxs, ids]

    def loadAugImages(self, path):
          #param path : folder que almacena las imagenes de los marcadores con ID
          #Return : Se devuelve el diccionario con claves como ID and valores como la imagen aumentada
          myList = os.listdir(path)
          noOfMarkers=len(myList)
          print("Total de Marcadores Detectados:", noOfMarkers)
          augDics = {}
          for imgPath in myList:
                key = int(os.path.splitext(imgPath)[0])
                imgAug = cv2.imread(f'{path}/{imgPath}')
                imgAug = cv2.cvtColor(imgAug, cv2.COLOR_RGB2RGBA)
                augDics[key] = imgAug
          return augDics

    def augmentAruco(self, bbox, id, img, imgAug, drawId=True):
          #param bbox : son los 4 puntos de las esquinas de la caja
          #param id : es la identificacion del marcador en la caja correspondiente
          #param img :  la imagen final sobre la cual se va dibujar o suporponer
          #param imgAug :  es la imagen aumentada que va ser superpuesta en el marcador
          #param drawId: es para mostrar o dibujar la identificacion  de los marcadores detectados
          #return : Se devuelve la imagen  con la imagen de aumento superpuesta

          tl = (bbox[0][0][0], bbox[0][0][1])
          tr = (bbox[0][1][0], bbox[0][1][1])
          br = (bbox[0][2][0], bbox[0][2][1])
          bl = (bbox[0][3][0], bbox[0][3][1])
          print(tl)
          h, w, c = imgAug.shape

          pts1 = np.array([tl, tr, br, bl])
          pts2 = np.float32([[0,0],[w,0],[w,h],[0,h]])
          matrix, _ = cv2.findHomography(pts2,pts1)
          imgOut = cv2.warpPerspective(imgAug, matrix, (np.shape(img)[1], np.shape(img)[0]))
          cv2.fillConvexPoly(img, pts1.astype(int), (0, 0, 0))
          imgOut = img + imgOut
          #print(type(tl))
          #coor = (180,200)
          #org = (0,imgAug.shape[0])
          #if drawId:
              #cv2.putText(imgOut, str(id),  (int(pts1[0][0]), int(pts1[0][1])),cv2.FONT_HERSHEY_PLAIN, 2,(255,0,255),2 )
              #cv2.putText(imgOut, str(id),  (int(tl[0]), int(tl[1])),cv2.FONT_HERSHEY_PLAIN, 2,(255,0,255),2 )

          return imgOut

class TestCamera(App):
    def build(self):
        return CameraClick()

TestCamera().run()
cv2.destroyAllWindows()

versions:

plataform: ubuntu 22.04 LTS
python: 3.10

I add that I am working in a virtualvenv

cmake                 3.22.5
Cython                0.29.30
Kivy                  2.1.0
opencv-contrib-python 4.6.0.66
opencv-python         4.6.0.66

Could you tell me what I'm doing wrong or what I'm missing?


Solution

  • I found the problem, in the buildozer request, I was putting opencv-python and it should be just opencv