Top 48 Opencv 필터 The 145 New Answer

You are looking for information, articles, knowledge about the topic nail salons open on sunday near me opencv 필터 on Google, you do not find the information you need! Here are the best content compiled and compiled by the https://chewathai27.com/to team, along with other related topics such as: opencv 필터 OpenCV 필터 종류, OpenCV 색 필터, OpenCV filter2D, OpenCV high pass filter, OpenCV Convolution, 파이썬 Median filter 구현, OpenCV mean filter c, cv2.filter2d 구현


OpenCV Filtering [ Python 데이터 분석과 이미지 처리 ]
OpenCV Filtering [ Python 데이터 분석과 이미지 처리 ]


OpenCV의 다양한 필터

  • Article author: overface.tistory.com
  • Reviews from users: 41114 ⭐ Ratings
  • Top rated: 4.3 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about OpenCV의 다양한 필터 #OpenCV 평균 값 필터링 함수 /* src : 입력 영상 ksize : 평균값 필터 크기. (wth, height) 형태의 튜플 dst : 결과 영상, 입력 영상과 같은 크기 … …
  • Most searched keywords: Whether you are looking for OpenCV의 다양한 필터 #OpenCV 평균 값 필터링 함수 /* src : 입력 영상 ksize : 평균값 필터 크기. (wth, height) 형태의 튜플 dst : 결과 영상, 입력 영상과 같은 크기 … 평균 값 필터(Mean filter) 영상의 특정 좌표 값을 주변 픽셀 값들의 산술 평균으로 설정 픽셀들 간의 그레이스케일 값 변화가 줄어들어 날카로운 에지가 무뎌지고, 영상에 있는 잡음의 영향이 사라지는 효과..
  • Table of Contents:

고정 헤더 영역

메뉴 레이어

검색 레이어

상세 컨텐츠

평균 값 필터(Mean filter)

가우시안 필터

언샤크 마스크(Unsharp mask) 필터링

영상의 잡음(Noise)

잡음 제거(1) 미디언 필터

잡음 제거(1) 가우시안 필터

잡음 제거(2) 양방향 필터

태그

추가 정보

페이징

티스토리툴바

OpenCV의 다양한 필터
OpenCV의 다양한 필터

Read More

OpenCV – 17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터

  • Article author: bkshin.tistory.com
  • Reviews from users: 28167 ⭐ Ratings
  • Top rated: 3.9 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about OpenCV – 17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터 이번 포스팅부터는 영상 필터에 대해 알아보겠습니다. 이번 포스팅 역시 ‘파이썬으로 만드는 OpenCV 프로젝트(이세우 저)’를 정리한 것임을 밝힙니다. …
  • Most searched keywords: Whether you are looking for OpenCV – 17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터 이번 포스팅부터는 영상 필터에 대해 알아보겠습니다. 이번 포스팅 역시 ‘파이썬으로 만드는 OpenCV 프로젝트(이세우 저)’를 정리한 것임을 밝힙니다. 이번 포스팅부터는 영상 필터에 대해 알아보겠습니다. 이번 포스팅 역시 ‘파이썬으로 만드는 OpenCV 프로젝트(이세우 저)’를 정리한 것임을 밝힙니다. 코드: github.com/BaekKyunShin/OpenCV_Project_Python/tree..
  • Table of Contents:

귀퉁이 서재

OpenCV – 17 필터(Filter)와 컨볼루션(Convolution) 연산 평균 블러링 가우시안 블러링 미디언 블러링 바이레터럴 필터 본문

티스토리툴바

OpenCV - 17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터
OpenCV – 17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터

Read More

[OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring) – 데이터 사이언스 사용 설명서

  • Article author: dsbook.tistory.com
  • Reviews from users: 47408 ⭐ Ratings
  • Top rated: 4.6 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about [OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring) – 데이터 사이언스 사용 설명서 openCV에서는 이미지와 kernel(filter)를 Convolution(합성곱)하여 이미지를 필터링 해주는 함수, cv2.filter2D를 제공한다. 필터링할 이미지와 … …
  • Most searched keywords: Whether you are looking for [OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring) – 데이터 사이언스 사용 설명서 openCV에서는 이미지와 kernel(filter)를 Convolution(합성곱)하여 이미지를 필터링 해주는 함수, cv2.filter2D를 제공한다. 필터링할 이미지와 … OpenCV: Smoothing Images Goals Learn to: Blur images with various low pass filters Apply custom-made filters to images (2D convolution) 2D Convolution ( Image Filtering ) As in one-dimensional signa..데이터 사이언스 분야를 어떻게 공부하는지 알려주는 블로그
  • Table of Contents:

검색

[OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring)

cv2filter2D(src ddepth kernel [ dst [ anchor [ delta [ borderType]]]]) → dst

Trackbar로 kernel 사이즈 조정하기

1 Averaging

cv2blur(src ksize [ dst [ anchor [ borderType]]]) → dst

2 Gaussian Filtering

cv2GaussianBlur(src ksize sigmaX [ dst [ sigmaY [ borderType]]]) → dst

3 Median Filtering

medianBlur(src ksize [ dst]) → dst

4 Bilateral Filtering

cv2bilateralFilter(src d sigmaColor sigmaSpace [ dst [ borderType]]) → dst

[OpenCV Practice 10 - 1] 이미지 필터링 (Image Filtering & Blurring) - 데이터 사이언스 사용 설명서
[OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring) – 데이터 사이언스 사용 설명서

Read More

[C++ opencv] 평균필터 적용하여 노이즈 제거하기 average filter, filter2d()

  • Article author: diyver.tistory.com
  • Reviews from users: 45062 ⭐ Ratings
  • Top rated: 4.0 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about [C++ opencv] 평균필터 적용하여 노이즈 제거하기 average filter, filter2d() OpenCV에서는 filter2D( ) 라는 함수가 있다. 이번에는 filter2D( ) 함수를 이용해서 영상을 blur 처리하는 방법을 알아본다. …
  • Most searched keywords: Whether you are looking for [C++ opencv] 평균필터 적용하여 노이즈 제거하기 average filter, filter2d() OpenCV에서는 filter2D( ) 라는 함수가 있다. 이번에는 filter2D( ) 함수를 이용해서 영상을 blur 처리하는 방법을 알아본다. 본문 목표 영상처리에 있어서 제일 중요한 것들 중 하나는 당연 ‘노이즈 제거’ 이다. 노이즈 제거 방법에는 정말 여러가지가 있지만, 제일 쉬운 방법은 blur 처리를 하는 것이다. blur 처리란 영상을 흐리게하는..
  • Table of Contents:

본문 목표

평균필터 – 원리 이해

알아볼 함수 원형

코드 테스트 결과

태그

관련글

댓글0

공지사항

최근글

인기글

최근댓글

태그

티스토리툴바

[C++ opencv] 평균필터 적용하여 노이즈 제거하기 average filter, filter2d()
[C++ opencv] 평균필터 적용하여 노이즈 제거하기 average filter, filter2d()

Read More

OpenCV-영상 공간 필터링 – Cornor’s Blog

  • Article author: wjddyd66.github.io
  • Reviews from users: 22595 ⭐ Ratings
  • Top rated: 4.6 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about OpenCV-영상 공간 필터링 – Cornor’s Blog 이미지 필터링(image filtering)은 필터(filter) 또는 커널(kernel) 또는 윈도우(window)라고 하는 정방행렬을 정의하고 이 커널은 이동시키면서 같은 임 … …
  • Most searched keywords: Whether you are looking for OpenCV-영상 공간 필터링 – Cornor’s Blog 이미지 필터링(image filtering)은 필터(filter) 또는 커널(kernel) 또는 윈도우(window)라고 하는 정방행렬을 정의하고 이 커널은 이동시키면서 같은 임 … 참고사항현재 Post에서 사용하는 Data를 만드는 법이나 사용한 Image는 github에 올려두었습니다.영상 공간 필터링이미지 필터링(image filtering)은 필터(filter) 또는 커널(kernel) 또는 윈도우(window)라고 하는 정방행렬을 정의하고 이 커널은 이동시키면서 같은 임미지 영역과 곱하여 그 결과값을 이미지의 해당 위치의 값으로 하는 새로운 이미지를 만드는 연산이다.기호 \(\otimes\)로 표기한다.원본 이미지의 화소(x,y)의 위치의 명도를 f(x,y)를 필터이미지 h(x,y), 필터링된 결과를 g(x,y)라고 하면 수식은 다음과 같다.$$f \otimes h = \sum_{u=-K/2}^{K} \sum_{u=-K/2}^{K} f(x+u,y+v) \cdot h(u,v)$$위의 식에서 K는 필터 크기의 절반을 의미한다. ex) 3 x 3 크기의 필터에서는 K=1이다.만약 위의 식에서 필터를 좌우 상하를 뒤집으면 콘볼루션(convolution)이라고 한다. 기호고는 \(*\)로서 표기한다.$$f * h = \sum_{u=-K/2}^{K} \sum_{u=-K/2}^{K} f(x-u,y-v) \cdot \tilde{h}(u,v)$$$$\tilde{h}(u,v) = h(-u,-v)$$사진 출처:데이터 사이언스 스쿨블러필터영상을 부드럽게 하는 블러링(bluring)/스무딩(smoothing)필터를 사용하게되면 영상의 잡음(noise)를 제거하고 영상을 부드럽게 한다.이러한 필터들은 고주파의 성분들을 저주파의 성분들로 근사시키는 역할을 하므로 LPF(Low Pass Filter)라고 불린다.cv2.boxFilter(src, ddepth, ksize[, dst[, anchor[, normalize[, borderType]]]]): 균일한 값을 가지는 커널을 이용한 이미지 필터링이다. 커널 영역내의 평균값으로 해당 픽셀을 대체한다.parameter src: input ddepth: outputimage depth ksize: bluring kernel size anchor: 커널 중심, default: (-1,-1) normalize: flag, 정규화 borderType: padding방법식$$K=\alpha \begin{bmatrix}1 & 1 & 1 & … & 1 \\1 & 1 & 1 & … & 1 \\ & & … & & \\1 & 1 & 1 & … & 1\end{bmatrix}$$$$ \alpha = \begin{cases} \frac{1}{kw x kh} & \mbox{if } normalize=True \\ 1 & \mbox{else} \end{cases} $$cv2.blur(src, ksize[, dst[, anchor[, borderType]]]) : Box Filter에서 normalized=True한 Filter이다.cv2.medianBlur(src, ksize[, dst]) : 평균값이 아닌 중앙값으로 픽셀값을 바꾸는 방법이다. 점 모양의 잡음을 제거하는데 효과적인다cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]): 필터를 가우시안 Kernel과 Convolution을 수행한다.가우시안 필터는 순환 대칭, 단일 돌출(Single peak)부분을 가진다. 즉, 중앙에 위치한 pixel과 먼 pixel값들을 낮추는 효과로서 잡음제거 효과가 있다.참고사항: 가우시안 필터parameter sigmaX : X-축 방향으로 가우시안 커널 표준편차 sigmaY: Y-축 방향으로 가우시안 커널 표준편차$$ sigmaX = sigmaY (\text{if } sigmaX \neq 0, sigmaY \neq 0)$$$$ sigmaX = 0.3(((kw-1)/2)-1)+0.8 (\text{if } sigmaX = 0$$$$ sigmaY = 0.3(((kh-1)/2)-1)+0.8 (\text{if } sigmaY = 0$$식$$f \otimes G = \sum_{u=-K/2}^{K} \sum_{u=-K/2}^{K} f(x+u,y+v) \cdot G(u,v)$$$$= \sum_{u=-K/2}^{K} \sum_{u=-K/2}^{K} f(x+u,y+v) \cdot G((x+u)-x,(y+v)-v)$$cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace[, dst[, borderType]]): 위에서 설명한 가우시안 필터를 사용하게 되면 이미지의 경계선을 무시하는 블러 필터 이다. 하지만 양방향 필터링을 사용하게 되면, 두 픽셀과의 거리 뿐 아니라 두 픽셀의 명앖값의 차이도 커널에 넣어서 가중치로 곱한다.식$$\begin{multline}f \otimes G = \\\sum_{u=-K/2}^{K} \sum_{u=-K/2}^{K} (f(x+u,y+v) \cdot \\G((x+u)-x,(y+v)-v) \cdot \\G^{\prime}(f(x+u)-f(x),f(y+v)-f(v)))\end{multline}$$추가사항: borderType(ksize=3)인 경우 bordertype 왼쪽패딩 배열 왼쪽패딩 cv2.BORDER_CONSTANT 000 123456 000 cv2.BORDER_REPLICATE 111 123456 111 cv2.BORDER_REFLECT 321 123456 654 cv2.BORDER_REFLECT_101 234 123456 543 cv2.BORDER_WRAP 456 123456 123 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189#Component 선언IntSlider_Box = IntSlider( value=1, min=1, max=30, step=1, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Biateral = IntSlider( value=0, min=-1, max=30, step=1, description=’d: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_MedianBlur = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Blur = IntSlider( value=1, min=1, max=20, step=1, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian2 = IntSlider( value=0, min=0, max=10, step=1, description=’Sigma: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)def layout(header, left, right): layout = AppLayout(header=header, left_sidebar=left, center=None, right_sidebar=right) return layoutwImg_original = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Box = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Biateral = Image(layout = Layout(border=”solid”), width=”50%”) wImg_MedianBlur = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Blur = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Gaussian = Image(layout = Layout(border=”solid”), width=”50%”) box1 = layout(IntSlider_Box,wImg_original,wImg_Box)box2 = layout(IntSlider_Biateral,wImg_original,wImg_Biateral)box3 = layout(IntSlider_MedianBlur,wImg_original,wImg_MedianBlur)box4 = layout(IntSlider_Blur,wImg_original,wImg_Blur)items = [IntSlider_Gaussian,IntSlider_Gaussian2]Gaussian = Box(items)box5 = layout(Gaussian,wImg_original,wImg_Gaussian)tab_nest = widgets.Tab()tab_nest.children = [box1, box2, box3, box4, box5]tab_nest.set_title(0, ‘Box Filter’)tab_nest.set_title(1, ‘Biateral Filter’)tab_nest.set_title(2, ‘MedianBlur Filter’)tab_nest.set_title(3, ‘Blur Filter’)tab_nest.set_title(4, ‘Gaussian Filter’)tab_nestimg = cv2.imread(‘./data/lena.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img)[1].tostring()wImg_original.value = tmpStreamdisplay.display(tab_nest)#Event 선언gaussian_ksize, gaussian_sigma = 1,0def on_value_change_Box(change): ksize = change[‘new’] Box_img = cv2.boxFilter(img,ddepth=-1,ksize=(ksize,ksize)) tmpStream = cv2.imencode(“.jpeg”, Box_img)[1].tostring() wImg_Box.value = tmpStream def on_value_change_Biateral(change): d = change[‘new’] Biateral_img = cv2.bilateralFilter(img,d=d,sigmaColor = 10, sigmaSpace = 10) tmpStream = cv2.imencode(“.jpeg”, Biateral_img)[1].tostring() wImg_Biateral.value = tmpStream def on_value_change_MedianBlur(change): ksize = change[‘new’] MedianBlur_img = cv2.medianBlur(img,ksize=ksize) tmpStream = cv2.imencode(“.jpeg”, MedianBlur_img)[1].tostring() wImg_MedianBlur.value = tmpStream def on_value_change_Blur(change): ksize = change[‘new’] Blurr_img = cv2.blur(img,ksize=(ksize,ksize)) tmpStream = cv2.imencode(“.jpeg”, Blurr_img)[1].tostring() wImg_Blur.value = tmpStream def on_value_change_Gaussian(change): global gaussian_ksize, gaussian_sigma gaussian_ksize = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Gaussian.value = tmpStream def on_value_change_Gaussian2(change): global gaussian_ksize, gaussian_sigma gaussian_sigma = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Gaussian.value = tmpStream #초기화 작업Box_img = cv2.boxFilter(img,ddepth=-1,ksize=(1,1))tmpStream = cv2.imencode(“.jpeg”, Box_img)[1].tostring()wImg_Box.value = tmpStreamBiateral_img = cv2.bilateralFilter(img,d=0,sigmaColor = 10, sigmaSpace = 10)tmpStream = cv2.imencode(“.jpeg”, Biateral_img)[1].tostring()wImg_Biateral.value = tmpStream MedianBlur_img = cv2.medianBlur(img,ksize=1)tmpStream = cv2.imencode(“.jpeg”, MedianBlur_img)[1].tostring()wImg_MedianBlur.value = tmpStreamBlurr_img = cv2.blur(img,ksize=(1,1))tmpStream = cv2.imencode(“.jpeg”, Blurr_img)[1].tostring()wImg_Blur.value = tmpStreamGaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma)tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring()wImg_Gaussian.value = tmpStream#Component에 Event 장착IntSlider_Box.observe(on_value_change_Box, names=’value’)IntSlider_Biateral.observe(on_value_change_Biateral, names=’value’)IntSlider_MedianBlur.observe(on_value_change_MedianBlur, names=’value’)IntSlider_Blur.observe(on_value_change_Blur, names=’value’)IntSlider_Gaussian.observe(on_value_change_Gaussian, names=’value’)IntSlider_Gaussian2.observe(on_value_change_Gaussian2, names=’value’)블러 필터 결과Box Filter(ksize = 13)Biateral Filter(d = 21)Median Blur Filter(ksize = 50)Blur Filter(ksize = 13)Gaussian Filter(ksize = 13, sigma=6)미분필터미분필터란 영상내의 여러가지 정보중 특별히 feature라고 명칭하는, 영상이 가지는 특별한 정보들이 있다.이러한 feature의 기본적인 요소로 blob, corner, edge등이 존재하게 된다.corner, edge의 경우 자신을 경계로하여 주변의 밝기값이 급격하게 변화하는 현상을 보이기 때문에 고주파 성분에 해당한다.이러한 고주파 성분을 찾아내는 필터이기 때문에 HPF(High Pass Filter)라고 불리게 된다.미분 연산1차 미분함수 f(x)에서 1차미분, \(f^{\prime}(x)\)는 다음과 같이 나타낼 수 있다.$$f^{\prime}(x) = \frac{\partial f(x)}{\partial x} = \lim_{h \rightarrow 0} \frac{f(x+h)-f(x)}{h}$$위의 식을 Image로적용시키면 h는 0에 가까워질수 없고 최소로 작은수는 인접화소를 가르킬수 있는 1의 값을 가지게 된다.이러한 Image의 특성을 고려하면 최종적인 미분식은 다음과 같다.$$f^{\prime}(x) = f(x+1) – f(x)$$2차 미분 위의 식을 활용하여 2차미분식을 유도하면 다음과 같다.$$f^{\prime \prime}(x) = (f^{\prime}(x))^{\prime} = (f(x+1) – f(x))^{\prime}$$$$= \frac{\partial(f(x+1)-f(x))}{\partial x} = \frac{\partial f(x+1)}{\partial x} – \frac{\partial f(x)}{\partial x}$$$$= \lim_{h \rightarrow 0} \frac{f(x+1+h)-f(x+1)}{h} – \lim_{h \rightarrow 0} \frac{f(x+h)-f(x)}{h}$$$$= f(x+2) – f(x+1) – f(x+1) + f(x) $$$$= f(x+2) – 2f(x+1) + f(x)$$$$\therefore f^{\prime \prime}(x) = f(x+1) – 2f(x) + f(x-1)$$Sobel Filtercv2.Sobel(src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]]): 지정한 축방향으로의 디지털 형태의 미분을 구현. 1차미분을 통하여 특정한 Edge를 검출하는 Filter이다.parameter src: input ddept: output image depth dx: x에 대한 미분 차수 dy: y에 대한 미분 차수 ksize: Sobel kernel 크기 (1,3,5,7만 가능)example사진 출처:bskyvision 블로그수직엣지 검출을 살펴보게 되면 위에서 정리한 식(1차 미분) \(f^{\prime}(x) = f(x+1) – f(x)\)을 사용한 것을 알 수 있다.주방향으로는 1이아닌 2의 가중치를 부여하여 Kernel을 구성한다.Laplacian Filtercv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]]): 1차 미분을 사용하는 Sobel Filter에 비하여 영상내에 blob이나, 섬세한 부분을 더 잘 검출하는 경향을 보인다. 이러한 특징 때문에 2차 미분은 주로 영상개선에 사용하며 1차 미분은 특징검출에 사용된다.example(3 x 3, ksize=1)$$\begin{bmatrix}0 & 1 & 0 \\1 & -4 & 1 \\0 & 1 & 0 \end{bmatrix}$$위의 식을 살펴보게 되면 위에서 정리한 식(2차 미분) \(f^{\prime \prime}(x) = f(x+1) – 2f(x) + f(x-1)\) 을 사용하는 것을 알 수 있다.각각 x,y에 대하여 미분해야 하므로 식을 바꿔서 정리하면 다음과 같이 나타낼 수 있다.$$f^{\prime \prime}(x,y) $$$$= f(x-1,y) + f(x,y-1)$$$$+ f(x+1,y) + f(x,y+1) – 4f(x,y)$$주의사항사진 출처: WORLD of NEO 블로그위의 사진을 보게되면 Laplacian Filter의 몇가지 특징이 나타나게 된다. 2차미분 방식을 이용하여 영상에서 에지를 검출하면 영상에 대해서 미분을 두 번 수행하기 때문에 에지가 중심 방향에 가능게 형성되며 검출된 윤각선이 폐곡선을 이루게된다. 2차 미분은 밝기 값이 점차적으로 변화되는 영역에 대해서는 반응을 보이지 않는다. Laplacian Filter는 잡음에 민감하다.위의 특징으로 인하여 다음과 같은 과정을 거친 뒤 Laplacian Filter를 사용하는 것이 권고된다.잡음에 민감한 특징Laplacian Filter를 사용하게 되면 잡음에 민감하다는 것때문에 Gausian Filter를 통하여 Smoothing및 잡음 제거 후 Laplacian Filter를 사용하게 된다.폐곡선폐곡선의 장점은 에지 주변에서 부호가 바뀌게 된다는 것 이다.이러한 부호의 변화를 영교차(zero crossing)이라고 한다.이러한 zerocrossing을 사용하여 edge 크기를 비교하여 임계값 이상일 경우에 에지로 정의할 수 있도록 임의의 임계값을 활용하여 edge를 출력할 수 있다.참고사항1(magnitude)cv2.magnitude(x, y[, magnitude]) : 2D vector의 magnitude계산parameter x: floating point1 y: floating point2 manitude: \(dst = \sqrt{x^2+y^2}\)참고사항2(각도 계산)sobel filter를 통하여 x와 y에 관한 각각의 미분값을 얻을 수 있다.이러한 미분값을 통하여 삼각함수를 이용하여 각도 (\(\theta\) )를 계산할 수 있다. gx: x에 관한 미분값 gy: y에 관한 미분값$$\theta = tan^{-1}(\frac{gy}{gx})$$참고 사항3(Normalization)Local Maxima를 선택하는 단계이다.극 값을 선택하는데 있어서 잘못된 영역이 나올 수 있다.즉 진짜 Edge가 아님에도 불구하고 검출이 된 영역이 있다는 것 이다.이러한 이유는 개인적으로 크게 2가지로 생각한다. Image의 경우 Text와 달리 주변 pixel끼리의 값이 비슷한 Data의 값이 연속적이라는 특징을 가지고 있기 때문이다. Blur를 통하여 흐려진 Edge에서 잘못된 검출이 발생하기 때문에 다시 Sharp한 edge로 변환해야 한다.이러한 Local Maxima는 양 방향과 음 방향으로 에지 강도와 현재 픽셀의 에지 강도를 비교판단을 하여 나타낼 수 있다.아래 사진은 주변 pixel 4개를 선택하였을때 Local Maxima를 선택하는 예시이다.사진 출처:carstart 블로그cv2.normalize(src[, dst[, alpha[, beta[, norm_type[, dtype[, mask]]]]]])parameter src: input alpha: norm value to normalize to or the lower range boundary beta: upper range boundary normType: normalization typeSobel Filter11234567891011121314151617181920212223242526272829303132333435363738src = cv2.imread(‘./data/rectangle.jpg’,cv2.IMREAD_GRAYSCALE) gx = cv2.Sobel(src,cv2.CV_32F,1,0,ksize=3)gy = cv2.Sobel(src,cv2.CV_32F,0,1,ksize=3)dstX = cv2.sqrt(np.abs(gx))dstX = cv2.normalize(dstX,None,0,255,cv2.NORM_MINMAX,dtype=cv2.CV_8U)dstY = cv2.sqrt(np.abs(gy))dstY = cv2.normalize(dstY,None,0,255,cv2.NORM_MINMAX,dtype=cv2.CV_8U)mag = cv2.magnitude(gx,gy)minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(mag)print(‘mag:’,minVal,maxVal,minLoc,maxLoc)dstM = cv2.normalize(mag,None,0,255,cv2.NORM_MINMAX,dtype=cv2.CV_8U)plt.figure(figsize=(20,4))imgae1=plt.subplot(1,4,1)imgae1.set_title(‘Original’)plt.axis(‘off’)plt.imshow(src, cmap=”gray”)imgae2=plt.subplot(1,4,2)imgae2.set_title(‘dstX’)plt.axis(‘off’)plt.imshow(dstX, cmap=”gray”)imgae3=plt.subplot(1,4,3)imgae3.set_title(‘dstY’)plt.axis(‘off’)plt.imshow(dstY, cmap=”gray”)imgae3=plt.subplot(1,4,4)imgae3.set_title(‘dstM’)plt.axis(‘off’)plt.imshow(dstM, cmap=”gray”)plt.show()Sobel Filter1 결과mag: 0.0 1080.467529296875 (0, 0) (100, 400)실행 결과 각각의 x,y,xy축으로 미분값(Edge)를 추출하는 것을 알 수 있다.mag같은 경우 x, y축으로 미분값의 공통된 점으로서 코너점을 출력하는 것을 알 수 있다.Sobel Filter2(gradient orientation)cv2.cartToPolar(x, y[, magnitude[, angle[, angleInDegrees]]])는 2D vector의 magnitude계산및 각도도 Return하는 함수이다.결과 angleM에서 색깔은 각각의 의미와 같다. 빨강: magnitude의 값 중 angle = 0 인곳 초록: magnitude의 값 중 angle = 90 인곳 파랑: magnitude의 값 중 angle = 180 인곳 노랑: magnitude의 값 중 angle = 270 인곳1234567891011121314151617181920212223242526272829303132333435363738394041424344454647src = cv2.imread(‘./data/rectangle.jpg’,cv2.IMREAD_GRAYSCALE)gx = cv2.Sobel(src,cv2.CV_32F,1,0,ksize=3)gy = cv2.Sobel(src,cv2.CV_32F,0,1,ksize=3)mag,angle = cv2.cartToPolar(gx,gy,angleInDegrees =True)minVal,maxVal,minLoc,maxLoc = cv2.minMaxLoc(angle)print(‘angle: ‘,minVal,maxVal,minLoc,maxLoc)ret,edge = cv2.threshold(mag,100,255,cv2.THRESH_BINARY)edge = edge.astype(np.uint8)width,height = mag.shape[:2]angleM = np.full((width,height,3),(255,255,255),dtype=np.uint8)for y in range(height): for x in range(width): if edge[y,x]!=0: if angle[y,x] == 0: angleM[y,x] = (0,0,255) # red elif angle[y,x] == 90: angleM[y,x] = (0,255,0) # green elif angle[y,x] == 180: angleM[y,x] = (255,0,0) # blue elif angle[y,x] == 270: angleM[y,x] = (0,255,255) # yellow else: angleM[y,x] = (128,128,128) # grayhist = cv2.calcHist(images=[angle],channels=[0],mask=edge,histSize=[360],ranges=[0,360])hist = hist.flatten() plt.figure(figsize=(20,4))imgae1=plt.subplot(1,3,1)imgae1.set_title(‘edge’)plt.imshow(edge, cmap=”gray”)imgae2=plt.subplot(1,3,2)imgae2.set_title(‘angleM’)plt.imshow(angleM)imgae3=plt.subplot(1,3,3)imgae3.set_title(‘hist: binX = np.arrange(360)’)plt.plot(hist,color=’r’)binX=np.arange(360)plt.bar(binX,hist,width=1,color=’b’)plt.show()Sobel Filter 2 결과angle: 0.0 359.94378662109375 (0, 0) (400, 399)Laplacian 필터원본 이미지를 부드럽게하여 미분 오차를 줄이기 위하여 ksize=(7,7)크기의 필터를 사용한 가우시안 블러링으로 blur를 생성한다.blur에 라플라시안 필터링을하여 lap을 생성한다.dst는 lap을 범위[0,255]로 정규화한 결과이다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104#Component 선언IntSlider_Gaussian = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian2 = IntSlider( value=0, min=0, max=10, step=1, description=’Sigma: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)def layout(header, left, right): layout = AppLayout(header=header, left_sidebar=left, center=None, right_sidebar=right) return layoutwImg_original = Image(layout = Layout(border=”solid”), width=”50%”)wImg_Gaussian = Image(layout = Layout(border=”solid”), width=”50%”)wImg_Laplacian = Image(layout = Layout(border=”solid”), width=”50%”)wImg_Laplacian2 = Image(layout = Layout(border=”solid”), width=”50%”)items = [IntSlider_Gaussian,IntSlider_Gaussian2]items2 = [wImg_original,wImg_Gaussian]items3 = [wImg_Laplacian,wImg_Laplacian2]Gaussian = Box(items)Input =Box(items2)Output = Box(items3)box = layout(Gaussian,Input,Output)tab_nest = widgets.Tab()tab_nest.children = [box]tab_nest.set_title(0, ‘Gaussian Filter, Laplacian’)tab_nestimg = cv2.imread(‘./data/lena.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img)[1].tostring()wImg_original.value = tmpStreamdisplay.display(tab_nest)#Event 선언gaussian_ksize, gaussian_sigma = 1,0 def on_value_change_Gaussian(change): global gaussian_ksize, gaussian_sigma gaussian_ksize = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Gaussian.value = tmpStream make_laplacian(Gaussian_img) def on_value_change_Gaussian2(change): global gaussian_ksize, gaussian_sigma gaussian_sigma = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Gaussian.value = tmpStream make_laplacian(Gaussian_img)def make_laplacian(img): lap = cv2.Laplacian(img,cv2.CV_32F) minVal,maxVal,minLoc,maxLoc = cv2.minMaxLoc(lap) dst = cv2.convertScaleAbs(lap) dst = cv2.normalize(dst,None,0,255,cv2.NORM_MINMAX) lap = lap.astype(np.uint8) tmpStream = cv2.imencode(“.jpeg”, lap)[1].tostring() wImg_Laplacian.value = tmpStream tmpStream = cv2.imencode(“.jpeg”, dst)[1].tostring() wImg_Laplacian2.value = tmpStream#초기화 작업Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma)tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring()wImg_Gaussian.value = tmpStreammake_laplacian(Gaussian_img)#Component에 Event 장착IntSlider_Gaussian.observe(on_value_change_Gaussian, names=’value’)IntSlider_Gaussian2.observe(on_value_change_Gaussian2, names=’value’)Laplacian 필터1 결과(ksize=7, sigma=4)Laplacian 필터2: zero-crossingzerocrossing을 사용하여 edge 크기를 비교하여 임계값 이상일 경우에 에지로 정의할 수 있도록 임의의 임계값을 활용하여 edge를 출력할 수 있다.이러한 결과로 인하여 잡음에 덜 민감한 Laplacian Filter를 적용할 수 있다는 장점이 생기게 된다.123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180#Component 선언IntSlider_Gaussian = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian2 = IntSlider( value=0, min=0, max=10, step=1, description=’Sigma: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian3 = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Gaussian4 = IntSlider( value=0, min=0, max=10, step=1, description=’Sigma: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)def layout(header, left, right): layout = AppLayout(header=header, left_sidebar=left, center=None, right_sidebar=right) return layoutwImg_original = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Blur = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Gaussian = Image(layout = Layout(border=”solid”), width=”50%”) wImg_original2 = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Blur2 = Image(layout = Layout(border=”solid”), width=”50%”) wImg_Gaussian2 = Image(layout = Layout(border=”solid”), width=”50%”) items = [IntSlider_Gaussian,IntSlider_Gaussian2]items2 = [wImg_original,wImg_Blur]Gaussian = Box(items)Gaussian2 = Box(items2)box1 = layout(Gaussian,Gaussian2,wImg_Gaussian)items = [IntSlider_Gaussian3,IntSlider_Gaussian4]items2 = [wImg_original2,wImg_Blur2]Gaussian = Box(items)Gaussian2 = Box(items2)box2 = layout(Gaussian,Gaussian2,wImg_Gaussian2)tab_nest = widgets.Tab()tab_nest.children = [box1, box2]tab_nest.set_title(0, ‘Rectangle.jpg’)tab_nest.set_title(1, ‘lena.jpg’)tab_nestimg = cv2.imread(‘./data/rectangle.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img)[1].tostring()wImg_original.value = tmpStreamimg2 = cv2.imread(‘./data/lena.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img2)[1].tostring()wImg_original2.value = tmpStreamdisplay.display(tab_nest)#Event 선언gaussian_ksize, gaussian_sigma = 1,0gaussian_ksize2, gaussian_sigma2 = 1,0 def on_value_change_Gaussian(change): global gaussian_ksize, gaussian_sigma gaussian_ksize = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Blur.value = tmpStream lap = cv2.Laplacian(Gaussian_img,cv2.CV_32F,3) zeroCrossing(lap,0) def on_value_change_Gaussian2(change): global gaussian_ksize, gaussian_sigma gaussian_sigma = change[‘new’] Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Blur.value = tmpStream lap = cv2.Laplacian(Gaussian_img,cv2.CV_32F,3) zeroCrossing(lap,0) def on_value_change_Gaussian3(change): global gaussian_ksize2, gaussian_sigma2 gaussian_ksize2 = change[‘new’] Gaussian_img = cv2.GaussianBlur(img2,ksize=(gaussian_ksize2,gaussian_ksize2),sigmaX=gaussian_sigma2) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Blur2.value = tmpStream lap = cv2.Laplacian(Gaussian_img,cv2.CV_32F,3) zeroCrossing(lap) def on_value_change_Gaussian4(change): global gaussian_ksize2, gaussian_sigma2 gaussian_sigma = change[‘new’] Gaussian_img = cv2.GaussianBlur(img2,ksize=(gaussian_ksize2,gaussian_ksize2),sigmaX=gaussian_sigma2) tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring() wImg_Blur2.value = tmpStream lap = cv2.Laplacian(Gaussian_img,cv2.CV_32F,3) zeroCrossing(lap,0) def SGN(x): if x>=0.01: sign=1 else: sign=-1 return signdef zeroCrossing(lap,option=1): width,height = lap.shape Z = np.zeros(lap.shape,dtype=np.uint8) for y in range(1,height-1): for x in range(1,width-1): neighbors = [lap[y-1,x],lap[y+1,x],lap[y,x-1],lap[y,x+1], lap[y-1,x-1],lap[y-1,x+1], lap[y+1,x-1],lap[y+1,x+1]] mValue = min(neighbors) if SGN(lap[y,x]) != SGN(mValue): Z[y,x] = 255 if option==0: tmpStream = cv2.imencode(“.jpeg”, Z)[1].tostring() wImg_Gaussian.value = tmpStream else: tmpStream = cv2.imencode(“.jpeg”, Z)[1].tostring() wImg_Gaussian2.value = tmpStream#초기화 작업Gaussian_img = cv2.GaussianBlur(img,ksize=(gaussian_ksize,gaussian_ksize),sigmaX=gaussian_sigma)tmpStream = cv2.imencode(“.jpeg”, Gaussian_img)[1].tostring()wImg_Blur.value = tmpStreamlap = cv2.Laplacian(Gaussian_img,cv2.CV_32F,3)zeroCrossing(lap,0)Gaussian_img2 = cv2.GaussianBlur(img2,ksize=(gaussian_ksize2,gaussian_ksize2),sigmaX=gaussian_sigma2)tmpStream = cv2.imencode(“.jpeg”, Gaussian_img2)[1].tostring()wImg_Blur2.value = tmpStreamlap2 = cv2.Laplacian(Gaussian_img2,cv2.CV_32F,3)zeroCrossing(lap2)#Component에 Event 장착IntSlider_Gaussian.observe(on_value_change_Gaussian, names=’value’)IntSlider_Gaussian2.observe(on_value_change_Gaussian2, names=’value’)IntSlider_Gaussian3.observe(on_value_change_Gaussian3, names=’value’)IntSlider_Gaussian4.observe(on_value_change_Gaussian4, names=’value’)Laplacian 필터2: zero-crossing 결과Rectangle.jpg(ksize=7, sigma=5)lena.jpg(ksize=7, sigma=5)Canny 에지 검출위에서 HPF인 미분 필터(Sobel, Laplacian)를 통하여 Feature의 특징을 알아보는 과정을 거쳤다.이러한 Sobel, Laplacian과 같은 단순 이진 Filter를 사용하게 되면 크게 2가지의 단점이 발생하게 된다.1. 검출한 Edge가 필요 이상으로 두꺼워 객체를 훨씬 더 식별 하기 어렵게 만든다.2. 영상의 모든 중요한 Edge를 검출하기 위한 명확한 경계값을 찾기란 불가능할 때가 있다.(zero crossing을 통하여 특정 값 이상의 값을 찾는 것은 가능하다.)이러한 2가지의 주된 문제점을 해결하기 위한 Algorithm이 Canny Algorithm이다.Canny Algorithm1. 가우시안 필터링을하여 영상을 부드럽게 한다.2. Sobel 연산자를 사용하여 기울기(gradient)벡터의 크기(magnitude)를 계산3. 가느다란 에지를 얻기 위해 3 x 3 창을 사용하여 gradient 벡터 방향에서 gradient 크기가 최대값인 화소만 남기고 나머지는 0으로 억제위의 단계를 이해하기 위해서는 먼저 gradient 방향을 이해해야 한다.예시를 살펴보기 위하여 \(y=x^2\)을 생각하면 다음과 같다.사진 출처:sdolnote 블로그위에 그래프에서 파란색 화살표를 보게 되면 Gradient 방향을 쉽게 알 수 있다.x=-1 에서 x가 작아지는 방향을 가르키고 있다.(기울기가 음수)x=1 에서 x가 커지는 방향을 가르키고 있다.(기울기가 양수)위의 결과를 일반화 하여 생각하면 다음과 같다.Gradient Descent가 가르키는 방향은 함수값이 커지는 방향이다.위에서 사용한 도형을 생각하면 아래와 같은 결과를 얻을 수 있다.(흰색은 255, 검은색은 0값을 가진다.)이러한 Gradient Descent의 방향은 Edge와 수직인 관계다.사진 출처:samsjang 블로그위의 사진을 보게 되면 A지점에서 gradient값이 B,C보다 값이 큰지 아닌지를 체크한다.A에서의 값이 가장 크면 그냥 다음단계로 넘어가고 그렇지 않으면 값을 0으로 만든다.4. 연결된 에지를 얻기 위해 두 개의 임계값을 사용. 높은 값의 임계값을 사용하여 gradient 방향에서 낮은 값의 임계값이 나올 때까지 추적하며 에지를 연결하는 히스테리시스 임계값(hysteresis thresholig) 방식 사용사진 출처:samsjang 블로그 A: maxVal보다 값이 크므로 Edge로 판단 B: minVal ~ maxVal사이에 있으나 A와 연결되지 않았으므로 제거 C: minVal ~ maxVal사이에 있으나 A와 연결되어 있으므로 남김 minVal보다 낮은값은 무조건 제거1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980#Component 선언IntSlider_Threshold1 = IntSlider( value=0, min=0, max=300, step=1, description=’threshold1: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)IntSlider_Threshold2 = IntSlider( value=0, min=0, max=300, step=1, description=’threshold2: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)def layout(header, left, right): layout = AppLayout(header=header, left_sidebar=left, center=None, right_sidebar=right) return layoutwImg_original = Image(layout = Layout(border=”solid”), width=”50%”)wImg_Canny = Image(layout = Layout(border=”solid”), width=”50%”)items = [IntSlider_Threshold1,IntSlider_Threshold2]Trick_Bar = Box(items)box = layout(Trick_Bar,wImg_original,wImg_Canny)tab_nest = widgets.Tab()tab_nest.children = [box]tab_nest.set_title(0, ‘Canny 에지검출’)tab_nestimg = cv2.imread(‘./data/lena.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img)[1].tostring()wImg_original.value = tmpStreamdisplay.display(tab_nest)#Event 선언threshold1, threshold2 = 0,0 def on_value_change_Threshold1(change): global threshold1, threshold2 threshold1 = change[‘new’] Canny_img = cv2.Canny(img,threshold1, threshold2) tmpStream = cv2.imencode(“.jpeg”, Canny_img)[1].tostring() wImg_Canny.value = tmpStream def on_value_change_Threshold2(change): global threshold1, threshold2 threshold2 = change[‘new’] Canny_img = cv2.Canny(img,threshold1, threshold2) tmpStream = cv2.imencode(“.jpeg”, Canny_img)[1].tostring() wImg_Canny.value = tmpStream#초기화 작업Canny_img = cv2.Canny(img,threshold1, threshold2)tmpStream = cv2.imencode(“.jpeg”, Canny_img)[1].tostring()wImg_Canny.value = tmpStream#Component에 Event 장착IntSlider_Threshold1.observe(on_value_change_Threshold1, names=’value’)IntSlider_Threshold2.observe(on_value_change_Threshold2, names=’value’)Canny 에지 검출 결과(threshold1=144, threshold2=197)일반적인 필터 연산cv2.filter2D(src, ddepth, kernel[, dst[, anchor[, delta[, borderType]]]]): Kernel을 통하여 Image를 Filtering하는 방법cv2.sepFilter2D(src, ddepth, kernelX, kernelY[, dst[, anchor[, delta[, borderType]]]]): image의 x, y를 각각의 kernel을 통하여 Filtering하는 방법parameter kenelX: image x-축에 적용할 kernel kenelY: image y-축에 적용할 kernelcv2.filter2D()와 cv2.sepFilter2D()에 의한 에지 검출현재 Image를 Filtering하는 Filter를 Sobel Filter를 사용하여 1차미분 기반으로하는 Edge검출을 하였다.1234567891011121314151617181920212223242526272829303132333435src1 = cv2.imread(‘./data/rectangle.jpg’,0)src2 = cv2.imread(‘./data/lena.jpg’,0)kx,ky = cv2.getDerivKernels(1,0,ksize=3)sobelX = ky.dot(kx.T)print(‘kx=’,kx)print(‘ky=’,ky)print(‘sobelX=’,sobelX)gx = cv2.filter2D(src1,cv2.CV_32F,sobelX)gx2 = cv2.sepFilter2D(src2,cv2.CV_32F,kx,ky)kx,ky = cv2.getDerivKernels(0,1,ksize=3)sobelY = ky.dot(kx.T)print(‘kx=’,kx)print(‘ky=’,ky)print(‘sobelY=’,sobelY)gy = cv2.filter2D(src1,cv2.CV_32F,sobelY)gy2 = cv2.sepFilter2D(src2,cv2.CV_32F,kx,ky)mag = cv2.magnitude(gx,gy)ret,edge = cv2.threshold(mag,100,255,cv2.THRESH_BINARY)mag2 = cv2.magnitude(gx2,gy2)ret2,edge2 = cv2.threshold(mag2,100,255,cv2.THRESH_BINARY)plt.figure(figsize=(20,4))imgae1=plt.subplot(1,2,1)imgae1.set_title(‘rectnagle’)plt.imshow(edge, cmap=”gray”)imgae2=plt.subplot(1,2,2)imgae2.set_title(‘lena’)plt.imshow(edge2, cmap=”gray”)plt.show()cv2.filter2D()와 cv2.sepFilter2D()에 의한 에지 검출 결과kx= [[-1.] [ 0.] [ 1.]]ky= [[1.] [2.] [1.]]sobelX= [[-1. 0. 1.] [-2. 0. 2.] [-1. 0. 1.]]kx= [[1.] [2.] [1.]]ky= [[-1.] [ 0.] [ 1.]]sobelY= [[-1. -2. -1.] [ 0. 0. 0.] [ 1. 2. 1.]]Log필터링, 0-교차 에지 영상Log(Laplacian of Gaussian)을 위에서 Laplacian 필터링이 잡음에 민감하다는 특징때문에 위에서 작업하였던 Gausian Filter를 통하여 Noise제거후 Laplacian Filter를 적용시키는 방법이다.2개의 Filter를 적용하는 것이아니라 하나의 Filter로서 선언하여 빠르게 결과를 얻을 수 있다.또한 zerocrossing을 통하여 임계값 이상의 값만 출력하였다.LoG Filter 식$$LoG(x,y) = -\frac{1}{\pi \sigma^4}[1-\frac{x^2+y^2}{2\sigma^2}]exp(-\frac{x^2+y^2}{2\sigma^2})$$123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110#Component 선언IntSlider_Ksize = IntSlider( value=1, min=1, max=20, step=2, description=’ksize: ‘, disabled=False, continuous_update=False, orientation=’horizontal’, readout=True, readout_format=’d’)def layout(header, left, right): layout = AppLayout(header=header, left_sidebar=left, center=None, right_sidebar=right) return layoutwImg_original = Image(layout = Layout(border=”solid”), width=”50%”)wImg_LoG = Image(layout = Layout(border=”solid”), width=”50%”)wImg_ZeroCrossing = Image(layout = Layout(border=”solid”), width=”50%”) items = [wImg_LoG,wImg_ZeroCrossing]Output = Box(items)box = layout(IntSlider_Ksize,wImg_original,Output)tab_nest = widgets.Tab()tab_nest.children = [box]tab_nest.set_title(0, ‘Zero Crossing’)tab_nestimg = cv2.imread(‘./data/lena.jpg’,0)tmpStream = cv2.imencode(“.jpeg”, img)[1].tostring()wImg_original.value = tmpStreamdisplay.display(tab_nest)#Event 선언ksize, sigma = 1,0def on_value_change_Ksize(change): global ksize, sigma ksize = change[‘new’] kernel = logFilter() LoG = cv2.filter2D(img,cv2.CV_32F,kernel) LoG_img = LoG.astype(np.uint8) tmpStream = cv2.imencode(“.jpeg”, LoG_img)[1].tostring() wImg_LoG.value = tmpStream edgeZ = zeroCrossing(LoG) tmpStream = cv2.imencode(“.jpeg”, edgeZ)[1].tostring() wImg_ZeroCrossing.value = tmpStream def logFilter(): global ksize k2 = ksize//2 sigma = 0.3 * (k2-1)+0.8 print(‘sigma=’,sigma) LoG = np.zeros((ksize,ksize),dtype=np.float32) for y in range(-k2,k2+1): for x in range(-k2,k2+1): g = -(x*x+y*y)/(2.0*sigma**2.0) LoG[y+k2,x+k2] = -(1.0+g)*np.exp(g)/(np.pi*sigma**4.0) return LoG def zeroCrossing(lap,thresh=0.01): width,height = lap.shape Z = np.zeros(lap.shape,dtype=np.uint8) for y in range(1,height-1): for x in range(1,width-1): neighbors = [lap[y-1,x],lap[y+1,x],lap[y,x-1],lap[y,x+1], lap[y-1,x-1],lap[y-1,x+1], lap[y+1,x-1],lap[y+1,x+1]] pos = 0 neg = 0 for value in neighbors: if value>thresh: pos+=1 if value 0 and neg > 0: Z[y,x] = 255 return Z #초기화 작업kernel = logFilter()LoG = cv2.filter2D(img,cv2.CV_32F,kernel)LoG_img = LoG.astype(np.uint8)tmpStream = cv2.imencode(“.jpeg”, LoG_img)[1].tostring()wImg_LoG.value = tmpStream edgeZ = zeroCrossing(LoG)tmpStream = cv2.imencode(“.jpeg”, edgeZ)[1].tostring()wImg_ZeroCrossing.value = tmpStream#Component에 Event 장착IntSlider_Ksize.observe(on_value_change_Ksize, names=’value’)Log필터링, 0-교차 에지 영상 결과(ksize=15)모폴리지 연산모폴리지 연산(morphological operation)은 구조 요소(structuring element)를 이용하여 반복적으로 영역을 확장시켜 떨어진 부분 또는 구멍을 채우거나, 잡음을 축소하여 제거하는 등의 연산으로 침식(Erosion), 팽창(dilate), 열기(opening), 닫기(closing)등 이 있다.Erosion해당 픽셀에서 structuring element를 범위안에서 극소값을 찾는 반환한다.그레이 스케일 영상에서는 반복적인 min 필터링과 같다.cv2.erode(img, kenel, iterations=1)parameter img: Erosion을 수행할 원본 이미지 kernel: Erosion을 위한 Kernel iterations: Erosion 반복 횟수Dilation해당 픽셀에서 structuring element를 범위안에서 극대값을 찾는 반환한다.그레이 스케일 영상에서는 반복적인 max 필터링과 같다.cv2.dilate(img, kenel, iterations=1)parameter img: Dilation 수행할 원본 이미지 kernel: Dilation 위한 Kernel iterations: Erosion 반복 횟수모폴로지 연산모폴로지 op연산을 iterations만큼 반복한다. cv2.morphologyEx(img, op, kenel, iterations=1)morphology op op 설명 cv2.MORPH_OPEN dst = dilate(erode(src,kernel),kernel) cv2.MORPH_Close dst = erode(dilate(src,kernel),kernel) cv2.MORPH_GRADIENT dst = dilate(src,kernel)-erode(src,kernel) cv2.MORPH_TOPHAT dst = src – open(src,kernel) cv2.MORPH_BLACKHAT dst = close(src,kernel) – src Structuring Element특정 모양의 structuring element를 만들어서 적용할 수 있다.cv2.getStructuringElement(shape, ksize[, anchor])parameter shape: Element의 모양 MORPH_RECT: 사각형 MORPH_CROSS: 십자 모양 MORPH_ELLIPSE: 타원형 모양 ksize: structuring element 사이즈모폴리지 연산1 erode: 연산결과 최소값 pixel인 검은색(0)이 점점커지거나 흰색이 제거되는것을 확인할 수 있다. dilate: 연산결과 최재값 pixel인 흰색(255)이 점점커지거나 검은색이 제거되는것을 확인할 수 있다.12345678910111213141516171819202122232425262728src = cv2.imread(‘./data/morphology.jpg’,0)kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT,ksize=(3,3))erode = cv2.erode(src,kernel,iterations=5)dilate = cv2.dilate(src,kernel,iterations=7)erode2 = cv2.erode(src,kernel,iterations=1)plt.figure(figsize=(10,10))imgae1=plt.subplot(2,2,1)imgae1.set_title(‘Original’)plt.axis(‘off’)plt.imshow(src, cmap=”gray”)imgae2=plt.subplot(2,2,2)imgae2.set_title(‘erode, iterations=5’)plt.axis(‘off’)plt.imshow(erode, cmap=”gray”)imgae3=plt.subplot(2,2,3)imgae3.set_title(‘dilate, iterations=7’)plt.axis(‘off’)plt.imshow(dilate, cmap=”gray”)imgae4=plt.subplot(2,2,4)imgae4.set_title(‘erode2, iterations=1’)plt.axis(‘off’)plt.imshow(erode2, cmap=”gray”)plt.show()모폴리지 연산1 결과morphologyEx() opening: erode 후 dilate를 연산, 검은물체 내에 흰색 잡음 제거 closing: dilate 후 erode연산, 흰색 물체 속의 검은색 잡음 제거 gradient: dilate – erode, 최대값 – 최소값으로서 급격한 차이를 가지는 Edge추출123456789101112131415161718192021222324252627282930313233343536373839404142src = cv2.imread(‘./data/morphology.jpg’,0)kernel = cv2.getStructuringElement(shape=cv2.MORPH_RECT,ksize=(3,3))closing = cv2.morphologyEx(src,cv2.MORPH_CLOSE,kernel,iterations=5)opening = cv2.morphologyEx(closing,cv2.MORPH_OPEN,kernel,iterations=5)gradient = cv2.morphologyEx(src,cv2.MORPH_GRADIENT,kernel,iterations=2)gradient2 = cv2.morphologyEx(src,cv2.MORPH_GRADIENT,kernel,iterations=5)tophat= cv2.morphologyEx(src,cv2.MORPH_TOPHAT,kernel,iterations=5)balckhat= cv2.morphologyEx(src,cv2.MORPH_BLACKHAT,kernel,iterations=5)plt.figure(figsize=(10,10))imgae1=plt.subplot(3,2,1)imgae1.set_title(‘Closing’)plt.axis(‘off’)plt.imshow(closing, cmap=”gray”)imgae2=plt.subplot(3,2,2)imgae2.set_title(‘Opening’)plt.axis(‘off’)plt.imshow(opening, cmap=”gray”)imgae3=plt.subplot(3,2,3)imgae3.set_title(‘gradient, iterations=2’)plt.axis(‘off’)plt.imshow(gradient, cmap=”gray”)imgae4=plt.subplot(3,2,4)imgae4.set_title(‘gradient2, iterations=5’)plt.axis(‘off’)plt.imshow(gradient2, cmap=”gray”)imgae5=plt.subplot(3,2,5)imgae5.set_title(‘tophat’)plt.axis(‘off’)plt.imshow(tophat, cmap=”gray”)imgae6=plt.subplot(3,2,6)imgae6.set_title(‘balckhat’)plt.axis(‘off’)plt.imshow(balckhat, cmap=”gray”)plt.show()morphologyEx() 결과모폴로지 연산 골격화각각의 T, alphabet에 십자모양, 사각형 모양의 structuring element를 생성 뒤 모폴리지 open 적용1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677src = cv2.imread(‘./data/T.jpg’,0)src2 = cv2.imread(‘./data/alphabet.bmp’,0)ret,A = cv2.threshold(src,128,255,cv2.THRESH_BINARY)ret,B = cv2.threshold(src2,128,255,cv2.THRESH_BINARY)shape1 = cv2.MORPH_CROSSshape2 = cv2.MORPH_RECTC = cv2.getStructuringElement(shape=shape1,ksize=(7,7))D = cv2.getStructuringElement(shape=shape2,ksize=(7,7))def skel_func(img,option=0): done = True A_1 = img A_2 = img skel_dst = np.zeros(src.shape,np.uint8) skel_dst2 = np.zeros(src.shape,np.uint8) while done: erode = cv2.erode(A_1,C) opening = cv2.morphologyEx(erode,cv2.MORPH_OPEN,C) tmp = cv2.subtract(erode,opening) erode2 = cv2.erode(A_2,D) opening2 = cv2.morphologyEx(erode,cv2.MORPH_OPEN,D) tmp2 = cv2.subtract(erode2,opening2) skel_dst = cv2.bitwise_or(skel_dst,tmp) skel_dst2 = cv2.bitwise_or(skel_dst2,tmp2) A_1 = erode.copy() A_2 = erode.copy() done = (cv2.countNonZero(A_1) !=0) and (cv2.countNonZero(A_2) !=0) return skel_dst,skel_dst2 skel_dst,skel_dst2 = skel_func(A)skel_dst3,skel_dst4 = skel_func(B,1)plt.figure(figsize=(10,10))imgae1=plt.subplot(2,3,1)imgae1.set_title(‘T.jpg’)plt.axis(‘off’)plt.imshow(src, cmap=”gray”)imgae2=plt.subplot(2,3,2)imgae2.set_title(‘cv2.MORPH_CROSS’)plt.axis(‘off’)plt.imshow(skel_dst, cmap=”gray”)imgae2=plt.subplot(2,3,3)imgae2.set_title(‘cv2.MORPH_RECT’)plt.axis(‘off’)plt.imshow(skel_dst2, cmap=”gray”)imgae1=plt.subplot(2,3,4)imgae1.set_title(‘alphabet.bmp’)plt.axis(‘off’)plt.imshow(src2, cmap=”gray”)imgae2=plt.subplot(2,3,5)imgae2.set_title(‘cv2.MORPH_CROSS’)plt.axis(‘off’)plt.imshow(skel_dst3, cmap=”gray”)imgae2=plt.subplot(2,3,6)imgae2.set_title(‘cv2.MORPH_RECT’)plt.axis(‘off’)plt.imshow(skel_dst4, cmap=”gray”)plt.show()모폴로지 연산 골격화 결과템플릿 매칭cv2.matchTemplate(image, templ, method[,result): 각 픽셀을 이동하면서 매칭 위치를 탐색하는 방법이다. 템플릿 매칭은 이동문제는 해결할 수 있지만, 회전 및 스케일링된 물체에 대한 매칭은 템플릿을 회전 및 스케일리시켜가며 여러 개의 템플릿을 이용할 수 있으나 어려운 문제이다.템플릿 매칭에서 영상의 밝기를 그대로 사용할 수도 있고, 에지, 코너점, 주파수 변환 등의 특징 공간으로 변환하여 템플릿 매칭을 수행할 수 있으며, 영상의 밝기 등에 덜 민감하도록 정규화 과정이 필요하다.parameter image: 매칭 받을 이미지 templ: 매칭할 이미지 method: 매칭 방법method cv2.TM_SQDIFF: 템플릿 T를 탐색영역 I에서 이동시켜가며 차이의 제곱합계를 계산 cv2.TM_SQDIFF_NORMED: cv2.TM_SQDIFF를 정규화 cv2.TM_CCORR: 템플릿 T를 탐색영역 I에서 이동시켜가며 곱의 합계를 계산 cv2.TM_CCORR_NORMED: cv2.TM_CCORR를 정규화12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273src = cv2.imread(‘./data/alphabet.bmp’,0)src = cv2.bitwise_not(src)tmp_A = cv2.imread(‘./data/A.bmp’,0)tmp_S = cv2.imread(‘./data/S.bmp’,0)tmp_b = cv2.imread(‘./data/b.bmp’,0)tmp_f_90 = cv2.imread(‘./data/f_90.bmp’,0)tmp_A = cv2.bitwise_not(tmp_A)tmp_S = cv2.bitwise_not(tmp_S)tmp_b = cv2.bitwise_not(tmp_b)tmp_f_90 = cv2.bitwise_not(tmp_f_90)dst = cv2.cvtColor(src,cv2.COLOR_GRAY2BGR)font = cv2.FONT_HERSHEY_SIMPLEXR1 = cv2.matchTemplate(src,tmp_A,cv2.TM_SQDIFF_NORMED)minVal,_,minLoc,_ = cv2.minMaxLoc(R1)print(‘TM_SQDIFF_NORMED: ‘,minVal, minLoc)w,h = tmp_A.shape[:2]cv2.rectangle(dst,minLoc,(minLoc[0]+h,minLoc[1]+w),(255,0,0),10)text = ‘Maching: A’dx,dy = minLocdx = dx-10dy = dy-10org = (dx,dy)cv2.putText(dst,text,org,font,1.3,(255,0,0),4)R2 = cv2.matchTemplate(src,tmp_S,cv2.TM_SQDIFF_NORMED)minVal,maxVal,minLoc,maxLoc = cv2.minMaxLoc(R2)w,h = tmp_S.shape[:2]cv2.rectangle(dst,minLoc,(minLoc[0]+h,minLoc[1]+w),(0,255,0),10)text = ‘Maching: S’dx,dy = minLocdx = dx-10dy = dy-10org = (dx,dy)cv2.putText(dst,text,org,font,1.3,(0,255,0),4)R3 = cv2.matchTemplate(src,tmp_b,cv2.TM_SQDIFF_NORMED)minVal,_,minLoc,_ = cv2.minMaxLoc(R3)w,h = tmp_b.shape[:2]cv2.rectangle(dst,minLoc,(minLoc[0]+h,minLoc[1]+w),(0,0,255),10)text = ‘Maching: b’dx,dy = minLocdx = dx-10dy = dy-10org = (dx,dy)cv2.putText(dst,text,org,font,1.3,(0,0,255),4)R4 = cv2.matchTemplate(src,tmp_f_90,cv2.TM_SQDIFF_NORMED)minVal,_,minLoc,_ = cv2.minMaxLoc(R4)w,h = tmp_b.shape[:2]cv2.rectangle(dst,minLoc,(minLoc[0]+h,minLoc[1]+w),(0,255,255),10)text = ‘Maching: f_90’dx,dy = minLocdx = dx-120dy = dy-10org = (dx,dy)cv2.putText(dst,text,org,font,1.3,(0,255,255),4)plt.axis(‘off’)plt.imshow(dst)템플릿 매칭 결과cv2에서 지원하는 템플렛 매칭의 경우 회전이나 글자크기는 Detect하지 못하는 한계점이 분명하게 존재한다.결과를 확인하면 f를 회전한 alphabet은 잘못된 글자를 mapping하는 것을 확인할 수 있다.참조:원본코드참조:Python으로 배우는 OpenCV 프로그래밍문제가 있거나 궁금한 점이 있으면 [email protected]으로 Mail을 남겨주세요.
  • Table of Contents:

Skip links

Paper26 Cooperative Learning for Multi-view Analysis

Paper25 Uncertainty-Guided Progressive GANs for Medical Image Translation

Paper24 Deep learning enables accurate clustering with batch effect removal in single-cell RNA-seq analysis

Paper23 Deep learning based feature-level integration of multi-omics data for breast cancer patients survival analysis

OpenCV-영상 공간 필터링 - Cornor’s Blog
OpenCV-영상 공간 필터링 – Cornor’s Blog

Read More

Python – OpenCV (1) : 영상 필터 convolution & blurring

  • Article author: wjunsea.tistory.com
  • Reviews from users: 39478 ⭐ Ratings
  • Top rated: 3.1 ⭐
  • Lowest rated: 1 ⭐
  • Summary of article content: Articles about Python – OpenCV (1) : 영상 필터 convolution & blurring OpenCV은 이것을 위해 cv2.medianBlur(src, ksize)를 제공합니다. ksize은 커널 크기를 말합니다. 바이레터럴 필터. 블러링은 … …
  • Most searched keywords: Whether you are looking for Python – OpenCV (1) : 영상 필터 convolution & blurring OpenCV은 이것을 위해 cv2.medianBlur(src, ksize)를 제공합니다. ksize은 커널 크기를 말합니다. 바이레터럴 필터. 블러링은 … 이번 포스팅은 책 ‘파이썬으로 만드는 OpenCV 프로젝트’를 보고 공부한 내용을 바탕으로 만들었습니다. 혹시나 틀린 부분이나 수정해야 할 부분이 있다면 댓글 남겨주세요! 영상 처리는 새로운 영상을 얻기 위해..
  • Table of Contents:

컨볼루션(convolution)

블러링(Blurring)

가우시안 블러링

미디언 블러링

바이레터럴 필터

태그

‘Python영상처리’ Related Articles

티스토리툴바

Python - OpenCV  (1) : 영상 필터 convolution & blurring
Python – OpenCV (1) : 영상 필터 convolution & blurring

Read More


See more articles in the same category here: Chewathai27.com/to/blog.

OpenCV의 다양한 필터

728×90

반응형

평균 값 필터(Mean filter)

영상의 특정 좌표 값을 주변 픽셀 값들의 산술 평균으로 설정

픽셀들 간의 그레이스케일 값 변화가 줄어들어 날카로운 에지가 무뎌지고, 영상에 있는 잡음의 영향이 사라지는 효과

실제 영상에 평균 값 필터를 적용한 결과

마스크 크기가 커질수록 평균 값 필터 결과가 더욱 부드러워지나 TRADE OFF 관계로 더 많은 연산량이 필요하다.

#OpenCV 평균 값 필터링 함수 /* src : 입력 영상 ksize : 평균값 필터 크기. (width, height) 형태의 튜플 dst : 결과 영상, 입력 영상과 같은 크기 & 같은 타입. */ cv2.blur(src. ksize, dst=None, anchor=None, borderType=None) -> dst

가우시안 필터

평균값 필터에 의한 블러링의 단점

필터링 대상 위치에서 가까있는 픽셀과 멀리 있는 픽셀이 모두 같은 가중치를 사용하여 평균을 계산 멀리 있는 픽셀의 영향을 많이 받을 수 있다.

(1차원) 가우시안 함수 (Gaussian function)

2차원 가우시안 함수

– 2차원 가우시안 필터마스크 (𝜎 = 1.0)

필터 마스크 크기: 8𝜎 + 1 또는 6𝜎 + 1

#OpenCV 가우시안 필터링 함수 /* src : 입력 영상. 각 채널 별로 처리됨. dst : 출력 영상. src와 같은 크기, 같은 타입. ksize : 가우시안 커널 크기. (0, 0)을 지정하면 sigma 값에 의해 자동 결정됨. sigmaX : x 방향 sigma sigmaY : y 방향 sigma. 0 이면 sigmaX와 같게 설정 borderType: 가장 자리 픽셀 확장 방식. */ cv2.GaussianBlur(src, ksize, sigmaX, dst=None, sigmaY=None, borderType=None) -> dst

다양한 크기의 sigma를 사용한 가우시안 필터링 실행 결과

언샤크 마스크(Unsharp mask) 필터링

날카롭지 않은(unsharp) 영상, 즉, 부드러워진 영상을 이용하여 날카로운 영상을 생성

#그레이 영상에 적용하기 import sys import numpy as np import cv2 src = cv2.imread(‘rose.bmp’, cv2.IMREAD_GRAYSCALE) if src is None: print(‘Image load failed!’) sys.exit() blr = cv2.GaussianBlur(src, (0, 0), 2) dst = np.clip(2.0*src – blr, 0, 255).astype(np.uint8) cv2.imshow(‘src’, src) cv2.imshow(‘dst’, dst) cv2.waitKey() cv2.destroyAllWindows()

#컬러 영상에 적용하기 import sys import numpy as np import cv2 src = cv2.imread(‘rose.bmp’) if src is None: print(‘Image load failed!’) sys.exit() src_ycrcb = cv2.cvtColor(src, cv2.COLOR_BGR2YCrCb) src_f = src_ycrcb[:, :, 0].astype(np.float32) blr = cv2.GaussianBlur(src_f, (0, 0), 2.0) src_ycrcb[:, :, 0] = np.clip(2. * src_f – blr, 0, 255).astype(np.uint8) dst = cv2.cvtColor(src_ycrcb, cv2.COLOR_YCrCb2BGR) cv2.imshow(‘src’, src) cv2.imshow(‘dst’, dst) cv2.waitKey() cv2.destroyAllWindows()

영상의 잡음(Noise)

영상의 픽셀값에 추가되는 원치 않는 형태의 신호

잡음의 종의 종류 : 가우시안 잡음(Gaussian noise), 소금&후츠 잡음(Salt&Pepper)

잡음 제거(1): 미디언 필터

주변 픽셀들의 값들을 정렬하여 그 중앙값(median)으로 픽셀 값을 대체 소금-후추 잡음 제거에 효과적dlek.

#OpenCV 미디언 필터링 함수 /* src : 입력 영상. 각 채널 별로 처리됨 ksize : 커널 크기. 1보다 큰 홀수를 지정. dst : 출력 영상. src와 같은 크기, 같은 타입. */ cv2.medianBlur(src, ksize, dst=None) -> dst

cv2.medianBlur(src, 3)

잡음 제거(1) : 가우시안 필터

가우시안 잡음 제거에는 가우시간 필터가 효과적이다.

잡음 제거(2): 양방향 필터

– 양방향 필터(Bilateral filter)

엣지 보존 잡음 제거 필터(edge-preserving noise removal filter)의 하나로 평균 값 필터 또는 가우시안 필터는 에지 부근에서도 픽셀 값을 평탄하게 만드는 단점이 있음 기준 픽셀과 이웃픽셀과의 거리, 그리고 픽셀 값의 차이를 함께 고려하여 블러링 정도를 조절한다.

(일반적인) 가우시간 필터링: 영상 전체에서 blurring을 적용한다.

양방향 필터 : 엣지가 아닌 부분에서만 blurring을 적용한다.

#OpenCV 양방향 필터링 함수 /* src : 입력 영상. 8비트 또는 실수형, 1채널 또는 3채널. d: 필터링에 사용될 이웃 픽셀의 거리(지름). 음수(-1)를 입력하면 sigmaSpace 값에 의해 자동 결정됨. sigmaColor : 색 공간에서 필터의 표준 편차 sigmaSpace : 좌표 공간에서 필터의 표준 편차 dst : 출력 영상. src와 같은 크기, 같은 타입. borderType : 가장자리 픽셀 처리 방식 */ cv2.bilateralFilter(src, d sigmaColor, sigmaSpace, dst=None, borderType=None) -> dst

src = cv2.imread(‘lenna.bmp’) dst = cv2.bilateralFilter(src, -1, 10, 5)

728×90

반응형

17. 필터(Filter)와 컨볼루션(Convolution) 연산, 평균 블러링, 가우시안 블러링, 미디언 블러링, 바이레터럴 필터

이번 포스팅부터는 영상 필터에 대해 알아보겠습니다. 이번 포스팅 역시 ‘파이썬으로 만드는 OpenCV 프로젝트(이세우 저)’를 정리한 것임을 밝힙니다.

코드: github.com/BaekKyunShin/OpenCV_Project_Python/tree/master/06.filter

시작하기 앞서 용어에 대해 먼저 정의하고 가겠습니다. 영상 처리는 새로운 영상을 얻기 위해 기존 픽셀 값에 어떤 연산을 가해서 새로운 픽셀 값을 얻는 작업입니다. 새로운 픽셀 값을 얻을 때 하나의 픽셀 값이 아닌 그 주변 픽셀들의 값을 활용하는 방법을 공간 영역 필터링(spacial domain filtering)라고 합니다. 또한, 블러링(Blurring)이란 기존의 영상을 흐릿하게 만드는 작업을 뜻합니다.

필터(Filter)와 컨볼루션(Convolution)

컨볼루션 연산은 공간 영역 필터링을 위한 핵심 연산 방법입니다. 블러링 작업을 예로 들어 컨볼루션 연산이 어떻게 진행되는지 살펴보겠습니다.

공간 영역 필터링은 연산 대상 픽셀과 그 주변 픽셀들을 활용하여 새로운 픽셀 값을 얻는 방법이라고 했습니다. 이때 주변 픽셀을 어느 범위까지 활용할지 그리고 연산은 어떻게 할지를 결정해야 합니다. 이런 역할을 하는 것이 바로 커널(kernel)입니다. 커널은 윈도(window), 필터(filter), 마스크(mask)라고도 부릅니다. 아래 그림에서 가운데 있는 3 x 3 짜리 행렬이 바로 커널입니다.

출처: https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641520

위 그림은 3 x 3 커널로 컨볼루션 연산을 하는 예시입니다. 기존 영상에서 픽셀 값 6을 기준으로 주변에 있는 픽셀 값인 3, 0, 1, 2, 1, 4, 2, 2(시계 방향)까지 활용했습니다. 일대일로 대응하는 위치에 있는 커널의 요소와 대응하는 입력 픽셀 값을 곱해서 모두 합한 것을 결과 픽셀 값으로 결정했습니다. 이런 연산을 마지막 픽셀까지 반복하는 것을 컨볼루션 연산이라고 합니다.

출처: https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641520 출처: https://www.slipp.net/wiki/pages/viewpage.action?pageId=26641520

OpenCV에서는 아래 함수로 컨볼루션 연산을 지원합니다.

dst = cv2.filter2D(src, ddepth, kernel, dst, anchor, delta, borderType)

src: 입력 영상, Numpy 배열

ddepth: 출력 영상의 dtype (-1: 입력 영상과 동일)

kernel: 컨볼루션 커널, float32의 n x n 크기 배열

dst(optional): 결과 영상

anchor (optional) : 커널의 기준점, default: 중심점 (-1, -1)

delta(optional): 필터가 적용된 결과에 추가할 값

borderType( optional): 외곽 픽셀 보정 방법 지정

평균 블러링(Average Blurring)

앞서 설명했듯이 블러링은 초점이 맞지 않듯이 영상을 흐릿하게 하는 작업을 뜻합니다. 가장 간단한 블러링 방법으로는 평균 블러링이 있습니다. 평균 블러링은 주변 픽셀 값들의 평균을 적용합니다. 주변 픽셀들의 평균값을 적용하면 픽셀 간 차이가 적어져 선명도가 떨어져 전체적으로 흐릿해집니다.

아래는 5 x 5 평균 블러링 필터를 활용하여 컨볼루션 연산을 적용한 예시입니다.

# 평균 필터를 생상하여 블러 적용 (blur_avg_kernel.py) import cv2 import numpy as np img = cv2.imread(‘../img/yeosu_small.jpg’) ”’ #5×5 평균 필터 커널 생성 —① kernel = np.array([[0.04, 0.04, 0.04, 0.04, 0.04], [0.04, 0.04, 0.04, 0.04, 0.04], [0.04, 0.04, 0.04, 0.04, 0.04], [0.04, 0.04, 0.04, 0.04, 0.04], [0.04, 0.04, 0.04, 0.04, 0.04]]) ”’ # 5×5 평균 필터 커널 생성 —② kernel = np.ones((5,5))/5**2 # 필터 적용 —③ blured = cv2.filter2D(img, -1, kernel) # 결과 출력 cv2.imshow(‘origin’, img) cv2.imshow(‘avrg blur’, blured) cv2.waitKey() cv2.destroyAllWindows()

원본인 여수 야경에 평균 블러링을 적용하니 흐릿해진 것을 볼 수 있습니다. 위 코드에서 np.ones((5, 5))/5**2는 평균 블러링 필터 역할을 합니다. 위에서 맨 처음 컨볼루션 연산을 배울 때는 원본 이미지의 픽셀 값과 그에 대응하는 필터의 픽셀 값을 요소 별로 곱한 뒤 모두 합해줬습니다. 하지만 여기서는 평균 블러링을 적용해야 하므로 5 x 5 필터의 요소 개수인 5**2(=25)로 나누어 준 것입니다.

출처: https://hsg2510.tistory.com/112

필터의 크기가 클수록 평균 블러링을 적용했을 때 선명도가 더 떨어집니다.

출처: https://hsg2510.tistory.com/112

위와 같이 개발자가 직접 커널을 생성하지 않고도 평균 블러링을 적용할 수 있습니다. OpenCV에서는 아래와 같은 평균 블러링 함수를 제공합니다.

dst = cv2.blur(src, ksize, dst, anchor, borderType)

src: 입력 영상, numpy 배열

ksize: 커널의 크기

나머지 파라미터는 cv2.filter2D()와 동일

src: 입력 영상, numpy 배열 ksize: 커널의 크기 나머지 파라미터는 cv2.filter2D()와 동일 dst = cv2.boxFilter(src, ddepth, ksize, dst, anchor, normalize, borderType)

ddepth: 출력 영상의 dtype (-1: 입력 영상과 동일)

normalize(optional): 커널 크기로 정규화(1/ksize²) 지정 여부 (Boolean), default=True

나머지 파라미터는 cv2.filter2D()와 동일

cv2.blur() 함수는 커널의 크기만 정해주면 알아서 평균 커널을 생성해서 평균 블러링을 적용한 영상을 출력합니다. 커널 크기는 일반적으로 홀수로 정합니다. cv2.boxFilter() 함수는 normalize에 True를 전달하면 cv2.blur() 함수와 동일한 기능을 합니다.

아래는 cv2.blur() 함수와 cv2.boxFilter() 함수를 이용하여 평균 블러링을 적용하는 예제 코드입니다.

# 블러 전용 함수로 블러링 적용 (blur_avg_api.py) import cv2 import numpy as np file_name = ‘../img/taekwonv1.jpg’ img = cv2.imread(file_name) # blur() 함수로 블러링 —① blur1 = cv2.blur(img, (10,10)) # boxFilter() 함수로 블러링 적용 —② blur2 = cv2.boxFilter(img, -1, (10,10)) # 결과 출력 merged = np.hstack( (img, blur1, blur2)) cv2.imshow(‘blur’, merged) cv2.waitKey(0) cv2.destroyAllWindows()

cv2.blur() 함수와 cv2.boxFilter() 함수가 동일한 결과를 냈음을 알 수 있습니다.

가우시안 블러링(Gaussian Blurring)

가우시안 분포를 갖는 커널로 블러링 하는 것을 가우시안 블러링이라고 합니다. 가우시안 분포(gaussian distribution)란 정규 분포(normal distribution)이라고도 하는데, 평균 근처에 몰려 있는 값들의 개수가 많고 평균에서 멀어질수록 그 개수가 적어지는 분포를 말합니다.

가우시안 블러링 커널은 아래와 같이 중앙값이 가장 크고 중앙에서 멀어질수록 그 값이 작아집니다.

출처: https://hsg2510.tistory.com/112

첫 번째 커널에서 16으로 나눈 이유는 커널의 모든 요소의 합이 16이기 때문입니다. (1+2+1+2+4+2+1+2+1 = 16) 두 번째 커널도 모든 요소의 합이 256이므로 256으로 나누어준 것입니다. 이런 가우시안 블러링 커널을 적용하면 대상 픽셀에 가까울수록 많은 영향을 주고, 멀어질수록 적은 영향을 주기 때문에 원래의 영상과 비슷하면서도 노이즈를 제거하는 효과가 있습니다.

OpenCV에서는 아래와 같이 가우시안 블러링을 적용하는 함수를 제공합니다.

cv2.GaussianBlur(src, ksize, sigmaX, sigmaY, borderType)

src: 입력 영상

ksize: 커널 크기 (주로 홀수)

sigmaX: X 방향 표준편차 (0: auto)

sigmaY(optional): Y 방향 표준편차 (default: sigmaX)

borderType(optional): 외곽 테두리 보정 방식

src: 입력 영상 ksize: 커널 크기 (주로 홀수) sigmaX: X 방향 표준편차 (0: auto) sigmaY(optional): Y 방향 표준편차 (default: sigmaX) borderType(optional): 외곽 테두리 보정 방식 ret = cv2.getGaussianKernel(ksize, sigma, ktype)

ret: 가우시안 커널 (1차원이므로 ret * ret.T 형태로 사용해야 함)

cv2.GaussianBlur() 함수는 커널 크기와 표준 편차를 전달하면 가우시안 블러링을 적용해줍니다. sigmaX에 0을 전달하면 자동으로 표준편차를 선택해서 사용하고, sigmaY를 생략하면 sigmaX 값과 동일하게 적용합니다.

cv2.getGaussianKernel() 함수는 커널 크기와 표준 편차를 전달하면 가우시안 필터를 반환합니다. 반환된 필터는 1차원이므로 cv2.filter2D() 함수에 사용하려면 ret * ret.T와 같은 형식으로 전달해야 합니다.

# 가우시안 블러링 (blur_gaussian.py) import cv2 import numpy as np img = cv2.imread(‘../img/gaussian_noise.jpg’) # 가우시안 커널을 직접 생성해서 블러링 —① k1 = np.array([[1, 2, 1], [2, 4, 2], [1, 2, 1]]) *(1/16) blur1 = cv2.filter2D(img, -1, k1) # 가우시안 커널을 API로 얻어서 블러링 —② k2 = cv2.getGaussianKernel(3, 0) blur2 = cv2.filter2D(img, -1, k2*k2.T) # 가우시안 블러 API로 블러링 —③ blur3 = cv2.GaussianBlur(img, (3, 3), 0) # 결과 출력 print(‘k1:’, k1) print(‘k2:’, k2*k2.T) merged = np.hstack((img, blur1, blur2, blur3)) cv2.imshow(‘gaussian blur’, merged) cv2.waitKey(0) cv2.destroyAllWindows()

첫 번째로 가우시안 필터를 직접 생성해서 cv2.filter2D() 함수에 전달하여 블러링을 적용했습니다. 두 번째로 cv2.getGaussianKernel() 함수를 이용해 가우시안 커널을 얻었습니다. 얻은 커널을 역시 cv2.filter2D() 함수에 전달하여 블러링을 적용했습니다. 이때 주의할 것은 k2*k2.T와 같은 형태로 전달해야 한다는 점입니다. 마지막으로 cv2.GaussianBlur() 함수를 활용하여 필터를 별도로 구하지 않고 직접 가우시안 블러링을 적용했습니다. 결과 이미지가 작아서 잘 안 보이겠지만 노이즈가 제거된 것을 알 수 있습니다. 이렇듯 가우시안 블러링은 노이즈를 제거하는 효과가 있습니다.

미디언 블러링(Median Blurring)

커널의 픽셀 값 중 중앙값을 선택하는 것을 미디언 블러링이라고 합니다. 미디언 블러링은 소금-후추 잡음을 제거하는 효과가 있습니다. 소금-후추 잡음이란 이미지에 소금과 후추를 뿌린 것과 같이 생긴 잡음을 뜻합니다. OpenCV는 미디언 블러링을 위해 아래 함수를 제공합니다.

dst = cv2.medianBlur(src, ksize)

src: 입력 영상

ksize: 커널 크기

# 미디언 블러링 (blur_median.py) import cv2 import numpy as np img = cv2.imread(“../img/salt_pepper_noise.jpg”) # 미디언 블러 적용 — ① blur = cv2.medianBlur(img, 5) # 결과 출력 merged = np.hstack((img,blur)) cv2.imshow(‘media’, merged) cv2.waitKey(0) cv2.destroyAllWindows()

보시는 바와 같이 미디언 블러링을 적용하니 소금-후추 잡음이 제거되었습니다.

바이레터럴 필터(Bilateral Filter)

지금까지 적용한 블러링은 잡음을 제거하는 효과는 뛰어났지만 그만큼 경계도 흐릿하게 만드는 문제가 있었습니다. 바이레터럴 필터는 이를 개선하기 위해 가우시안 필터와 경계 필터를 결합합니다. 경계도 뚜렷하고 노이즈도 제거되는 효과가 있지만 속도가 느리다는 단점이 있습니다.

dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace, dst, borderType)

src: 입력 영상

d: 필터의 직경(diameter), 5보다 크면 매우 느림

sigmaColor: 색공간의 시그마 값

sigmaSpace: 좌표 공간의 시그마 값

일반적으로 sigmaColor와 sigmaSpace는 같은 값을 사용하며, 값의 범위는 10~150을 권장합니다.

# 바이레터럴 필터와 가우시안 필터 비교 (blur_bilateral.py) import cv2 import numpy as np img = cv2.imread(“../img/gaussian_noise.jpg”) # 가우시안 필터 적용 —① blur1 = cv2.GaussianBlur(img, (5,5), 0) # 바이레터럴 필터 적용 —② blur2 = cv2.bilateralFilter(img, 5, 75, 75) # 결과 출력 merged = np.hstack((img, blur1, blur2)) cv2.imshow(‘bilateral’, merged) cv2.waitKey(0) cv2.destroyAllWindows()

가우시안 필터를 적용했을 때는 경곗값이 흐릿하지만, 바이레터럴 필터를 적용했을 때는 노이즈는 줄면서 경곗값을 유지되는 것을 볼 수 있습니다.

[OpenCV Practice 10 – 1] 이미지 필터링 (Image Filtering & Blurring)

import cv2 import numpy as np import matplotlib.pyplot as plt

이미지 주파수

이미지도 음성 신호처럼 주파수로 표현할 수 있다. 일반적으로 고주파는 밝기 변화가 많은 곳, 즉 경계선(edge) 영역에서 나타나며, 저주파는 밝기 변화가 적은 곳인 배경의 영역에 주로 나타난다. 이를 이용해 고주파를 제거하면 경계선의 변화가 약해져 Blur 처리가 되고, 저주파를 제거하면 배경 영역이 흐려져 대상의 영역(edge)를 뚜렷하게 확인할 수 있다. Low-pass Filter(LPF)와 High-pass Filter(HPF)를 이용해 LPF(저주파만 통과)를 적용하면 노이즈를 제거하거나 Blur 처리를 할 수 있으며 HPF(고주파만 통과)를 적용하면 경계선을 찾을 수 있다.

이미지 필터링 (Image Filtering)

이미지 필터링은 kernel(filter)라고 하는 정방행렬을 정의하고, 이 커널을 이미지 위에서 이동시켜가면서 커널과 겹쳐진 이미지 영역과 연산한 후 그 결과값을 연산을 진행한 이미지 픽셀을 대신하여 새로운 이미지를 만드는 연산이다. (kernel을 사용하는 대부분의 이미지 처리 과정이 이와 비슷하다.) openCV에서는 이미지와 kernel(filter)를 Convolution(합성곱)하여 이미지를 필터링 해주는 함수, cv2.filter2D를 제공한다. 필터링할 이미지와 kernel이 있으면 cv2.filter2D 함수를 통해 필터링된 이미지를 반환한다.

필터링하는 방법에는 여러 종류가 있는데 대표적으로 이미지의 픽셀 값을 해당 픽셀의 이웃과 평균하여 그 값을 취하도록 하는 “Average Filter”가 있다. 커널의 크기를 5 * 5라고 하면, Average Filtering하기 위한 커널의 형태는 다음과 같다.

K = 1 / 25 * [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ]

모든 Filtering 커널 행렬의 전체 원소의 합은 1이 되도록 만들어지기 때문에 총 원소의 합(25)으로 나누어 정규화한다.

해당 커널이 적용되는 방법은 다음과 같다.

1. 픽셀을 중심으로 5 * 5의 영역을 만든다.

2. 이 영역에 속하는 픽셀 값과 커널을 곱하여 그 값을 합친다.

3. 더한 값을 25로 나누고 이 값을 적용한 픽셀 값으로 초기화 한다.

이러한 계산방법 덕분에 커널이 커지면 커질수록 이미지는 더더욱 흐려진다.

img = cv2.imread(‘atom-4.png’) kernel1 = np.ones((5, 5), np.float32)/25 kernel2 = np.ones((11, 11), np.float32)/121 blur1 = cv2.filter2D(img, -1, kernel1) blur2 = cv2.filter2D(img, -1, kernel2) cv2.imshow(‘original’, img) cv2.imshow(‘blur1’, blur1) cv2.imshow(‘blur2’, blur2) cv2.waitKey(0) cv2.destroyAllWindows() images = [img, blur1, blur2] titles = [“original”, “blur1”, “blur2”] plt.figure(figsize=(8, 8)) for i in range(3): plt.subplot(1, 3, i+1) plt.imshow(images[i]) plt.title(titles[i]) plt.axis(‘off’) plt.tight_layout() plt.show()

cv2.filter2D(src, ddepth, kernel [, dst [, anchor [, delta [, borderType]]]]) → dst

1. src : 입력 이미지

2. ddepth : 출력 이미지에 적용되는 이미지의 깊이 (자료형 크기), -1이면 입력과 동일하게 적용된다. 입력이미지와 출력이미지의 조합을 확인해보고 싶으면 Image Filtering에서 depth combination을 확인해보면 된다.

3. kernel : 합성곱 kernel, 해당 kernel은 float 상수로 구성된 단일 채널이어야 한다. 다른 채널에 각각 다른 커널을 적용하고 싶다면 split 함수를 이용해 해당 이미지의 채널을 분리한 뒤 개별적으로 적용해야 한다.

4. anchor : 커널 내에서 필터링된 지점의 상대적 위치를 나타내는 커널의 앵커(닻), 즉 필터링할 이미지 픽셀의 위치가 커널의 어디에 존재해야 하는지 그 기준점을 지정해준다. default = (-1, -1)로 앵커가 커널의 중심에 있음을 의미한다.

5. delta : dst에 저장하기 전에 필터링된 픽셀에 추가적으로 더해주는 값 (bias의 역할)

Trackbar로 kernel 사이즈 조정하기

# Trackbar로 kernel size 조정하기 def onChange(x): pass def filteringTrackbar(): img = cv2.imread(‘The-original-Gaussian-noisy-image-The-Gaussian-noise-parameters-are-zero-mean-and.png’) cv2.namedWindow(‘filtering’) cv2.createTrackbar(‘SIZE’, ‘filtering’, 1, 20, onChange) cv2.imshow(‘filtering’, img) while True: k = cv2.waitKey(0) & 0xFF if k == 27: break size = cv2.getTrackbarPos(‘SIZE’, ‘filtering’) if size == 0: size = 1 kernel = np.ones((size, size), np.float32)/(size * size) dst = cv2.filter2D(img, -1, kernel) cv2.imshow(‘filtering’, dst) cv2.destroyAllWindows()

filteringTrackbar()

이미지 블러링 (Image Blurring)

블러, Blur는 이미지 필터링을 사용하여 이미지를 흐리게 만드는 것을 의미한다. 주로 LPF(Low-pass Filter)를 이용해 고주파 영역을 제거함으로써 노이즈를 제거하거나 경계선을 흐리게 할 수 있다. openCV에서는 아래 4가지 블러링 기술을 제공하고 있다. 1. Averaging

2. Gaussian Filtering

3. Median Filtering

4. Bilateral Filtering

1. Averaging

가장 일반적인 필터링 방법으로 균일한 값을 가전 정규화 된 커널을 이용한 이미지 필터링 방법으로 커널 영역 내에서 평균 값으로 해당 픽셀을 대체한다. 앞서 살펴봤던 Averaging Filtering과 동일한 방법이지만, 차이점으로는 위에서는 직접 kernel을 만들었다면, 이번에는 blur 함수를 통해 정규화된 kernel를 만들었다는 것이다. blur 함수는 boxFilter 함수와 동일하게 적용된다. 커널의 형태는 다음과 같다.

K = 1 / 25 * [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ] [ 1, 1, 1, 1, 1 ]

cv2.blur(src, ksize [, dst [, anchor [, borderType]]]) → dst

정규화된 box filter를 이용해 이미지를 블러처리하는 함수이다.

1. src : 입력 이미지, 채널 수는 상관 없으나 다음과 같은 데이터 타입에만 사용할 수 있다.

(CV_8U, CV_16U, CV_16S, CV_32F, CV_64F)

2. ksize : 커널의 크기 입력 이미지와 커널의 크기 등을 인자로 전달하면, blur 함수 내에서 정규화된 커널을 만들고 해당 커널을 입력 이미지에 적용하여 블러 처리된 이미지를 출력한다.

img = cv2.imread(‘atom-4.png’) img_average = img.copy() k_size = 10 average = cv2.blur(img_average, (k_size, k_size)) plt.figure(figsize=(8, 8)) plt.subplot(1, 2, 1) plt.imshow(img_average) plt.title(‘Original’) plt.axis(‘off’) plt.subplot(1, 2, 2) plt.imshow(average) plt.title(’10 x 10 kernel averaging’) plt.axis(‘off’) plt.show()

2. Gaussian Filtering

앞서 Averaging 방법과 같은 경우는 동일한 값으로 구성된 커널을 사용하지만 Gaussian Filtering 방법의 경우에는 가우시안 함수를 이용한 커널을 적용한다. 즉, 커널의 행렬 값을 가우시안 함수를 통해 수학적으로 생성하여 적용한다. 따라서 중앙 위치에 놓인 픽셀과 가까울 수록 가중치가 높아지고 멀어질 수록 가중치가 작아져 중앙 픽셀과 멀어질 수록 해당 픽셀값에 대한 영향력이 작아진다. 가우시안 필터링의 경우 전체적으로 Gaussian Noise (전체적으로 밀도가 동일한 노이즈, 백색노이즈 등)를 제거하는데 가장 효과적이다.

cv2.GaussianBlur(src, ksize, sigmaX [, dst [, sigmaY [, borderType]]]) → dst

1. src : 입력 이미지. 채널 수는 상관 없으나 다음과 같은 데이터 타입에만 사용할 수 있다.

(CV_8U, CV_16U, CV_16S, CV_32F, CV_64F)

2. ksize : 커널의 크기. GaussianBlur를 적용하기 위한 커널의 크기는 반드시 양수의 홀수이어야 한다.

3. sigmaX : 가우시안 커널의 X방향 표준편차

4. sigmaY : 가우시안 커널의 Y방향 표준편차

(sigmaY = 0이면 sigmaX와 같은 크기가 적용된다. 만약 X와 Y 둘 모두 크기가 0이라면 커널의 너비와 높이에 따라 계산된다.)

img = cv2.imread(‘atom-4.png’) img_gaussian = img.copy() img_gaussian = np.clip((img / 255 + np.random.normal(scale = 0.1, size = img.shape)) * 255, 0, 255).astype(‘uint8’) k_size = 7 gaussian = cv2.GaussianBlur(img_gaussian, (k_size, k_size), 0) plt.figure(figsize=(8, 8)) plt.subplot(1, 2, 1) plt.imshow(img_gaussian) plt.title(‘White Noise’) plt.axis(‘off’) plt.subplot(1, 2, 2) plt.imshow(gaussian) plt.title(‘7 x 7 kernel Gaussian’) plt.axis(‘off’) plt.show()

3. Median Filtering

Median Filtering 방법과 같은 경우에는 따로 kernel을 만들어 합성곱하지 않고, kernel window에 있는 모든 픽셀들을 정렬한 후 중간값을 선택하여 적용한다. 그래서 중간값 필터링의 경우에는 항상 이미지의 일부 픽셀 값으로 대체된다. 이는 점 잡음(salt-and-pepper noise) 제거에 효과적이다.

medianBlur(src, ksize [, dst]) → dst

1. src : 입력 이미지, 채널 수가 1, 3, 4개여야 한다. 또한 이미지 타입은 CV_8U여야 한다.

2. ksize : 커널의 크기. MedianBlur를 적용하기 위한 커널의 크기는 반드시 1보다 큰 홀수여야 한다.

img = cv2.imread(‘atom-4.png’) img_median = img.copy() np.random.seed(0) N = 10000 idx1 = np.random.randint(img_median.shape[0], size = N) idx2 = np.random.randint(img_median.shape[1], size = N) img_median[idx1, idx2] = 0 k_size = 9 median = cv2.medianBlur(img_median, k_size) plt.figure(figsize=(8, 8)) plt.subplot(1, 2, 1) plt.imshow(img_median) plt.title(‘Salt-and-pepper Noise’) plt.axis(‘off’) plt.subplot(1, 2, 2) plt.imshow(median) plt.title(‘9 x 9 kernel Median’) plt.axis(‘off’) plt.show()

4. Bilateral Filtering

지금까지의 Blur 처리는 경계선까지 Blur처리가 되어 경계선이 흐려지게 된다면 Bilateral Filtering(양방향 필터링)의 경우에는 경계선을 유지하면서 Gaussian Filtering이 하는 것처럼 Blur 처리를 해주는 방법이다. 가우시안 필터링에는 두 픽셀의 거리 차이만 고려하지만 양방향 필터링의 경우에는 두 픽셀의 명암값 차이또한 커널에 넣어서 가중치로 곱한다. 따라서 픽셀 값의 차이가 너무 크면 가중치가 0에 가까운 값이 되어 합쳐지지 않으므로 영역과 영역사이의 경계선이 잘 보존될 수 있다.

cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace [, dst [, borderType]]) → dst

1. src : 8 bit, 채널 수가 1, 3인 이미지

2. d : 커널의 크기, 필터링 시 고려할 주변 픽셀의 지름. 양수가 아니면 sigmaSpace로 계산된다.

3. sigmaColor : 색공간 표준편차. 값이 크면 색이 많이 달라도 서로 영향을 미친다.

4. sigmaSpace : 거리공간 표준편차. 값이 크면 멀리 떨어져 있는 픽셀들이 서로 영향을 미친다. d가 양수이면 sigmaSpace에 상관없이 인접한 픽셀들에 영향을 미치지만, 그렇지 않으면 d는 sigmaSpace에 비례한다.

img = cv2.imread(‘atom-4.png’) img_bilateral = img.copy() img_bilateral = np.clip((img / 255 + np.random.normal(scale = 0.1, size = img.shape)) * 255, 0, 255).astype(‘uint8’) k_size = 9 bilateral = cv2.bilateralFilter(img_bilateral, k_size, 75, 75) plt.figure(figsize=(8, 8)) plt.subplot(1, 2, 1) plt.imshow(img_bilateral) plt.title(‘White Noise’) plt.axis(‘off’) plt.subplot(1, 2, 2) plt.imshow(bilateral) plt.title(‘9 x 9 kernel Bilateral’) plt.axis(‘off’) plt.show()

[ openCV 에서의 이미지 Data Type ]

– CV_8U : 8-bit unsigned integer: uchar ( 0..255 )

– CV_8S : 8-bit signed integer: schar ( -128..127 )

– CV_16U : 16-bit unsigned integer: ushort ( 0..65535 )

– CV_16S : 16-bit signed integer: short ( -32768..32767 )

– CV_32S : 32-bit signed integer: int ( -2147483648..2147483647 )

– CV_32F : 32-bit floating-point number: float ( -FLT_MAX..FLT_MAX, INF, NAN )

– CV_64F : 64-bit floating-point number: double ( -DBL_MAX..DBL_MAX, INF, NAN )

일반적으로 Data Type과 채널 수가 같이 표현이 되어 CV_8UC1과 같이 표현된다. (8bit unsigned integer 이면서 채널이 1개)

So you have finished reading the opencv 필터 topic article, if you find this article useful, please share it. Thank you very much. See more: OpenCV 필터 종류, OpenCV 색 필터, OpenCV filter2D, OpenCV high pass filter, OpenCV Convolution, 파이썬 Median filter 구현, OpenCV mean filter c, cv2.filter2d 구현

Leave a Comment