arducam 에서는 B4010 (Arducam ToF Camera / Device Code : UC-981 Rev.B)
ToF 카메라 모듈 제어를 위한 python 예제코드를 제공하고 있다.
이 예제코드를 사용하려면 python 패키지 'ArducamDepthCamera' 를 설치해야 한다.
python 코드가 어떻게 작성되어 있는지 궁금하여, ArducamDepthCamera 를 열어보려 했더니
특이한 이름의 .so 파일과, 처음보는 파일들이 존재하고 있다.
root@raspberrypi:~# python3
Python 3.11.2 (main, Sep 14 2024, 03:00:30) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ArducamDepthCamera as ac
>>> print(ac.__file__)
/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera/__init__.py
>>>
root@raspberrypi:/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera# ls -l
total 492
-rw-r--r-- 1 root root 469817 Mar 19 11:43 ArducamDepthCamera.cpython-311-aarch64-linux-gnu.so
-rw-r--r-- 1 root root 21808 Mar 19 11:43 ArducamDepthCamera.pyi
-rw-r--r-- 1 root root 128 Mar 19 11:43 __init__.py
drwxr-xr-x 2 root root 4096 Mar 19 14:23 __pycache__
root@raspberrypi:/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera#
또한 상위폴더에는 뭔가, 직접 사용하는듯한 so 파일들이 여럿 발견된다
root@raspberrypi:/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera# cd ..
root@raspberrypi:/usr/local/lib/python3.11/dist-packages# cd ArducamDepthCamera.libs/
root@raspberrypi:/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera.libs# ls -l
total 11240
-rw-r--r-- 1 root root 66617 Mar 19 11:43 libarducam_config_parser-7bca5332.so
-rw-r--r-- 1 root root 4836225 Mar 19 11:43 libarducam_evk_sdk-665d9580.so.1.0.4
-rw-r--r-- 1 root root 277833 Mar 19 11:43 libgomp-6eb3f1d0.so.1.0.0
-rwxr-xr-x 1 root root 6323865 Mar 19 11:43 libsi4c-50f152a4.so
root@raspberrypi:/usr/local/lib/python3.11/dist-packages/ArducamDepthCamera.libs#
python 에서 so 를 가져다 쓸수 있는 구조인듯 한데, 컨셉을 파악해본다.
먼저 pycache 는 무시
내부에 있는건 python bytecode (.pyc) 로, init.py 를 그대로 빌드한 것이다.
## pycdc decompiler 로 확인 (cf. uncompyle6 / decompyle3 는 python 3.11 미지원)
root@raspberrypi:~/pycdc/build# ./pycdc /usr/local/lib/python3.11/dist-packages/ArducamDepthCamera/__pycache__/__init__.cpython-311.pyc
# Source Generated with Decompyle++
# File: __init__.cpython-311.pyc (Python 3.11)
from ArducamDepthCamera import *
from ArducamDepthCamera import __version__ as __tof_version__
__version__ = __tof_version__
root@raspberrypi:~/pycdc/build#
다음은 pyi 파일
이건 pip install 시 생성된 타입정보 파일이라고 한다.
ArducamDepthCamera.pyi this file is auto generated? or python source code written by human?
ChatGPT의 말:
The ArducamDepthCamera.pyi file is a stub file, typically used for type hinting in Python. It is usually auto-generated but can also be written manually by a developer.
직접 stubgen 으로 생성해보았다. --> pyi 파일도 무시
root@user-virtual-machine:~/cython2# pip install mypy
...
root@user-virtual-machine:~/cython2# stubgen -m mymodule
Processed 1 modules
Generated out/mymodule.pyi
root@user-virtual-machine:~/cython2# cd out/
root@user-virtual-machine:~/cython2/out# ls
mymodule.pyi
root@user-virtual-machine:~/cython2/out#
root@user-virtual-machine:~/cython2/out# cat mymodule.pyi
import _cython_3_0_12
PI: float
__test__: dict
add: _cython_3_0_12.cython_function_or_method
divide: _cython_3_0_12.cython_function_or_method
multiply: _cython_3_0_12.cython_function_or_method
subtract: _cython_3_0_12.cython_function_or_method
root@user-virtual-machine:~/cython2/out#
그럼 남은 파일은 특이한 이름을 가진, ArducamDepthCamera.cpython-311-aarch64-linux-gnu.so 이다.
먼저 gotcha 가 한가지 있다.
cython 과 CPython 은 다른것이다.
따라서 .so 이름에 cpython 이 붙는건, cython 으로 생성된 .so 라는 강한 심증을 가질수 있다.
그렇다면, 다음 2가지 테스트로 확인 과정을 수행하였다.
1) cython 으로 .so 생성, python 테스트 프로그램에서 import
## .so 로 만들 python 스크립트
root@user-virtual-machine:~/cython# cat mymodule.pyx
# mymodule.pyx
# Function to add two numbers
def add(int a, int b):
return a + b
# Function to subtract two numbers
def subtract(int a, int b):
return a - b
# Function to multiply two numbers
def multiply(int a, int b):
return a * b
# Function to divide two numbers
def divide(int a, int b):
if b != 0:
return a / b
else:
return "Cannot divide by zero"
# A constant value
PI = 3.14159
## cythonize 를 호출할 스크립트
root@user-virtual-machine:~/cython# cat setup.py
from setuptools import setup
from Cython.Build import cythonize
setup(
ext_modules=cythonize("mymodule.pyx")
)
## setup.py 실행명령
root@user-virtual-machine:~/cython# cat run.sh
#!/bin/bash
python3 setup.py build_ext --inplace
## cython 으로 만든 .so 를 사용할 파이선 테스트 프로그램
root@user-virtual-machine:~/cython# cat main.py
# main.py
import mymodule
result_add = mymodule.add(5, 3)
print(f"Add : {result_add}")
cythonize 로 만들어진 CPython 확장모듈(.so) 을, 일반 파이선 프로그램에서 정상적으로 import 하는걸 확인
root@user-virtual-machine:~/cython# ./run.sh
Compiling mymodule.pyx because it changed.
[1/1] Cythonizing mymodule.pyx
/usr/local/lib/python3.10/dist-packages/Cython/Compiler/Main.py:381: FutureWarning: Cython directive 'language_level' not set, using '3str' for now (Py3). This has changed from earlier releases! File: /root/cython/mymodule.pyx
tree = Parsing.p_module(s, pxd, full_module_name)
running build_ext
building 'mymodule' extension
creating build
creating build/temp.linux-x86_64-3.10
x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.10 -c mymodule.c -o build/temp.linux-x86_64-3.10/mymodule.o
creating build/lib.linux-x86_64-3.10
x86_64-linux-gnu-gcc -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -g -fwrapv -O2 -Wl,-Bsymbolic-functions -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.10/mymodule.o -o build/lib.linux-x86_64-3.10/mymodule.cpython-310-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-3.10/mymodule.cpython-310-x86_64-linux-gnu.so ->
root@user-virtual-machine:~/cython#
root@user-virtual-machine:~/cython# ls
build main.py mymodule.c mymodule.cpython-310-x86_64-linux-gnu.so mymodule.pyx run.sh setup.py
root@user-virtual-machine:~/cython#
root@user-virtual-machine:~/cython# python3 main.py
Add : 8
root@user-virtual-machine:~/cython#
2) 1번 테스트 + cython 으로 생성된 .so 에서 ctypes 를 활용해 다른 .so 를 사용하도록 구성
## .so 로 만들 python 스크립트
root@user-virtual-machine:~/cython2# cat mymodule.pyx
# mymodule.pyx
import ctypes
sdk = ctypes.CDLL("./sdk/build/lib/libsdk.so")
# Function to add two numbers
def add(int a, int b):
sdk.main()
return a + b
# Function to subtract two numbers
def subtract(int a, int b):
return a - b
# Function to multiply two numbers
def multiply(int a, int b):
return a * b
# Function to divide two numbers
def divide(int a, int b):
if b != 0:
return a / b
else:
return "Cannot divide by zero"
# A constant value
PI = 3.14159
## 다른 .so (sdk)
root@user-virtual-machine:~/cython2# cd sdk
root@user-virtual-machine:~/cython2/sdk# ls
CMakeLists.txt main.c
root@user-virtual-machine:~/cython2/sdk# cat main.c
#include <stdio.h>
int main() {
printf("hello cython\n");
return 0;
}
root@user-virtual-machine:~/cython2/sdk#
root@user-virtual-machine:~/cython2/sdk# cat CMakeLists.txt
cmake_minimum_required(VERSION 3.10)
project(sdk)
add_library(sdk SHARED main.c)
set_target_properties(sdk PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
)
root@user-virtual-machine:~/cython2/sdk#
CPython 확장모듈(.so) 에서 정상적으로 libsdk.so 를 사용한걸 확인
root@user-virtual-machine:~/cython2# ./run.sh
Compiling mymodule.pyx because it changed.
[1/1] Cythonizing mymodule.pyx
/usr/local/lib/python3.10/dist-packages/Cython/Compiler/Main.py:381: FutureWarning: Cython directive 'language_level' not set, using '3str' for now (Py3). This has changed from earlier releases! File: /root/cython2/mymodule.pyx
tree = Parsing.p_module(s, pxd, full_module_name)
running build_ext
building 'mymodule' extension
creating build
creating build/temp.linux-x86_64-3.10
x86_64-linux-gnu-gcc -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -I/usr/include/python3.10 -c mymodule.c -o build/temp.linux-x86_64-3.10/mymodule.o
creating build/lib.linux-x86_64-3.10
x86_64-linux-gnu-gcc -shared -Wl,-O1 -Wl,-Bsymbolic-functions -Wl,-Bsymbolic-functions -g -fwrapv -O2 -Wl,-Bsymbolic-functions -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 build/temp.linux-x86_64-3.10/mymodule.o -o build/lib.linux-x86_64-3.10/mymodule.cpython-310-x86_64-linux-gnu.so
copying build/lib.linux-x86_64-3.10/mymodule.cpython-310-x86_64-linux-gnu.so ->
root@user-virtual-machine:~/cython2# ls
build main.py mymodule.c mymodule.cpython-310-x86_64-linux-gnu.so mymodule.pyx run.sh sdk setup.py
root@user-virtual-machine:~/cython2#
root@user-virtual-machine:~/cython2# python3 main.py
hello cython
Add : 8
root@user-virtual-machine:~/cython2#
ArducamDepthCamera 에서는 cython 과 ctypes 를 활용해
1) 효과적으로 python 예제 수행에 필요한 패키지를 제공함과 동시에
2) 패키지 내부코드는 감추는 구성을 사용하였다.
참고한 링크