Python/컴퓨터 비전

2024-08-22 필터링, 블러링

nomad06 2024. 7. 25. 12:51

1. 필터링(filtering)

커널(filter)이라고 하는 행렬을 정의하고, 이미지 위에서 이동해가며 커널과 겹쳐진 이니지 영역과 연산을 한 후,
그 결과값을 연산을 진행한 이미지 픽셀을 대신하여 새로운 이미지를 만드는 연산

 

 이미지 필터링 함수


       

          filter2D(영상, 이미지 깊이, 커널의 크기, 중심점 좌표, 추가될 값, 가장자리 화소 처리)
 


[이미지깊이]
* 1: 입력과 동일한 크기의 영상
* 커널: 3 * 3, 5 * 5, ..
* 가장자리 회소 처리
    BORDER_CONSTANT: 000abcdef000
    BORDER_REPLICATE: aaaavcdeffff

 

 

 

2. 블러링(Blurring)

초점이 맞지 않는 듯 영상을 흐릿하게 하는 작업

 

 평균 블러링


       
           cv2.blur(영상, 커널)


- 가장 일반적인 블러링 방법으로 일한 값을 정규화 된 커널을 이용한 이미지 필터링 방법
- 커널 영역 내에서 평균값으로 해당 픽셀을 대체함 
- 주변 픽셀들의 평균값을 적용하면 픽셀 간 차이가 적어져 선명도가 떨어져 전체적으로 흐려짐 
- 필터의 크기가 클수록 평균 블러링을 적용했을 때 선명도가 떨어짐

 

 

 예제: 평균블러링 적용하기



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

          # 이미지 읽기 및 색상 변환 (BGR -> RGB)
          img = cv2.imread('/dog.bmp')
          img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

          # 블러링 적용 (3x3 커널)
          blurred_img = cv2.blur(img_rgb, (3, 3))

          # 다양한 커널 크기로 필터링
          kernel_sizes = [5, 7, 9]

          # Matplotlib을 사용하여 이미지 및 필터링 결과 표시
          plt.figure(figsize=(15, 5))

          # 원본 이미지 및 블러링된 이미지 표시
          plt.subplot(1, 4, 1)
          plt.imshow(img_rgb)
          plt.title('Original Image')
          plt.axis('off')

          plt.subplot(1, 4, 2)
          plt.imshow(blurred_img)
          plt.title('Blurred Image (3x3)')
          plt.axis('off')

          # 다양한 커널 크기로 필터링 및 표시
          for i, k in enumerate(kernel_sizes):
              kernel = np.ones((k, k), dtype=np.float32) / (k ** 2)
              filtered_img = cv2.filter2D(img_rgb, -1, kernel)
             
              plt.subplot(1, 4, i + 3)
              plt.imshow(filtered_img)
              plt.title(f'Kernel size: {k}')
              plt.axis('off')

          plt.tight_layout()
          plt.show()

 
 

 

 

 가우시안 블러링

       

        cv2.GaussianBlur(영상, 출력영상, 커널)
        출력영상: (0, 0)이면 입력 영상과 같은 크기의 영상 생성
        커널: 3 = 3 * 3


- 가우시안 분포를 갖는 커널로 블러링 하는 것(정규 분포, 평균 근처에 몰려 있는 값들의 개수가 많고 평균에서 멀어질수록 그 개수가 적어지는 분포)
- 대상 픽셀의 가까올수록 많은 영향을 주고, 멀어질수록 적은 영향을 주기 때문에 원래의 영상과 비슷하면서도 노이즈를 제거하는 효과가 있음

 

 

 예제 : 평균블러링과 가우시안 블러링 비교


       
        import cv2

        img = cv2.imread('/dog.bmp', cv2.IMREAD_GRAYSCALE)
        dst1 = cv2.GaussianBlur(img, (0,0), 2)
        dst2 = cv2.blur(img, (9, 9))

        cv2.imshow('img', img)
        cv2.imshow('dst1', dst1)
        cv2.imshow('dst2', dst2)
        cv2.waitKey()

 
 

 

 

 미디언 블러링

 
     
         cv2.mdeianBlur(영상, 커널)



- 커널의 픽셀 값 중 중안값을 선택 
- 소금-후추 잡음을 제거하는 효과

 

 

 예제: 미디언블러링 적용하기


       
        import cv2
       
        img = cv2.imread('./noise.bmp', cv2.IMREAD_GRAYSCALE)
        dst = cv2.medianBlur(img, 3)
       
        cv2.imshow('img', img)
        cv2.imshow('dst', dst)
       
        cv2.waitKey()


 

 

 

 

 바이레터럴 필터(Bilateral Filter)_양방향 필터


       

            cv2.bilateralFilter(영상, 픽셀의 거리, 시그마 컬러, 시그마 스페이스)
           픽셀의 
거리(필터의 직경): -1을 입력하면 자동 결정됨
           * 시그마 컬러
: 색공간의 시그마 값
           * 시그마 스페이스
: 좌표 공간의 시그마 (값이 클수록 떨어져 있음 픽셀들의 영향을 미침)


- 잡음을 제거하는 효과는 뛰어났지만, 해당 잡음의 경계도 흐릿하게 만드는 문제 해결하기 위해 나옴
- 경계도 뚜렷하고 노이즈도 제거되는 효과는 있지만 속도가 느리다는 단점이 존재

 

 

 예제: 가우시안 블러와 바이레터럴 필터 비교하기


       
      import cv2

      img = cv2.imread('./gaussian_noise.jpg', cv2.IMREAD_GRAYSCALE)
      dst1 = cv2.GaussianBlur(img, (5, 5), 1)
      dst2 = cv2.bilateralFilter(img, 5, 80, 80)

      cv2.imshow('img', img)
      cv2.imshow('dst1', dst1)
      cv2.imshow('dst2', dst2)

      cv2.waitKey()

 
 

 

 

 

 에지(edge) 검출

       

            cv2.Canny(영상, 최소임계값, 최대입계값, 커널)
        * 최소임계값
, 최대입계값: 두 개의 경계 (Max, Min)을 지정해서 경계에 있는 영역 픽셀을 찾음

 
- 영상에서 화소의 밝기가 급격하게 변하는 부분
- 물체의 윤곽선(경계선)이 해당
- 에지를 검출할 수 있으면 물체의 윤관선을 알 수 있음
- "캐니 에지 검출"은 상당한 수준으로 에지를 신뢰성 있게 검출하는 방법

 

 

 

 예제: 가우시안 블러를 적용하고 Canny 에지 검출


       
        import cv2
        import numpy as np

        img = cv2.imread('./dog.bmp')

        # 중간 값 계산
        med_val = np.median(img)

        # Canny 에지 검출을 위한 하위, 상위 임계값 계산
        lower = int(max(0, 0.7 * med_val))
        upper = int(min(0, 1.3 * med_val))

        dst = cv2.GaussianBlur(img, (3, 3), 0, 0) # 가우시안 블러 적용
        dst = cv2.Canny(dst, lower, upper, 3) # Canny 에지 검출 적용

        cv2.imshow('img', img)
        cv2.imshow('dst', dst)

        cv2.waitKey()


 

 

 

 

 스페이스바를 누를 때마다 영상에 다른 필터링 적용하기 (기본 -> 가우시안 블러 -> 케니필터링)


       

      import cv2

      video_path = 'sea.mp4'

      # 동영상 캡처 객체 생성
      cap = cv2.VideoCapture(video_path)

      # 필터 상태 변수
      filter_state = 0

      # 동영상 읽기 확인
      if not cap.isOpened():
          print("동영상을 로드할 수 없습니다.")
          exit()

      while True:
          ret, frame = cap.read()

          # 동영상이 끝나면 루프 종료
          if not ret:
              break

          # 필터 상태에 따라 필터 적용
          if filter_state == 0:
              filtered_frame = frame  # 기본 영상
          elif filter_state == 1:
              filtered_frame = cv2.GaussianBlur(frame, (15, 15), 0)  # 가우시안 필터링
          elif filter_state == 2:
              gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
              filtered_frame = cv2.Canny(gray, 100, 200)  # 캐니 에지 검출
              filtered_frame = cv2.cvtColor(filtered_frame, cv2.COLOR_GRAY2BGR)  # 다시 BGR로 변환

          # 영상 출력
          cv2.imshow('Video', filtered_frame)

          key = cv2.waitKey(30) & 0xFF

          # 'q' 키를 누르면 종료
          if key == ord('q'):
              break

          # 스페이스바를 누르면 필터 상태 변경
          if key == ord(' '):
              filter_state = (filter_state + 1) % 3

      # 동영상 캡처 객체와 모든 창 닫기
      cap.release()

 
 

 

 

스페이스바를 누를 때마다 캠에 다른 필터링 적용하기 (기본 -> 가우시안 블러 -> 케니필터링)


       
            import cv2
            import numpy as np
           
            def blur_filter(img):
                img = cv2.GaussianBlur(img, (0, 0), 3)
                return img
           
            def canny_filter(img):
                med_val = np.median(img)
                lower = int(max(0, 0.7 * med_val))
                upper = int(min(255, 1.3 * med_val))
           
                dst = cv2.GaussianBlur(img, (3, 3), 0, 0)
                dst = cv2.Canny(dst, lower, upper, 3)
           
                return dst
           
           
            cap = cv2.VideoCapture(0)
           
            cam_mode = 0
           
            while True:
                ret, frame = cap.read()
           
                if cam_mode == 1:
                    frame = blur_filter(frame)
                elif cam_mode == 2:
                    frame = canny_filter(frame)
                cv2.imshow('frame', frame)
           
                key = cv2.waitKey(10)
           
                if key == 27:
                    break
                elif key == ord(' '):
                    cam_mode += 1
                    if cam_mode == 3:
                        cam_mode = 0
           
            cap.release()