본문 바로가기
AI/컴퓨터 비전

07. 이미지 유사도, 영상의 변환

by 사라리24 2024. 7. 18.

 

1. 이미지 유사도

- 픽셀 값의 분표가 서로 비슷하다면 유사한 이미지일 확률이 높음

 

🔵 비교 알고리즘

       

        cv2.compareHist(히스트그램1, 히스트그램2, 알고리즘)

        cv2.HISTCMP_CORREL: 상관관계(1: 완정 일치, -1: 완전 불일치, 0: 무관계)
 
        cv2.HISTCMP_CHISQR: 카이관계(0: 완전 일치, 무한대: 완전 불일치)

       cv2.HISTCMP_INTERSECT: 교차(1: 완전 일치, 0: 완전 불일치)
 
       cv2.BHATTACHARYYA: 밀도함수(0: 완정 일치, 1: 완정 불일치)


 

 

 

예제 : 여러 이미지 간의 히스토그램 유사도를 비교하기



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

          img1 = cv2.imread('./taekwonv1.jpg')
          img2 = cv2.imread('./taekwonv2.jpg')
          img3 = cv2.imread('./taekwonv3.jpg')
          img4 = cv2.imread('./dr_ochanomizu.jpg')

          imgs = [img1, img2, img3, img4]
          hists = []

          # 각 이미지에 대해 히스토그램 계산 및 비교를 수행
          for i, img in enumerate(imgs):
              plt.subplot(1, len(imgs), i+1)
              plt.title('img%d' % (i+1))
              plt.axis('off')
              plt.imshow(img[:, :, ::-1])
              hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
              hist = cv2.calcHist([hsv], [0, 1], None, [180,256], [0, 180, 0, 256])
              cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
              hists.append(hist)


          query = hists[0]
          methods = {'CORREL': cv2.HISTCMP_CORREL, 'CHISQR': cv2.HISTCMP_CHISQR, 'INTERSECT': cv2.HISTCMP_INTERSECT, 'BHATTACHARYYA': cv2.HISTCMP_BHATTACHARYYA}

          # 각 메소드별로 히스토그램 유사도를 계산하고 출력
          for j, (name, flag) in enumerate(methods.items()):
              print('%-10s' % name, end = '\t')

              for i, (hist, img) in enumerate(zip(hists, imgs)):
                  ret = cv2.compareHist(query, hist, flag)
                  # INTERSECT 메소드의 경우 정규화된 결과를 사용하여 출력
                  if flag == cv2.HISTCMP_INTERSECT:
                      ret = ret/np.sum(query)

                  print('img%d:%7.2f' % (i+1, ret), end = '\t')

              print()

          plt.show()

      
 
  • 주석
더보기

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

          img1 = cv2.imread('./taekwonv1.jpg')
          img2 = cv2.imread('./taekwonv2.jpg')
          img3 = cv2.imread('./taekwonv3.jpg')
          img4 = cv2.imread('./dr_ochanomizu.jpg')

          imgs = [img1, img2, img3, img4]
          hists = []

          # 각 이미지에 대해 히스토그램 계산 및 비교를 수행합니다.
          for i, img in enumerate(imgs):
              plt.subplot(1, len(imgs), i+1)
              plt.title('img%d' % (i+1))
              plt.axis('off')
              plt.imshow(img[:, :, ::-1]) # OpenCV는 BGR 순서이므로 RGB 순서로 변환
              hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)  # HSV 색 공간으로 변환
              # 2차원 히스토그램을 계산
              hist = cv2.calcHist([hsv], [0, 1], None, [180,256], [0, 180, 0, 256])
              # 히스토그램을 [0, 1] 범위로 정규화
              cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX)
              # 계산된 히스토그램을 리스트에 추가
              hists.append(hist)

          # 쿼리 이미지로 사용할 첫 번째 이미지의 히스토그램을 선택
          query = hists[0]
          # 비교할 메소드들과 그에 대한 설명
          methods = {'CORREL': cv2.HISTCMP_CORREL, 'CHISQR': cv2.HISTCMP_CHISQR, 'INTERSECT': cv2.HISTCMP_INTERSECT, 'BHATTACHARYYA': cv2.HISTCMP_BHATTACHARYYA}

          # 각 메소드별로 히스토그램 유사도를 계산하고 출력
          for j, (name, flag) in enumerate(methods.items()):
              print('%-10s' % name, end = '\t')

              for i, (hist, img) in enumerate(zip(hists, imgs)):
                  ret = cv2.compareHist(query, hist, flag)
                  # INTERSECT 메소드의 경우 정규화된 결과를 사용하여 출력
                  if flag == cv2.HISTCMP_INTERSECT:
                      ret = ret/np.sum(query)

                  print('img%d:%7.2f' % (i+1, ret), end = '\t')

              print()

          plt.show()

 
  • 이미지 로드 및 표시: 네 개의 이미지를 cv2.imread() 함수를 사용하여 BGR 형식으로 읽어옵니다. matplotlib.pyplot을 사용하여 이미지를 subplot에 표시합니다.
  • HSV 변환 및 히스토그램 계산: 각 이미지를 HSV 색 공간으로 변환하고, cv2.calcHist() 함수를 사용하여 2차원 히스토그램을 계산합니다. 이후 cv2.normalize() 함수를 사용하여 히스토그램을 [0, 1] 범위로 정규화합니다.
  • 히스토그램 비교: 첫 번째 이미지(img1)를 쿼리로 선택하고, 나머지 이미지들과의 히스토그램 유사도를 다양한 메소드를 사용하여 비교합니다. cv2.compareHist() 함수를 사용하여 각 이미지 쌍의 유사도를 계산합니다.
  • 결과 출력: 각 비교 메소드별로 계산된 유사도를 출력합니다. 특히 cv2.HISTCMP_INTERSECT 메소드의 경우 정규화된 결과를 사용하여 출력합니다.
  • 맷플롯립을 사용한 이미지 출력: 각 이미지를 subplot에 표시하여 비교할 이미지들을 시각적으로 보여줍니다.

 

 

 

2. 영상의 변환

- 영상을 구상하는 픽셀의 배치 구조를 변경함으로 전체 영상의 모양을 바꾸는 작업

 

🔵이미지 이동(translate)


       
         M = [1 0 a]
                 [0 1 b]

         x 방향으로 a 만큼
, y 방향으로 b 만큼 이동하는 행렬

         cv2.warpaffine(영상, 2 * 3 변환행렬, 결과, 보간법 알고리즘)


이미지 이동(translate) : 
(0,0)을 매게변수로 전달하면 입력 영상과 같은 행렬을 반환
원래 있던 좌표에 이동시키려는 거리 만큼 연산(shift)

 

🔵보간법 일고리즘(interpolation)



      cv2.INTER_LINEAR: 인접한 4개의 픽셀 값에 거리 가중치 사용 -> 속도는 빠르지만 퀄리티가 좀 떨어짐
      cv2
.INTRE_NEAREST: 가장 가까운 픽셀 값 사용 -> 속도가 가장 빨르지만 퀄리티가 떨어짐
      cv2
.INTER_AREA: 픽셀 영역 관계를 이용한 재샘플링 -> 영역적인 정보를 추출해서 결과 영상을 세팅하는 방법, 다운샘플링시 효과적
      cv2
.INTER_CUBIC: 인접한 16개의 픽셀 값에 사중치를 사용 -> 퀄리티는 가장 촣치만 속도가 떨어짐

 
 

 

예제: 이미지 이동  / cv2.warpaffine() 사용하기


       
        import cv2
        import numpy as np

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

        # [1, 0, a], [0, 1, b]
        aff = np.array([[1, 0, 150], [0, 1, 100]], dtype=np.float32)
        dst = cv2.warpAffine(img, aff, (0, 0))

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


 

 

🔵이미지 크기 변환 (resize)


       

               cv2.resize(영상, 결과, x와 y방향 스케일 비율, 보간법)


영상의 크기를 원본 영상보다 크게 또는 작게 만드는 변환

 

 

 

예제: 이미지 확대하기 / cv2.INTER_NEAREST, cv2.INTER_CUBIC 사용하기


       
        import cv2

        img = cv2.imread('../Day1/dog.bmp')
        dst1 = cv2.resize(img, (1280, 1024), interpolation=cv2.INTER_NEAREST)
        dst2 = cv2.resize(img, (1280, 1024), interpolation=cv2.INTER_CUBIC)

        cv2.imshow('img', img)
        cv2.imshow('dst1', dst1[400:800, 200:600])
        cv2.imshow('dst2', dst1[400:800, 200:600])

        cv2.waitKey()




 

 

🔵 회전(rotation) 


       
            cv2.getRotationMatrix2D(중심 좌표, 회전각도, 확대비율) -> affine 행렬


영상의 특정 각도 만큼 회전시키는 변환(반시계 방향)

* 회전각도: 반시계방향(기본값), 음수는 시계방향
* 확대비율: 0~1 사이의 실수

 

예제: 이미지 회전시키기


       
        import cv2

        img = cv2.imread('../Day1/dog.bmp')
        cp = (img.shape[1] / 2, img.shape[0] / 2)

        rot = cv2.getRotationMatrix2D(cp, 30, 0.5)
        dst = cv2.warpAffine(img, rot, (0, 0))

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

 

 

🔵투시변환(perspective)

       

        cv2.getPerspectiveTransforms(영상, 4개의 결과 좌표점) -> 투시 변환 행렬
        cv2
.wrapPerspective(영상, 투시 변환 행렬, 결과 영상 크기)

 
- 직사각형 형태의 영상을 임의의 입체감 있는 사각형 형태로 변경할 수 있는 변환
- 원본 영상에 있는 직선은 결과 영상에서 그대로 유지 되지 않고 평행 관계가 깨질 수 있음
- 투시 변환은 보통 3*3 크기의 실수 행렬로 표현
- 8개의 파라미터로 표현할 수 있지만, 좌표 계산 편의상 9개의 원소를 갖는 행렬로 표현

 

 

예제 : 이미지 투시변환하기



        import cv2
        import numpy as np

        img = cv2.imread('./pic.jpg')
        w, h = 600, 400

        srcQuad = np.array([[370, 173], [1220, 155], [1420, 840], [210, 850]], np.float32)
        dstQuad = np.array([[0, 0], [w, 0], [w, h], [0, h]], np.float32)

        pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
        dst = cv2.warpPerspective(img, pers, (w, h))

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


 

 

방법1


       
        import cv2
        import numpy as np

        # 전역 변수 선언
        points = []
        dragging_point_index = -1
        image_path = './namecard.jpg'

        # 마우스 이벤트 핸들러 함수
        def mouse_handler(event, x, y, flags, param):
            global points, image, dragging_point_index
            if event == cv2.EVENT_LBUTTONDOWN:
                for i, point in enumerate(points):
                    if abs(point[0] - x) < 10 and abs(point[1] - y) < 10:
                        dragging_point_index = i
                        break
            elif event == cv2.EVENT_MOUSEMOVE:
                if dragging_point_index != -1:
                    points[dragging_point_index] = (x, y)
                    redraw_image()
            elif event == cv2.EVENT_LBUTTONUP:
                dragging_point_index = -1

        def redraw_image():
            global image
            image = orig_image.copy()
            for i, point in enumerate(points):
                cv2.circle(image, point, 10, (255, 0, 255), -1)  # 큰 핑크색 점
                if i > 0:
                    cv2.line(image, points[i-1], point, (255, 0, 255), 2)  # 핑크색 선
            if len(points) == 4:
                cv2.line(image, points[3], points[0], (255, 0, 255), 2)  # 마지막 점과 첫 점을 연결하는 선
            cv2.imshow("Image", image)

        # 이미지를 불러오고 초기화
        image = cv2.imread(image_path)
        orig_image = image.copy()

        # 초기 네 점 설정 (이미지의 모서리 근처에 배치)
        h, w, _ = orig_image.shape
        points = [(50, 50), (w - 50, 50), (w - 50, h - 50), (50, h - 50)]

        redraw_image()
        cv2.setMouseCallback("Image", mouse_handler)
        print("이미지에서 네 점을 선택하고 움직이세요. Enter 키를 누르면 완료됩니다.")

        while True:
            key = cv2.waitKey(1) & 0xFF
            if key == 13:  # Enter 키를 누르면 변환된 이미지를 새 창에 표시
                if len(points) == 4:
                    # 원본 이미지의 크기를 얻음
                    h, w, _ = orig_image.shape

                    # 선택한 네 점
                    pts1 = np.float32(points)

                    # 대상 좌표 설정 (직사각형으로 변환)
                    pts2 = np.float32([[0, 0], [w, 0], [w, h], [0, h]])

                    # 변환 행렬 계산
                    matrix = cv2.getPerspectiveTransform(pts1, pts2)

                    # 원근 변환 적용
                    result = cv2.warpPerspective(orig_image, matrix, (w, h))

                    # 결과 이미지 표시
                    cv2.imshow("Transformed Image", result)
                    print("변환된 이미지가 새 창에 표시됩니다.")
                else:
                    print("네 점이 선택되지 않았습니다.")
            elif key == 27:  # Esc 키를 누르면 종료
                break

        cv2.destroyAllWindows()




 

 

 

방법2


       
      import cv2
      import numpy as np
      import sys

      img = cv2.imread('./namecard.jpg')
      h, w = img.shape[:2]
      dh = 500
      # A4용지 크기: 210*297mm
      dw = round(dh * 297 / 210)

      srcQuad = np.array([[30, 30], [30, h-30], [w-30, h-30], [w-30, 30]], np.float32)
      dstQuad = np.array([[0, 0], [0, dh], [dw, dh], [dw, 0]], np.float32)

      dragSrc = [False, False, False, False]

      def drawROI(img, corners):
          cpy = img.copy()
          c1 = (192, 192, 255)
          c2 = (128, 128, 255)

          for pt in corners:
              cv2.circle(cpy, tuple(pt.astype(int)), 25, c1, -1)

          cv2.line(cpy, tuple(corners[0].astype(int)), tuple(corners[1].astype(int)), c2, 2)
          cv2.line(cpy, tuple(corners[1].astype(int)), tuple(corners[2].astype(int)), c2, 2)
          cv2.line(cpy, tuple(corners[2].astype(int)), tuple(corners[3].astype(int)), c2, 2)
          cv2.line(cpy, tuple(corners[3].astype(int)), tuple(corners[0].astype(int)), c2, 2)

          return cpy

      def onMouse(event, x, y, flags, param):
          global srcQuad, dragSrc, ptOld, img

          if event == cv2.EVENT_LBUTTONDOWN:
              for i in range(4):
                  if cv2.norm(srcQuad[i] - (x, y)) < 25:
                      dragSrc[i] = True
                      ptOld = (x, y)
                      break

          if event == cv2.EVENT_LBUTTONUP:
              for i in range(4):
                  dragSrc[i] = False

          if event == cv2.EVENT_MOUSEMOVE:
              for i in range(4):
                  if dragSrc[i]:
                      srcQuad[i] = (x, y)
                      cpy = drawROI(img, srcQuad)
                      cv2.imshow('img', cpy)
                      ptOld = (x, y)
                      break


      disp = drawROI(img, srcQuad)
      cv2.namedWindow('img')
      cv2.setMouseCallback('img', onMouse)
      cv2.imshow('img', disp)

      while True:
          key = cv2.waitKey()
          if key == 13:
              break
          elif key == 27:
              sys.exit()

      pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)
      dst = cv2.warpPerspective(img, pers, (dw, dh), flags=cv2.INTER_CUBIC)
      cv2.imshow('dst', dst)
      cv2.waitKey()






 

 

'AI > 컴퓨터 비전' 카테고리의 다른 글

09. 모폴로지 변환  (0) 2024.07.23
08. 필터링, 블러링  (0) 2024.07.22
06. 이진화  (0) 2024.07.18
05. 마스크, 관심영역  (0) 2024.07.18
04. 평활화, 색공간, CLAHE, 정규화  (1) 2024.07.17