프롤로그
매트랩에서 C언어에서 작성된 함수를 불러와서 사용해야하는 일을 알아보기 위해 이것저것 찾아보았다.
찾은 방법은 총 2가지이며 MEX와 DLL 방법 두 가지이다.
MEX는 MATLAB에서 빠르고 편리하게 사용할 수 있는 방면에 MATLAB API 를 사용하기 때문에 C언어에서 조금씩 변형된 함수들로 작성해야하고
DLL은 그냥 복잡했다.
MEX (MATLAB Executable)
MEX는 MATLAB에서 C, C++ 또는 Fortran 으로 작성된 프로그램을 실행할 수 있게 하는 기능으로, 이를 통해 MATLAB의 고수준 언어 기능과 C/C++ 또는 Fortran 의 저수준 언어 성능을 결합할 수 있다.
MEX 파일은 MATLAB 함수처럼 동작하며, MATLAB 데이터 타입과 직접적으로 상호작용할 수 있게 해주는 특수한 형태의 동적 링크 라이브러리이다.
이 방식은 MATLAB API를 통해 MATLAB 데이터 타입과 상호작용하여, 성능이 중요한 계산을 최적화 하거나 이미 존재하는 C/C++ 코드를 재사용하고자 할 때 매우 유용하다.
사용법은 아래와 같다.
매트랩 애드온 받기
매트랩 애드온 받기버튼을 누르고
MATLAB Support for MinGW-w64 C/C+/Fortan Compiler 를 설치한다.
파일 준비
C언어를 매트랩에서 사용할 것이기에 일단 임시적으로 파일을 생성하자.
//name - test.c
#include "mex.h"
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { //출력인자, 출력인자를 위한 배열, 입력인자, 입력인자를 위한 배열
// 입력 인자 확인
if(nrhs != 2) {
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:nrhs", "두 입력이 필요합니다.");
}
// 입력이 double 형식인지 확인
if(!mxIsDouble(prhs[0]) || !mxIsDouble(prhs[1])) {
mexErrMsgIdAndTxt("MyToolbox:arrayProduct:notDouble", "입력은 double이어야 합니다.");
}
// 입력 배열 받기
double *A = mxGetPr(prhs[0]);
double *B = mxGetPr(prhs[1]);
// 입력 배열의 크기 받기
mwSize n = mxGetNumberOfElements(prhs[0]);
// 출력 배열 생성
plhs[0] = mxCreateDoubleMatrix(1, n, mxREAL);
// 출력 배열에 데이터 쓰기
double *C = mxGetPr(plhs[0]);
for(mwSize i = 0; i < n; i++) {
C[i] = A[i] + B[i];
}
}
test.c라는 C언어 파일을 하나 생성한다. 매트랩에서 C언어를 사용하기 위해서는 매트랩의 API를 사용해야하므로, 기존에 알던 C언어와는 좀 다를 수가 있다.
위 코드는 1차원 배열 2개 입력받았을 때(매트랩 행렬) 두 행렬을 더해주는 기능이다.
그리고 test_mat.m 이라는 파일을 하나 생성해준다.
//test_map.m
A = [1, 2, 3];
B = [4, 5, 6];
C = test(A, B); % 'test'는 컴파일된 MEX 파일의 이름입니다.
disp(C)
이렇게 테스트 할 파일들은 준비되었다.
파일은 이렇게 준비되어야 한다.
이게 해보니까 파일 명이 막 OneDrive - xxx 이런식으로 공백이 있거나 한글이 있거나 하면 제대로 실행이 안 되니까 그냥 C:\ 에다가 matlab 폴더 생성하고 거기에다가 작성하기를 추천한다.
실행
명령창에 mex -setup 를 입력한다.
mex -setup
그렇다면 다른 C 컴파일러를 선택하라는데, MinGW64 Compiler (C) 를 선택해준다.
그리고 mex test.c 를 입력하여
mex test.c
위와 같이 성공적으로 완료되었습니다. 가 뜨면 정상이다.
폴더가 하나 생성되어있을 것이다.
여기에 이제 아까 작성한 test_mat.m 을 실행시키자
정상적으로 두 행렬의 덧셈 연산이 수행된 것을 알 수 있다.
동적 링크 라이브러리(DLL)
C언어로 작성된 함수를 동적 링크 라이브러리(DLL)로 컴파일 하고 MATLAB에서 이를 불러와서 사용할 수 있다.
Visual Studio 프로젝트 생성하기
여기서 새 프로젝트 만들기 버튼을 클릭하고
dll을 입력하고 "내보내기가 있는 동적 연결 라이브러리(DLL)을 선택해준다.
프로젝트 명은 아무거나 입력하는데, 나는 Project1을 입력하고, 제일 오른쪽 아래에 만들기 버튼을 클릭한다
만들어진 모습이다.
C 소스파일과 헤더파일 만들기
아까와 같은 기능을 하도록 설정을 할 것이다.
소스파일 우클릭 -> 추가 -> 새 항목 버튼을 클릭하고
test2.c를 입력하고 추가 버튼을 누른다
#include "test2.h"
__declspec(dllexport) void array_add(double* A, double* B, double* C, int n) {
for (int i = 0; i < n; ++i) {
C[i] = A[i] + B[i];
}
}
test2.c 에다가 위와 같이 입력한다.
입력 후 저장(ctrl+s)를 한다.
다음으로 헤더파일을 생성할 것 이다.
오른쪽 헤더파일 우클릭 -> 추가 -> 새항목을 클릭하고
test2.h 헤더파일을 생성한다.
그리고나서
//#pragma once
#ifdef __cplusplus
extern "C" {
#endif
__declspec(dllexport) void array_add(double* A, double* B, double* C, int n);
#ifdef __cplusplus
}
#endif
위 와 같이 입력한다.
//#pragma once 는 일단 주석처리 해놨는데, 나중에 오류떠서 주석처리 해놓은 것이다.(어떤 기능을 하는지는 따로 찾아보지는 않음)
입력이 다 되면 저장버튼을 누르고
이제 Project1을 우클릭해서 속성 버튼을 누른다
왼쪽에 C/C++ 부분에서 미리 컴파일된 헤더 부분을 클릭하고 나서 오른쪽 맨 위에 있는 미리 컴파일된 헤더 부분을
사용 에서 미리 컴파일된 헤더 사용 안 함으로 바꿔주고 적용 -> 확인 버튼을 누른다. (이거 안 하니까 오류남)
그 후에
위 사진과 같이 빌드 -> 솔루션 빌드 버튼을 클릭한다.
이렇게 뜨면 성공이다.
MATLAB 코드 작성하기
일단 매트랩을 실행시킨다.
파일 경로를 확인한다.
Project1/x64/Debug 안에 Project1.dll 이 있는지 확인하고, Project1 안에 방금 만든 test2.c 와 test2.h 가 있는지 확인한다.
그리고 이제 현재 폴더에다가 test2_mat.m 파일을 만든다
% MATLAB 스크립트: test2_mat.m
% DLL과 헤더 파일의 경로를 정의
dllPath = fullfile('C:', 'matlab', 'Project1', 'x64', 'Debug', 'Project1.dll');
headerPath = fullfile('C:', 'matlab', 'Project1', 'test2.h');
% 라이브러리가 이미 로드되었는지 확인
if not(libisloaded('Project1'))
[notfound, warnings] = loadlibrary(dllPath, headerPath);
if not(isempty(notfound))
error('DLL could not be found or loaded. Check the path and the name.');
end
if not(isempty(warnings))
disp(warnings);
end
end
% 입력 배열 생성
A = [1, 2, 3];
B = [4, 5, 6];
C = zeros(1, 3); % 결과를 저장할 배열
% libpointer를 사용하여 배열에 대한 포인터 생성
pA = libpointer('doublePtr', A);
pB = libpointer('doublePtr', B);
pC = libpointer('doublePtr', C);
% 라이브러리 함수 호출
if libisloaded('Project1')
calllib('Project1', 'array_add', pA, pB, pC, numel(A));
C = pC.Value; % 결과를 MATLAB 배열로 가져오기
else
error('The library is not loaded.');
end
% 결과 확인
disp('결과');
disp(C);
% 사용이 끝났으면 라이브러리 언로드
unloadlibrary('Project1');
dllpath와 headerPath 는 fullfile 을 이용하여 Project1.dll과 test2.h 가 위치하고 있는 파일 경로를 적는다.
그리고 실행을 시켜보면
결과가 제대로 나온 것을 확인할 수 있다.
Korean_Korea.949 저거 오류 뜨는 거 해결해보려고 했는데, 해결이 안 되고 딱히 중요한 것은 아니라서 그냥 냅뒀다.
DLL방식에서 중요한 것은 파일 이름, 파일 경로이다.
파일 명이라든지 프로젝트 명이 달라지더라도 위와 같이 사용하면 될 것이다.
에필로그
DLL만 하는데 6시간 걸렸다. 자꾸 어디서 오류가 뜨는지 파악하기도 힘들었고, 파일 경로부터;; 힘들긴했다.
Visual Studio 도 처음 써보는 게 컸다.
나중에 DLL와 MEX 사용방법에 대해 좀 더 찾아보겠다.
'스펙업 > 2024 winter-study' 카테고리의 다른 글
[MATLAB] 매트랩에서 파이썬 코드 실행하기 (AES) (0) | 2024.01.19 |
---|---|
[MATLAB] 매트랩에서 파이썬 코드 실행하기 (3DES) (0) | 2024.01.18 |
[매트랩] 이미지, 영상, 음성 데이터를 bit 데이터로 변환하기 (1) | 2024.01.15 |
[MATLAB] 매트랩으로 파일 불러오고 저장하기 (동영상, 사진, 음성) (0) | 2024.01.10 |