Favicon

Background subtraction

Peponi7/28/20255m

C#
NugetPackageOpenCvSharp4BackgroundSubtractorMOG2BackgroundSubtractorKNNBackgroundSubtractorGMG

1. Introduction

Background subtraction은 이미지 또는 비디오의 배경을 분리하는 데 사용되는 기술이다. 주로 CCTV와 같은 고정된 영상에서 움직이는 사람, 차량 등을 감지할 때 중요한 전처리 단계로 활용된다. Background subtraction은 기본적으로 현재 프레임과 배경 모델을 비교하여 전경 요소만을 따로 분리해내며 주로 다음과 같은 단계를 거친다.

  1. 배경 모델링
    정적인 배경 이미지를 사용하거나 여러 이미지 프레임을 활용해 통계적 특성을 학습한다.
  2. 현재 프레임 비교
    배경 모델과 현재 프레임을 비교하여 배경과 다른 픽셀을 식별하여 전경으로 분류한다. 이를 이용하여 전경 마스크를 생성하며, 보통 binary 이미지로 구성된다.
  3. 배경 모델 업데이트
    알고리즘에 따라 다르지만, 많은 알고리즘은 배경 모델을 지속적으로 업데이트 하는 기능을 자체적으로 내장하고 있다.

OpenCV는 BackgroundSubtractor 클래스를 제공하여 MOG2, KNN, GMG 알고리즘을 이용한 background subtraction을 쉽게 수행할 수 있다. 이 문서에서는 KNN 알고리즘을 이용하여 고속도로의 차량을 식별하는 예시를 보여준다.

실습에 사용할 동영상은 다음과 같다.

Video by ChristianBodhi from Pixabay

2. Example

background removedBackground removed
pre-processedPre-processed
resultResult

이 예제에서는 다음 과정을 거쳐 차량을 인식한다.

  1. Background remove
    BackgroundSubtractorKNN을 이용하여 그림자가 포함된 전경 마스크를 생성한다.
  2. Pre-process
    Thresholdmorphological transformation을 이용하여 그림자, 노이즈를 제거한다.
  3. 객체 검출
    Contour detection을 이용하여 차량의 윤곽선을 찾으며, 면적을 기준으로 객체 필터링을 수행한다.
private void Perform(VideoCapture video)
{
    // OpenCV 제공 window
    Window processed = new("Processed");
    Window result = new("Result");
 
    // BackgroundSubtractor 생성
    // history : 배경 모델링에 사용할 프레임 수 설정
    // dist2Threshold : 이웃 거리에 대한 임계값
    // detectShadows : 그림자 검출 여부 설정 (그림자는 전경과 구분 가능하게 출력됨)
    using var backgroundSubtractor = BackgroundSubtractorKNN.Create(100, 500);
 
    while (true)
    {
        using var frame = new Mat();
        video.Read(frame);
 
        // 동영상이 끝나면 frame.Empty 가 true로 바뀜
        if (frame.Empty())
            break;
 
        // 배경 분리
        using var backgroundRemoved = new Mat();
        backgroundSubtractor.Apply(frame, backgroundRemoved);
 
        // 그림자 제거
        using var shadowRemoved = backgroundRemoved.Threshold(200, 255, ThresholdTypes.Binary);
 
        // 객체 보존하면서 노이즈 제거
        using var kernel = Mat.Ones(3, 3, MatType.CV_8UC1);
        using var morphed = shadowRemoved.MorphologyEx(MorphTypes.Open, kernel);
 
        // 객체 찾기
        morphed.FindContours(out var contours, out var hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxTC89L1);
 
        // 면적을 기준으로 객체 필터링
        var filtered = contours.Where(contour => Cv2.ContourArea(contour) > 100);
 
        // 최종 객체 표시
        foreach (var contour in filtered)
        {
            var rect = Cv2.BoundingRect(contour);
            frame.Rectangle(rect, Scalar.Crimson, 3);
        }
 
        processed.ShowImage(morphed);
        result.ShowImage(frame);
 
        Cv2.WaitKey(10);
    }
}

3. 참조 자료