본문 바로가기

IT/OPENCV

[OPENCV] 4. 이미지 프로세싱(1) - 블러링

반응형

☞ 메인보드 : Jetson Nano Developer Kit
운영 체제 : Ubuntu 18.04 - JetPack 4.4.1
☞ OPENCV 버전 : 4.2.0
☞ IDE : Visual Studio Code
☞ 언어 : C++
 
 
 
 
 
 

 
 
 


목차

1. Bilateral Filter
2. Blur
3. Median Blur
4. Gaussian Blur
5. 짬뽕

 
 
 


 
 
 
 


Visual Studio Code 다운로드 방법

Jetson Nano(젯슨나노) vscode- Microsoft  Visual Studio Code 설치하기

최근 핫한 코드편집기인 마이크로소프트사의 Visual Studio Code(이하 vscode)를 ARM기반의 싱글보드 PC인 젯슨나노에 설치하기위해 알아보았습니다. IDE 장인으로 유명한 마소의 vscode는 다양한 개발환

opencourse.tistory.com

 여기서 첫 번째 방법으로 소개해주신 'JetsonHacks'의 패키지를 git으로 다운로드했다.
 
그리고 마켓에 가서 사용할 언어를 다운로드했다. 이걸 안 받는다고 해서 언어를 사용하지 못하는 건 아니고, 입력했을 때 변수, 함수 등의 색이 바뀌기 때문에 오타를 발견하기 쉽다. 다운로드하지 않아도 상관은 없다.
 

 
 


 

터미널에서 code 라고 입력하면 위와 같이 창이 하나 나온다. 맨 위에 있는 메뉴에 file을 눌러서 새 파일을 만들거나 Ctrl + N을 눌러서 새 파일을 만들자.
 
 
imgProcessing.cpp 라는 이름으로 코드 파일을 만들고 사용할 라이브러리를 입력했다.
 

 
 
 
 
 

① Bilateral Filter

 

#include "opencv2/imgproc.hpp"
cv::bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFALUT)
cv::bilateralFilter(입력 이미지, 반환 이미지, 필터 크기[직경], 색 공간 시그마 필터링, 좌표 공간 시그마 필터링, 테두리 종류)

 
이 단어가 무슨 뜻인지 몰라서 네이버에 찾아보니 '쌍방의'라는 뜻을 갖고 있었다.
 
int d 는 직경 값으로 diameter의 d를 나타내는데 필터 효과 범위를 지정하기 위해 필요한 값이며 5를 초과하는 값은 필터링 과정이 매우 느리기 때문에 균형 있게 하려면 5, 정밀한 필터 효과가 필요할 때는 9를 추천하고 있다. sigmaColor와 sigmaSpace 또한 값이 클수록 변화를 주는 정도가 커지고 적용하는 픽셀의 범위도 넓어진다고 한다. 약한 효과를 주려면 10 이하, 큰 효과를 주고 싶으면 150 이상의 값을 추천하고 있다.
 
 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

int main(int argc, char** argv){
    cv::Mat src = cv::imread("road.jpg");
    if(src.empty()) return -1;

    cv::Mat bilateral;
    cv::bilateralFilter(src, bilateral, 5, 50, 50);
    cv::Mat bilateral1;
    cv::bilateralFilter(src, bilateral1, 5, 100, 100);
    cv::Mat bilateral2;
    cv::bilateralFilter(src, bilateral2, 5, 150, 150);
    
    cv::imshow("original", src);
    cv::imshow("bilateral",bilateral);
    cv::imshow("bilateral1",bilateral1);
    cv::imshow("bilateral2",bilateral2);
    
    cv::waitKey(0);

    return 0;
}

 
 

※ 실행 결과

 

원본(왼쪽) bilateral 50(오른쪽)
bilateral 100(왼쪽) bilateral 150(오른쪽)

 
별 차이가 없어 보이긴 하나 각 사진의 중심부에 있는 철조망을 보면 원본에 비해 번짐 얼마나 심해졌는지 보일 것이다. 사실 이러한 변화를 번졌다고 표현하기보다는 주위 픽셀에 맞게 변화했다고 해야 한다. 수가 커질수록 철조망을 거대한 덩어리로 보게 되는 느낌이다. 즉, 경계가 뚜렷해져 모서리나 선이 만들어지기 쉬운 이미지로 변화한다고 볼 수 있다. 물론 이후에 HSV나 이진화를 이용하여 픽셀을 집합을 조각조각 나누어야 된다.
 
주변 픽셀에 적응하거나 번짐의 정도가 심해지는 블러 효과가 나타나는 이유는 bilateral Filter가 가우시안 블러 작업을 거치기 때문이고 가장자리가 돋보이고 깔끔해지는 샤프닝 효과를 보이는 것은 급격히 변화할수록 블러 효과를 주지 않게 하는 수식 때문이다. 다만, 속도가 조금 느리다는 단점이 있다.
 
결론 : 정밀하게 작업을 하면서 좋은 결과를 원할 때 사용하는게 좋을 것 같다. 잡다한 픽셀의 값을 인접한 픽셀과 비교하여 비슷하게 만들어주면서(노이즈 제거) 시그마 값을 낮추면 윤곽선이 꽤 보존된다.
 
 
 
 
 
 


② Blur

 

#include "opencv2/imgproc.hpp"
cv::blur(InputArray src, OutputArray dst, Size ksize, Point anchor = Point(-1,-1), int borderType = BORDER_DEFAULT)
cv::blur(입력 이미지, 반환 이미지, 효과 커널의 크기[nxn] 효과 영역 기준점[기본값:중심] , 테두리 모드)

 

커널 크기에 맞게 흐림 효과를 적용하는 필터이다. 부드럽게하는 용도로 쓰는데 변화를 주는 매개변수가 커널의 크기뿐이다. ksize 값이 커질수록 변화 강도가 커지는데 조건이나 제한에 따라 흐림의 정도가 다르게 반영되는 게 아니라서 다른 함수들을 더 사용하게 된다.

 
 
 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

int main(int argc, char** argv){
    cv::Mat src = cv::imread("road.jpg");
    if(src.empty()) return -1;
    
    cv::Mat blur;
    cv::blur(src, blur, cv::Size(3,3));
    cv::Mat blur1;
    cv::blur(src, blur1, cv::Size(5,5));
    cv::Mat blur2;
    cv::blur(src, blur2, cv::Size(7,7));
    
    cv::imshow("original", src);
    cv::imshow("blur",blur);
    cv::imshow("blur1", blur1);
    cv::imshow("blur2",blur2);
    
    cv::waitKey(0);
    
    return 0;
}

 

※ 실행 결과

 

원본(왼쪽) blur kernel=3(오른쪽)
blur kernel=5(오른쪽) blur kernel=7(오른쪽)

 

커널 크기의 수 증가로도 엄청난 변화를 볼 수 있다. bilateral이나 gaussian 만큼 파라미터를 주어 사용자 입맛에 맞게 설정할 수 있는 것이 아니라 일정한 계산에 의해서 출력되는 것이 아쉽다.
 
 
 
 
 
 
 


③ Median Blur

 

#include "opencv2/imgproc.hpp"
cv::medianBlur(InputArray src, OutputArray dst, int ksize)
cv::medianBlur(입력 이미지, 반환 이미지, 커널의 크기[ksize는 1보다 큰 홀수])

 

커널의 크기에 따라 인접한 픽셀들의 중간값으로 반환해서 중간값 필터라고 불린다. 부드럽고 노이즈 제거에 효과적이라고 하는데 실제로 사용해보면 가장자리(윤곽선)를 뚜렷하게 해주는 효과가 있다.
 
 
 

X = [1, 3, 3, 50, 16, 4, 10 , 90, 10]  이런 정보를 갖고 있는 픽셀의 중간 값은 작은 수부터 차례대로 세워서 구할 수 있다. 
1 3 3 4 10 10 16 50 90

 
 
 
중간값은 가장 가운데 있는 10임을 알 수 있다. 양쪽에 있는 1,  50, 90과 엄청난 차이를 보이지만 일단은 중간에 있기 때문에 10이 중간값으로 반환되어 계산될 것이다. 엄청난 차이를 보이는 50, 90은 노이즈 취급을 받아 없어질 것이다. [극단적이지만 50, 90과 같이 편차치로부터 많이 벗어나는 값은 노이즈로 제거되는 게 맞다고 생각한다]

 
 
 
 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

int main(int argc, char** argv){
    cv::Mat src = cv::imread("road.jpg");
    if(src.empty()) return -1;
    
    cv::Mat median;
    cv::medianBlur(src, median, 3);
    cv::Mat median1;
    cv::medianBlur(src, median1, 5);
    cv::Mat median2;
    cv::medianBlur(src, median2, 7); 
    
    cv::imshow("original", src);
    cv::imshow("median", median);
    cv::imshow("median1", median1);
    cv::imshow("median2", median2);
    
    cv::waitKey(0);
    
    return 0;
}

 
 
 

※ 실행 결과

 

원본(왼쪽) median kernel=3(오른쪽)
median kernel=5(왼쪽) median kernel=7(오른쪽)

 

내가 가져온 사진에서는 median blur를 사용하기 좋은 것 같다. 물론 중앙에 있는 노란색 선을 없애버린 것은 곤란하지만 어두운 차도와 밝은 노란색 선의 대비가 커 커널이 커질수록 노란색 선이 선명해지는 것을 볼 수 있다. 극단적이나 잘만 사용하면(사진의 특성에 따라 다르겠지만) 좋을 것 같긴 하다?

 

 
 
 
 
 
 
 
 
 
 


④ Gaussian Blur

 

#include "opencv2/imgproc.hpp"
cv::GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType=BORDER_DEFAULT)
cv::GaussianBlur(입력 이미지, 반환 이미지, 커널의 크기, x축 표준 편차, y축 표준 편차[기본값=0], 테두리 모드)

 
포토샵에서도 많이 쓰고 가장자리, 윤곽선 검출에 가장 많이 사용되는 가우시안 필터는 일반적으로 이미지, 영상을 자연스럽게 만드는 용도로 사용한다. 위키피디아에 의하면 매개변수 옵션에 따라 반환된 값을 ksize로 정한 근접한 픽셀에 나누어 불규칙한 분포도를 정규 분포도에 비슷하게 만드는 규칙인 듯하다.
 
Median Blur와 마찬가지로 픽셀의 값을 세워 정규분포에 어긋난 부분은 노이즈라 인식하고 제거하는 방식으로 보인다. 접근 방법은 비슷할 수 있으나 적절하게 노이즈 제거도 하면서 사용자가 직접 편차치를 설정할 수 있다는 장점이 있기 때문에 가장 사랑받는(유명한) 필터링 기법이다.
 
 
 

 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

int main(int argc, char** argv){
    cv::Mat src = cv::imread("road.jpg");
    if(src.empty()) return -1;
    
    cv::Mat gaussian;
    cv::GaussianBlur(src, gaussian, cv::Size(3,3), 10);
    cv::Mat gaussian1;
    cv::GaussianBlur(src, gaussian1, cv::Size(5,5), 10);
    cv::Mat gaussian2;
    cv::GaussianBlur(src, gaussian2, cv::Size(7,7), 10);
    
    cv::imshow("original", src);
    cv::imshow("gaussian", gaussian);
    cv::imshow("gaussian1", gaussian1);
    cv::imshow("gaussian2", gaussian2);
    
    cv::waitKey(0);
    
    return 0;
}

 
 

※ 실행 결과

 

원본(왼쪽) gaussina kernel=3(오른쪽)
gussian kernel=5(왼쪽) gussian kernel=7(오른쪽)
gussian kernel=3 sigmaX=3(왼쪽) gussian kernel=3 sigmaX=5(중앙) gussian kernel=3 sigmaX=7(오른쪽)

 

가우시안 블러는 노이즈 제거에 탁월한 효과를 주는 보정 효과이다. 가까운 픽셀과 비슷해지려는 특성이 있고 이는 거리에 반비례하는 가중치에 의해서 결정된다. 단순히 노이즈 제거에 사용할 때는 상관없지만 가장자리 검출을 위해서 사용할 때는 이러한 장점이 단점으로 변할 수 있다. 블러 효과때문에 오히려 가장자리가 무뎌지고 검출하기 힘들게 된다.

 
 


⑤ 짬뽕

 

※ 소스 코드

 

#include "opencv2/imgproc.hpp"
#include "opencv2/highgui.hpp"

int main(int argc, char** argv){
    cv::Mat src = cv::imread("road.jpg");
    if(src.empty()) return -1;
   
    cv::Mat bilateral;
    cv::bilateralFilter(src, bilateral, 5, 50, 50);

    cv::Mat blur;
    cv::blur(src, blur, cv::Size(3,3));

    cv::Mat median;
    cv::medianBlur(src, median, 3);

    int size = 3;
    cv::Mat gaussian;
    cv::GaussianBlur(src, gaussian, cv::Size(size,size), 100);

    cv::imshow("bilateral", bilateral);
    cv::imshow("blur", blur);
    cv::imshow("median", median);
    cv::imshow("gaussian", gaussian);

    cv::waitKey(0);

    return 0;

}

 
 
 

※ 실행 결과

 

bilateral 50(왼쪽) blur kernel=3(오른쪽)
median kernel=3(왼쪽) gaussian kernel=3(오른쪽)

 
결론 : blur는 원본 이미지의 보존(사용할 수 있는) 정도를 벗어나기 때문에 사용하기 어려워 보인다. 일방적인 계산에 의하여 나오는 결과 값이고 사용자가 직접 설정할 수 있는 매개변수가 없어서 단순히 값의 높고 낮음에 따라 흐려짐의 강도가 정해지기 때문에 입맛에 맞게 사용하는 것은 어렵다고 생각한다.
 
median blur는 이미지에 따라 다르고(이는 적절한 ROI를 잡아서 작업하는 것으로 해결해야 한다.) 중간값을 고르는 게 전부이기 때문에 극단적인 필터링이 될 수 있으나 반대로 터무니없는 노이즈들을 제거하기에는 매우 효과적일 것이라 생각한다.
 
gaussian blur와 bilateral filter는 다른 블러에 비해 사용자가 직접 설정할 수 있는 매개변수의 수가 많다. gaussian blur는 sigmax, sigmay라는 편차치의 정도를 설정하면 평균과 편차치를 이용해 흐려짐의 강도를 결정하고, bliateral filter는 gaussian blur에 기초하여 만들어진 블러 효과이기 때문에 같은 맥락으로 세부 조정이 가능하다고 생각한다.
 
가장자리 검출을 위해서 블러 효과를 사용할 예정이라면, 가장자리를 보존하지 못한다는 gaussian blur의 단점을 보완하기 위해서 만들어진 bilateral filter를 사용하면 좋을 것 같다.
 
 
 
<참고>

OpenCV: OpenCV modules

OpenCV  4.2.0 Open Source Computer Vision

docs.opencv.org

 
 
 
 

반응형