목차
- 목차
- 1. mediapipe
- 2. ipc
- 3. 참고
1. mediapipe
1.1. opencv camera
1
2
3
4
5
6
7
8
9
10
11
cap = cv2.VideoCapture(0)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print('original size: %d, %d' % (width, height))
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width/3)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height/3)
width = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
print('changed size: %d, %d' % (width, height))
1.2. mediapipe index
1.2.1. MAX_NUM_HANDS
- 감지할 손의 최대 개수
1
2
3
4
5
with mp_hands.Hands(
model_complexity=0,
max_num_hands=1,
min_detection_confidence=0.5,
min_tracking_confidence=0.5) as hands:
1.2.2. MULTI_HAND_LANDMARKS
- 21개의 hand landmarks
- 각 landmark의 normalized x, y, 수
- z: depth, 손목의 깊이를 원점으로, 랜드마크 깊이를 나타냄, 값이 작을 수록 카메라에 가까움
- x와 거의 같은 스케일
2. ipc
- ipc은 프로세스가 프로세스와 통신하는 방법이다.
- sysv, posix 두 표준이 있다. 간단하게 sysv는 정수키값을 사용하는 방식이고, posix는 이름 string을 사용하는 방식이다.
- 여러 종류의 ipc 기법들이 있지만 공유 메모리를 사용하였다.
2.1. winAPI C++
- 메모리 맵 파일: 가상 메모리처럼 프로세스 주소 공간을 예약
- 예약한 영역에 물리 저장소를 커밋하는 기능
- 가상 메모리와의 차이점: 시스템의 페이징 파일을 사용하는 대신 디스크 상에 존재하는 어떤 파일이라도 물리 저장소로 사용 가능
- 다른 프로세스간의 데이터를 공유할 수 있게 해준다.(IPC)
- 파일을 마치 메모리인 것처럼 사용
대용량 파일을 한꺼번에 메모리에 올려 고성능 I/O
- 참고: 파일 대신 다음과 같은 구조체를 사용하여 공유할 수 있다.
1
2
3
4
5
6
7
typedef struct
{
void *hFileMap;
void *pData;
char MapName[256];
size_t Size;
} SharedMemory;
- 할당
CreateFileMapping
: 파일의 크기, 접근 방식 => 파일 매핑 커널 오브젝트 생성MapViewOfFile
: 프로세스의 주소 공간에 파일 매핑 오브젝트의 전체나 일부를 매핑
참고: CreateFile = 메모리 맵 파일로 사용할 디스크 상의 파일 생성, 열기
해제
UnMapViewOfFile
: 프로세스의 주소 공간으로부터 파일 매핑 오브젝트의 매핑을 해제CloseHandle
:파일 커널 매핑 오브젝트, 파일 커널 오브젝트 닫기
2.1.1. CreateFileMapping
1
2
3
4
5
6
7
HANDLE CreateFileMapping(
HANDLE hFile, // 물리 저장소로 사용할 파일의 핸들(CreateFile시)
PSECURITY_ATTRIBUTES psa, // 커널 오브젝트의 보안 관련 파라미터
DWORD fdwProtect,
DWORD dwMaximumSizeHigh,
DWORD dwMaximumSizeLow,
PCTSTR pszName);
2.1.1.1. fdwProtect
PAGE_WRITECOPY
: Copy-on-write, 새로운 페이지로 복사한 후 쓰기PAGE_READONLY
:GENERIC_READ
보호 속성으로 설정 -PAGE_READWRITE
:GENERIC_READ|GENERIC_WRITE
로 설정
2.1.1.2. dwMaximumSizeHigh, Low
- 매핑할 파일의 최대 크기
- high: 매핑할 범위를 지정하는 상위 바이트
- low: 매핑할 범위를 지정하는 하위 바이트
- 64비트: 4GB보다 작을 경우 dwMaximumSizeHigh는 항상 0, high를 설정하여 그 이상의 크기도 핸들가능
- 32비트: 2GB까지만 가능?
- 매핑 파일로 지정한 파일의 크기가 기준이면, 두 파라미터에 0
2.1.1.3. pszName
- 고유한 객체 이름 부여 (이를 통해 다른 프로세스가 접근 가능)
2.1.1.4. CreateFileMapping: example
1
2
3
4
5
6
7
shm->hFileMap = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
shm->Size,
shm->MapName)
2.1.2. MapViewOfFile
MapViewOfFile
: 파일을 주소공간에 매핑할 때 전체를 한번에 할 수 있고, 일부분만 할 수 있다. 이렇게 매핑된 영역을 view 라 한다.- 파일의 데이터에 접근하기 위한 영역을 프로세스 주소 공간에 확보하는 커밋단계
- 파일 매핑 오브젝트의 전체 크기는 고려 x, 단지 view에 필요한 크기만 고려
- 함수 성공시 매핑된 뷰의 시작주소 반환(void형 타입, 실패시 NULL)
1
2
3
4
5
6
7
PVOID MapViewOfFile(
HANDLE hFileMappingObject, // createFileMapping으로 얻은 핸들
DWORD dwDesiredAccess,
DWORD dwFileOffsetHigh, // offset
DWORD dwFileOffsetLow, // offset
DWORD dwNumberOfBytesToMap // 얼마만큼 매핑할것인지
);
2.1.2.1. dwDesiredAccess
- FILE_MAP_READ: createFileMapping에서 PAGE_READONLY
- FILE_MAP_WRITE: createFileMapping에서 PAGE_READWRITE
- FILE_MAP_COPY: 새로운 페이지에서 데이터 쓰기
- FILE_MAP_EXECUTE: 데이터를 코드로 수행
FILE_MAP_ALL_ACCESS: READ WRITE COPY
2.1.2.2. offset
- 반드시 시스템 할당 단위의 배수 (64KB)
dwNumberOfBytesToMap
이 0일 경우, offset으로부터 파일의 끝까지를 view로
2.1.2.3. MapViewOfFile: example
1
2
3
4
5
6
7
shm->pData = MapViewOfFile(
shm->hFileMap,
FILE_MAP_ALL_ACCESS,
0,
0,
shm->Size)
2.1.3. UnmapViewOfFile
1
BOOL UnmapViewOfFile(PVOID pvBaseAddress);
pvBaseAddress
: 해제할 영역의 주소MapViewOfFile
함수의 반환값과 동일한 값을 사용해야한다.
1
2
3
4
if (shm->pData)
{
UnmapViewOfFile(shm->pData);
}
2.1.4. CloseHandle
1
2
3
4
if (shm->hFileMap)
{
CloseHandle(shm->hFileMap);
}
2.2. boost-interprocess C++
- namespace 가 길기 때문에
boost::interprocess
는 생략했다.
2.2.1. shared_memory_object
- 이 객체의 생성자는 3개의 파라미터가 있다.
- 첫번째 파라미터는 생성할것인지와 열것인지를 설정하는 것이다.(open_or_create)
- 두번째 파라미터는 공유 메모리의 고유 이름이다.
- 세번째 파라미터는 공유 메모리 접근하는 방식이다. (read_write)
truncate()
는 read_write일 경우에만 사용가능하다.truncate()
를 호출하여 나중에 메모리 크기를 조정할 수 있다.truncate()
호출 전에는 메모리 사이즈 0
1
2
shared_memory_object shdmem(open_or_create, "Local\\MPipe", read_write);
2.2.2. mapped_region()
- 다른 어플리케이션과 통신하려면 각 어플리케이션은 공유 메모리와 자신의 메모리 공간을 일치 시켜야함.
- 특정 어플의 주소공간에 다른 객체들을 맵핑시키는데 사용 가능하다.(ex. file_mapping의 경우 특정 파일을 위한 공유 메모리를 나타냄. 그렇기 때문에 이 타입의 객체는 파일, 이 객체는 전체 파일을 로드하지 않고 일부분만 주소공간과 맵핑하게 해주는 함수가
mapped_region()
)
1
2
3
4
#include <boost/interprocess/mapped_region.hpp>
mapped_region region(shdmem, read_write);
std::cout << std::hex << "0X" << region.get_address() << std::endl;
std::cout << std::dec << region.get_size() << std::endl;
- 첫번째 파라미터는 shared_memory_object 객체
- 두번째 파라미터는 접근 권한을 나타낸다.
2.2.3. region.get_address()
2.2.3.1.write
1
2
3
mapped_region region(shdmem, read_write);
int *i1 = reinterpret_cast<int*>(region.get_address());
*i1 = 99;
2.2.3.2. read
1
2
3
mapped_region region2(shdmem, read_only);
int *i2 = reinterpret_cast<int*>(region2.get_address());
std::cout<<*i2<<std::endl;
2.2.4. remove()
- 공유메모리 삭제하기 위한 정적 함수
2.2.5. remove_shared_memory_on_destroy()
- RAII 개념
- 객체 생성시 인자로서 존재하는 공유 메모리 이름을 전달하면,
- 이 객체가 파괴될때 소멸자에서 가지고 있던 공유메모리 함께 제거
1
2
3
#include <boost/interprocess/shared_memory_object.hpp>
bool removed = shared_memory_object::remove("Loca\\MPipe");
remove()
가 호출되지 않으면 어플이 종료되어도 공유메모리는 삭제되지 않는다.- 유닉스, 리눅스의 경우 시스템 재시작시 자동으로 공유메모리 삭제
- 윈도우나 mac은
remove()
호출이 없다면 시스템 재시작 후에도 파일로서 계속 존재, 재사용 가능 - 윈도우의 경우 공유 메모리를 사용하던 어플에서 최종 사용자가 종료될 때 자동으로 공유 메모리를 삭제할 수 있다 (
windows_shared_memory
클래스 사용)
2.2.6. managed_shared_memory
- shared_memory_object는 공유메모리에 바이트 단위로 읽고 쓰는 기능을 제공한다.
- 객체를 생성해서 저장하고 가져오는 방식을 사용하기위한 객체가 managed_shared_memory
- 객체는 공유메모리상에 만들어지고, 다른 어플에서도 그 객체를 사용할 수 있다.
1
2
3
4
5
6
7
8
9
10
#include <boost/interprocess/managed_shared_memory.hpp>
managed_shared_memory managed_shm(open_or_create, "Local\\MPipe", 512);
int *i = managed_shm.construct<int>("Integer")(99);
std::cout<<*i<<std::endl;
std::pair<int*, std::size_t> p = managed_shm.find<int>("Integer");
if (p.first) {
std::cout<<*p.first<<std::endl;
}
managed_shm.destroy<int>("Integer");
- shared_memory_object는 데이터를 읽고 쓸 때 공유메모리의 개별 바이트들로 직접 접근했지만,
- managed_shared_memory는
construct()
같은 함수를 사용한다.construct<>(" ")
의 템플릿 인자는 데이터 타입- 그리고 파라미터는 객체를 구분하기 위한 이름이 들어간다.
construct<>()
는 프록시 객체를 리턴하기 때문에 생성된 객체를 초기화할 수 있다.- construct 호출 시 이미 존재하는 경우 0이 리턴된다.
- 기존에 존재하는 managed shared memory에서 존재하는 객체를 재사용하려면
find_or_construct()
를 사용한다.(객체 초기화 x) - 실패할 경우 bad_alloc 예외가 발생한다.
managed_shm.destroy<>();
를 사용하여 객체를 삭제한다. 이 때 인자는 객체의 이름이며, 성공시 true를ㅌ리턴한다.(destroy_ptr()
은 객체, 객체 배열 삭제를 위해 객체 포인터를 인자로 넘긴다.)
- managed_shared_memry는
find()
를 통해 특정 객체를 찾는다.- 객체의 이름을 인자로 넘기면, 객체의 포인터를 반환한다. (실패시 0)
- 반환값 = <객체 포인터, 객체의 사이즈(객체 배열의 크기)>
managed_shm.construct<int>("Int")[10](99)
: [] 를 사용하여 배열로 공유메모리에 생성 가능하다. 이 때 초기값은 개별로 줄 수 없다.
2.2.7. atomic_func()
- 각 어플에서 동일한 공유메모리에 접근할때, 생성, 검색, 삭제 등은 자동적으로 동기화 된다.
- 만약 공유 메모리에 서로 다른 이름의 객체를 생성하려고 하면 그 연산이 적절히 직렬화된다.
- 다른 어플에 의한 동작에 방해받지 않고 동시에 실행시키려면
atomic_func()
함수를 사용하면된다. named_mutex
등을 이용하여 동기화를 적절하게 할 수 있다.
2.3. mmap python
- mmap을 통해 공유 메모리에 접근할 수 있다.
- write에는 바이트 타입이 들어가야한다.
- write는 호출시 데이터를 덮어쓰고, 공유메모리의 다음 주소를 가리키게 한다.
1
2
3
4
5
6
shm = mmap.mmap(0, 512, "Local\\MPipe")
if shm:
for idx in range(0, 21):
shm.write(struct.pack('f', hand_landmarks.landmark[idx].x))
shm.write(struct.pack('f', hand_landmarks.landmark[idx].y))
shm.write(struct.pack('f', hand_landmarks.landmark[idx].z))
2.4. sysv_ipc python
1
2
shm = sysv_ipc.SharedMemory(777)
shm.write(struct.pack('f', hand_landmarks.landmark[idx].x), offset)