신호에서 배경 제거(background subtraction)를 통한 움직이는 물체의 위치 검출

본 글은 신호에서 배경 제거(background subtraction)를 통한 움직이는 물체의 위치를 검출하는 예제 코드를 싣고 있다.

배경 제거(background subtraction)


배경 제거(background subtraction)는 이미지 프로세싱에서 움직이는 물체를 찾는데 사용된다. 이미지 프로세싱에서의 background subtraction은 아래 그림처럼 전체 이미지에서 배경 이미지를 제거해 움직이는 물체를 찾는 과정을 의미한다. 물론 단순히 아래 그림처럼 선명하게 나오진 않고 여러가지 후처리가 있어야 한다.
 

배경 제거를 사용해 움직이는 물체를 검출하는 방식을 이미지 이외에도 적용 가능한 것인가? 
가능해 보인다. 타임 도메인이나 주파수 도메인 모두. 
단순히 생각하면 배경 데이터는 일정 시간 Δt 동안의 평균 데이터이고, 최근 프레임 데이터에 이 배경 데이터를 빼는 것이 배경제거라고 할 수 있다. 타임 도메인이나, 주파수 도메인의 신호 데이터 모두 이런 방식을 사용하면, 신호의 변화가 많은 위치나 주파수를 검출하는 것이 가능할 것이다.


1차원 신호에서의 배경제거(background subtraction) c 예제 소스 background subtraction c

위에서 언급한 간단한 배경 제거(background subtraction) 알고리즘을 사용하여 구현한 예제 코드는 아래와 같다.

#define BG_INITIAL_FRAME_CNT 3
#define BG_UPDATE_ALPHA 0.9
#define UPDATE_ALPHA (1.0 -BG_UPDATE_ALPHA)

#if defined(WIN32)
#define _lock(x) WaitForSingleObject(x->mutex, INFINITE);
#else 
#define _lock(x) pthread_mutex_lock(&x->mutex);
#endif

#if defined(WIN32)
#define _unlock(x) ReleaseMutex(x->mutex);
#else 
#define _unlock(x) pthread_mutex_unlock(&x->mutex);
#endif

typedef struct _bgs_t
{
#if defined(WIN32)
HANDLE mutex;
#else
pthread_mutex_t mutex;
#endif

int frame_len;
int initial_frame_cnt;

double *object;
double *background;

}bgs_t;


void bgs_deinit(bgs_t *bgs)
{
if (bgs)
{
safe_free(bgs->object);
safe_free(bgs->background);
#if defined(WIN32)
CloseHandle(bgs->mutex);
#endif
free(bgs);
}
}

bgs_t *bgs_init(int frame_len)
{
bgs_t *bgs = (bgs_t *)malloc(sizeof(bgs_t));
if (!bgs) return NULL;

memset(bgs, 0, sizeof(bgs_t));

#if !defined(WIN32)
pthread_mutex_init(& bgs->mutex, NULL);
#else
bgs->mutex = CreateMutex(NULL, FALSE, TEXT("bgs mutex"));
#endif

bgs->frame_len = frame_len;
bgs->object = (double*)malloc(sizeof(double)*frame_len);
bgs->background = (double*)malloc(sizeof(double)*frame_len);

if (!bgs->object || !bgs->background)
{
bgs_deinit(bgs);
return NULL;
}

memset(bgs->object, 0, sizeof(double)*frame_len);
memset(bgs->background, 0, sizeof(double)*frame_len);
return bgs;
}

int bgs_update(bgs_t *bgs, double *frame, int len)
{
int ret = 0;
if (!bgs || !frame) return -1;
_lock(bgs);

if (len > bgs->frame_len) len = bgs->frame_len;

if (bgs->initial_frame_cnt < BG_INITIAL_FRAME_CNT)
{
if (bgs->initial_frame_cnt == 0)
{
memcpy(bgs->background, frame, len*sizeof(double));
}
else
{
int i = 0;
int n = bgs->initial_frame_cnt + 1;
int nm1 = bgs->initial_frame_cnt;

while (i < len)
{
bgs->background[i] = (bgs->background[i] * nm1 + frame[i]) / n;
i++;
}
}

bgs->initial_frame_cnt++;

}
else if (bgs->initial_frame_cnt >= BG_INITIAL_FRAME_CNT)
{
int i = 0;
while (i < len)
{
double in = frame[i];
double bg = bgs->background[i];

bgs->background[i] = (bg * BG_UPDATE_ALPHA) + (in*UPDATE_ALPHA);
bgs->object[i] = (in - bgs->background[i]);

i++;
}
}

_unlock(bgs);

return ret;
}


테스트 코드


길이가 30, 개수가 30개인 1차원 프레임 데이터를 만들어 테스트했다.

double frame[30][30] = {
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 3, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 2, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },

{ 2, 1, 3, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 3, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 4, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 2, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 2, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 1, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 1, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 1, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 2, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 3, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 3, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 2, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 2, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 1, 1, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 2, 3, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 4, 2, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 3, 1, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 2, 0, 1, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 1, 1, 2, 2, 2, 2, 1 },

{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 2, 2, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 3, 2, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 3, 2, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 3, 2, 1 },
{ 2, 1, 2, 2, 3, 3, 2, 1, 1, 1, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 3, 2, 1, 0, 1, 2, 2, 2, 3, 1 }
};



void bgs_test()
{
int i = 0;
int frame_len = 30;
int fframe_count = 30;

bgs_ctx_t *bgs = bgs_init(frame_len);
if (!bgs) return;

for (i = 0; i < fframe_count; i++)
{
system("cls");
bgs_update(bgs, frame[i], frame_len);

printf("\n     frame\n");
for (int y = 4; y >= 0; y--)
{
printf(" ");
for (int x = 0; x < frame_len; x++)
{
if (frame[i][x] >= y && frame[i][x] < y+1) printf("*");
else printf(" ");
}
printf("\n");
}

printf("\n     bg\n");
for (int y = 4; y >= 0; y--)
{
printf(" ");
for (int x = 0; x < frame_len; x++) if (bgs->background[x] >= y && bgs->background[x] < y+1) printf("*"); else printf(" ");
printf("\n");
}

printf("\n     moving object location\n ");
for (int x = 0; x < frame_len; x++) if (bgs->object[x] > 0) printf("*"); else printf("_");
printf("\n");
Sleep(1000);
}

bgs_deinit(bgs);
}

실행하면 아래처럼 moving object가 검출되는 것을 볼 수 있다.
 
 




또한 위의 배경 제거(background subtraction) 예제 코드를 사용하여 레이더 신호에서 움직이는 물체 추출하면 아래와 같은 결과를 얻을 수 있다.
 



댓글

이 블로그의 인기 게시물

windows에서 간단하게 크롬캐스트(Chromecast)를 통해 윈도우 화면 미러링 방법

간단한 cfar 알고리즘에 대해

바로 프로젝트 적용 가능한 FIR Filter (low/high/band pass filter )를 c나 python으로 만들기

간단한 칼만 필터(Kalman Filter) 소스 코드와 사용 예제

python winsound를 이용한 윈도우 환경에서 소리 재생 예제