Home [note] mediapipe와 ipc
Post
Cancel

[note] mediapipe와 ipc

목차

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

mediapipe-hand 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;
  • 할당
  1. CreateFileMapping: 파일의 크기, 접근 방식 => 파일 매핑 커널 오브젝트 생성
  2. MapViewOfFile: 프로세스의 주소 공간에 파일 매핑 오브젝트의 전체나 일부를 매핑
  • 참고: CreateFile = 메모리 맵 파일로 사용할 디스크 상의 파일 생성, 열기

  • 해제

  1. UnMapViewOfFile: 프로세스의 주소 공간으로부터 파일 매핑 오브젝트의 매핑을 해제
  2. 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: READWRITECOPY

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)

3. 참고

ipc의 종류와 특징 wrapping c/c++ for python

windows_IPC

winapi-MemoryMap1

winapi-MemoryMap2

boost-interprocess

linux-ipc

This post is licensed under CC BY 4.0 by the author.

[box2d] box2d 문서

[게임 프로그래밍 패턴] Design Patterns Revisited: Prototype