2024. 8. 27. 11:19ㆍ프로그래밍
Python을 사용하다보면 어떠한 목적으로 C혹은 C++ 쪽에서 작성한 코드와 연동하여 사용할 필요가 있다.
이는 성능이슈로 C, C++를 사용하든, 아니면 python 코드의 경우 코드를 완벽하게 숨길 수 없어 숨겨야하는 핵심 코드가 있든...
아무튼 C, C++ 와 Python 간의 연동을 위한 ctypes 라는 모듈이 있는데, 기본적으로 C 로 작성된 코드들을
Python script에서 사용할 수 있도록 해준다.
자세히는 아래 url을 참고한다.
https://docs.python.org/ko/3/library/ctypes.html
아래는 C/C++ 모듈에서 JPG 파일을 읽은 후 디코딩한 RGB 이미지를 Python으로 전달해서
화면에 표시하기 위해서 PIL 모듈의 Image 객체를 만드는 예제이다.
이를 통해 Structure 자료구조를 python과 C 모듈에서 공유하고, C 모듈 함수 호출하는 것과
C/C++에서 잡은 byte array를 python에서 사용하는 것과 반대로 Python에서 잡은 byte array를 C/C++에 전달하는 것에 대해서 구체적인 예제를 작성하였다.
Python 코드
from ctypes import *
import Image, ImageTk # PIL module로 Python설치시 기본 설치되는 모듈은 아니고,
# jpg, png 등 각종 이미지 포맷과 이미지 관련 함수 제공
lib = cdll.LoadLibrary('C:/Devel/MyDLL/my.dll')
class ImageInfo(Structure) :
_fields_ = (
('_width', c_uint),
('_height', c_uint),
('_depth', c_uint),
('_name', c_char_p),
('_data', POINTER(c_ubyte)),
) # C 와 공유될 자료구조 선언
pImageInfo = POINTER(ImageInfo)
lib.getJpgImage.argtypes = [pImageInfo] # C모듈에서 제공되는 getJpgImage 함수 인자들의 type과
lib.getJpgImage.restype = c_int # return type
lib.freeJpgImage.argtypes = [pImageInfo] #
lib.freeJpgImage.restype = c_int #
imgInfo = ImageInfo()
fileName = "C:/Devel/Fisheye/Fisheye_ceil_down.jpg"
imgInfo._name jj= c_char_p(fileName)
lib.getJpgImage(imgInfo) # C모듈 getJpgImage 함수 호출
w,h,d = imgInfo._width, imgInfo._height, imgInfo._depth
print "original image - %d x %d (%d)" % (w,h,d)
# C모듈에서 만든 byte array인 imagInfo._data를 python array 변수형으로 변경하는 부분,
# Image 모듈에서는 (h, w, rgb) 3차원 array 형태여야함.
# (c_ubyte*3*w*h) 는 3차원 array 로 정의되는데 3*w*h 로 반대순서 유의
tmp = cast(imgInfo._data, POINTER(c_ubyte*3*w*h)).contents
oriImage = Image.frombuffer('RGB', (w, h), tmp, 'raw', 'RGB', 0, 1)
print "Image is created"
# Python에서 만든 byte array를 C모듈로 전달하고자할 경우
# 역시 cast를 이용하여 c_ubyte pointer를 전달하면 된다.
imgInfo2 = ImageInfo()
imgData = (w*h*d*ctypes.c_ubyte)()
imgInfo2._data = cast(imgData, POINTER(c_ubyte))
...
C++ 코드 - PythonWrap.h
#ifndef _PYTHON_WRAP_H_
#define _PYTHON_WRAP_H_
typedef struct _ImageInfo {
unsigned int _width;
unsigned int _height;
unsigned int _depth;
char* _name;
unsigned char* _data;
} ImageInfo, *pImageInfo;
#endif // _PYTHON_WRAP_H_
C++ 코드 - PythonWrap.cpp
extern "C" { // C++ 모듈이라도 Python 과는 C 함수로 공유되어야 한다.
__declspec(dllexport) int __cdecl getJpgImage(pImageInfo pInfo)
{
if (pInfo->_name == NULL) {
return -1;
}
unsigned char* data = jpgLoad(pInfo->_name, &pInfo->_width, &pInfo->_height);
if (data == NULL) {
return -1;
}
pInfo->_depth = 3; // always rgb 24bit
pInfo->_data = (unsigned char*) malloc(pInfo->_width*pInfo->_height*pInfo->_depth);
if (pInfo->_data == NULL) {
jpgFree();
return -1;
}
memcpy(pInfo->_data, data, pInfo->_width*pInfo->_height*pInfo->_depth);
return 0;
}
__declspec(dllexport) void __cdecl freeJpgImage(pImageInfo pInfo)
{
if (pInfo->_data) {
free (pInfo->_data);
pInfo->_data = NULL;
}
pInfo->_width = 0;
pInfo->_height = 0;
pInfo->_depth = 0;
}
}
Python쪽에서 ctypes.Structure에 pointer가 아닌 실제 array를 사용 예저는 아래와 같다. c_float*10 면 array가 되는데, 이때 혹시 c_float*(5*2) 처럼 * 연산을 통해 array 크기를 지정할 때, 꼭 괄호를 하든지 아니면 5*2*c_float 처럼 숫자를 앞에 적어야 1차원 array가 된다. c_float*5*2 처럼 괄호 없이 * 를 c_float같은 형 뒷쪽에 사용하면 2차원 array가 되니 조심해야한다. 이때 row와 column은 순서가 반대가 되는데, 즉 c_float*5*2 로 하면 row가 2 이고 column 이 5인 2차원 행렬이 된다.
# Python code
class TestStruct(Structure) :
_fields_ = [
('_a',c_int),
('_b',c_int),
('_c',c_float*(5*2)) #주의! c_float*5*2 하면 2차원 array
# 5*2*c_float 도 1차원, c_float*(5*2)도 1차원 array,
]
위에 대응되는 C나 C++ 코드는
// C, C++ 코드
struct TestStruct {
int _a;
int _b;
float _c[2*5];
};
Windows에서 DLL 방식으로 작성된 C++ 모듈의 예인데, Linux에서는 SO 형태로 작성된 모듈도 동일한 방식으로 연동될 수 있다.
Windows에서 주의점은 아래와 같은 모듈 정의 파일 my.def를 만들고 프로젝트 속성에서 링커 > 입력 > 모듈 정의 파일 에 지정해야 한다.
; my.def : Declares the module parameters for the DLL.
; LIBRARY "my"
EXPORTS
getJpgImage
freeJpgImage
'프로그래밍' 카테고리의 다른 글
OpenVINO int8 모델 변환 2024.5 버전 (0) | 2024.12.14 |
---|---|
C++ wchar unicode utf-8 utf-32 (0) | 2024.09.06 |
C 혹은 C++ 에서 실행중인 파일 경로 구하기 (0) | 2024.08.27 |
ChatGPT의 sample code 오류 (0) | 2023.03.08 |