[OpenCV]Blur/Sharpening에 대한 고찰

유경박·2023년 12월 5일
0

일상생활에서 앱이나 카메라를 통해 쉽게 접할 수 있는 이미지 처리 기술인 블러와 샤프닝에 대해서 알아보자.

먼저 처리방법에 있어 회선이라는 용어가 나오는데 아래의 그림을 참고해보자.

  • Convolusion(회선)에 대한 계산 방식

난데없이 점화식이 들이닥치며 뭔말일까 했지만 다행히도 OpenCV 공식문서에서 시각적인 자료로 이해를 돕고있다.

  • OpenCV Documentation에 소개된 이미지

즉, 회선은 데이터에 대해 어떤 작은 패턴(필터,마스크,커널 등 다양한 용어로 사용)을 찾거나 그 데이터를 변형하는 작업이라고 보통 불리오며 결국 아래와 같은 식이 도출된다.

출력영상 = 입력영상 * 마스킹 -> (회선수식)

블러&샤프닝

블러의 정의는 이웃 화소의 차이를 감소시켜 부드럽게 만드는 형태를 의미한다. 이는 마스크를 적용함으로써 입력 영상이 있을때 마스크를 적용할 행렬의 크기가 커!
지면 그만큼 인접한 원소들의 영향을 계속 받기때문에 블러처리가 강해진다는 의미이다.

즉, 3x3행렬보다 5x5의 행렬로 마스크를 씌운 경우가 블러처리가 효과가 강해!
지는것을 의미한다.

반면 샤프닝은 그 반대이다. 출력화소의 이웃 화소의 차이를 크게하여 날카롭게 하는것을 의미한다.

그렇다면 행렬의 크기에 따라 블러/샤프닝의 효과는 정도가 결정된다고 했는데 원소는 어떻게 설정해야할까?

예를들어 3*3의 형태를 보자

블러:
[
1/3, 1/3, 1/3,
1/3, 1/3, 1/3,
1/3, 1/3, 1/3
]

샤프닝:
[
-1,-1,-1,
-1,9,-1,
-1,-1,-1
]

보통 이런식으로 설정이 되나 총합이 1이면 되기때문에 조금씩 달라질수도 있다.
(그에따라 위치에따른 효과가 조금씩 변경이 될수 있다.)

예제코드

다른 블로그나 책을 보면 주로 전체 이미지에 대한 블러/샤프닝의 예제를 보여주는걸 확인했는데 이 블로그에서는 조금 응용한 코드를 적을까 한다.

그 예제는 윈도우에서 마우스를 움직임에 따라 원을 그려주고 그 이후 ROI를 기반으로 블러처리하는 예제 코드이다.(윈도우 이벤트 관련 콜백함수 세팅은 생략)

1) 불러올 이미지를 설정하고 리사이징 후 준비

image = imread(imagePath);
resize(image, image, Size(500,500));
setMouseCallback(title, onMouseMove,0);

2) 마우스 콜백함수에서 이동/클릭에 따른 핸들러 작성

선언
onMouseMove(int event, int x, int y, int flags, void * param)
구현
if (event == Event_LBUTTON){
->마우스 왼쪽 클릭시 블러/샤프닝 적용할 Image 로직
} else if (event == EVENT_RBUTTONDOWN){
->마우스 오른쪽 클릭시 로직 (필자는 초기화하는 용도로 사용)
} else if (event == EVENT_MOUSEMOVE){
->마우스 움직일때 로직 (필자는 ROI(Circle)를 UI로 표시하는데 사용)
}

3)블러를 기준으로 설명하면 다음과 같다.

  • mask 변수, x,y좌표 기준으로 Rect 및 영상의 roi Mat을 설정
  • roi Mat 기준으로 rows,cols를 조회후 mask에 대한 연산을 진행
    (단, ROI를 처리할때 가장자리는 제외해야 깔끔하게 적용됨에 주의한다.
    대략적인 코드는 아래와 같다.)
     for (int i = 1; i < roi.rows - 1; ++i) {
        for (int j = 1; j < roi.cols - 1; ++j) {
            Vec3f sum(0, 0, 0); //회선 적용후 저장할 출력 화소값
            for (int u = -1; u <= 1; ++u) {
                for (int v = -1; v <= 1; ++v) {
                    Vec3b pixel = roi.at<Vec3b>(i + u, j + v);
                    sum += mask.at<float>(u + 1, v + 1) * Vec3f(pixel[0], pixel[1], pixel[2]);
                }
            }
            roi.at<Vec3b>(i, j) = Vec3b(sum[0], sum[1], sum[2]);
        }
    }

4)ROI 출력화소값을 image에 적용시킨후 다시 보여준다.

roi.copyTo(image(roi_rect));
imshow(title, image);

*사실 이렇게 쌩으로 구현 할일은 없고 그냥 Filter2D 함수를 사용하면 된다.

결과

profile
으아아

0개의 댓글