【CV】Computer Vision at FastCampus3, chap11~13

  1. FastCampus 사이트의 Computer vision 강의 내용 정리
  2. 구글링을 해도 되지만은, 필요하면 강의를 찾아서 듣기
  3. FastCampus - Computer vision Lecture
  4. 이전 Post Link

chap11 - Machine Learning

  1. 머신러닝 이해하기

  2. OpenCV 머신 러닝 클래스

    • image-20210131104310345
    • 위의 어떤 방법으로 머신러닝 알고리즘 객체를 생성한 후, train(), predict() 함수를 사용하면 된다.
    • cv2.ml_StatModel.train(samples, layout, responses)
    • cv2.ml_StatModel.predict(samples, results=None, flags=None)
    • 예제코드를 통해서 공부할 예정이고, 이번 수업에서는 아래를 공부할 예정
      1. KNearest : K 최근접 이웃 알고리즘은 샘플 데이터와 인접한 K개의 학습 데이터를 찾고, 이 중 가장 많은 개수에 해당하는 클래스를 샘플 데이터 클래스로 지정
      2. SVM : 두 클래스의 데이터를 가장 여유 있게 분리하는 초평면을 구함
  3. k최근접 이웃 알고리즘

    • KNearest 이론

      • image-20210131105134458
      • 원래 파란, 빨간 백터만 있는 공간에, 초록색이 들어왔다고 가정하다.
      • 1번은 파랑 클래스, 2번은 빨강 클래스라고 정하면 될 것 같다.
      • 3번은 뭘까?? 이것을 결정하기 위해, 초록색에 대해서 가까운 k개의 백터를 찾는다. 그리고 가장 많이 뽑힌 클래스를 3번의 클래스라고 정하는 방법이다.
    • KNearest 코드

      • cv2.ml.KNearest_create() -> retval

      • # Knnplane.py 아래의 순서대로 OpenCV 사용
               
        # 이거 사용할거다. 라는 의미의 객체 생성
        knn = cv2.ml.KNearest_create()
        # 백터 저장
        knn.train(trainData, cv2.ml.ROW_SAMPLE, labelData)
        # Inference
        ret, _, _, _ = knn.findNearest(sample, k_value)
        
      • Knnplane.py 코드는 tuple(x,y,label) 의 다수 포인트를 이용해서, 2차원 평면상의 임의 포인트는 (3개의 클래스 중) 어떤 클래스를 가지는지 결정하여 색칠하는 코드이다. 어렵지 않으니 필요하면 참고
  4. KNN 필기체 숫자 인식 (KNearest 사용)

    • cv2.matchTemplate(chap8-segmentation-detection) 를 사용해서 필기체 인식을 했었다. 하지만 그 방법은 똑같은 폰트를 사용한 숫자여야하고 레이블링 후 숫자 부분영상의 정규화도 해야한다. (이전 post 참고)

    • image-20210131112211954

    • # 학습 데이터 & 레이블 행렬 생성
      digits = cv2.imread('digits.png?raw=tru', cv2.IMREAD_GRAYSCALE)
      h, w = digits.shape[:2]
           
      # 배열 분할 하기 h_split,v_split = https://rfriend.tistory.com/359
      cells = [np.hsplit(row, w//20) for row in np.vsplit(digits, h//20)]
      cells = np.array(cells) # cells: shape=(50, 100, 20, 20), dtype: uint8
           
      # 위 사진과 같이 배열 및 클래스 저장
      train_images = cells.reshape(-1, 400).astype(np.float32)
      train_labels = np.repeat(np.arange(10), len(train_images)/10)
      # train_images: shape=(5000, 400), dtype=**float32**, train_labels: shape=(5000, ), dtype=**int32**
           
      # KNN 학습
      knn = cv2.ml.KNearest_create()
      knn.train(train_images, cv2.ml.ROW_SAMPLE, train_labels)
      # Inference
      ret, _, _, _ = knn.findNearest(test_image, 5)
      
  5. SVM 알고리즘 (아주 간략히만 설명한다. 자세한 수학적 설명은 직접 찾아공부)

    • SVM : 기본적으로 두 개의 그룹(데이터)을 분리하는 방법으로 데이터들과 거리가 가장 먼 초평면(hyperplane)을 선택하여 분리하는 방법 (maximum margin classifier)
    • 아래의 1번과 2번 라인이 아닌, 3번의 라인처럼, 직선/평면과 가장 거리가 최소한 서포트 백터와의 거리가 최대maximum가 되도록 만드는 직선/평면(n차원 백터에 대해서 n-1차원 평면)을 찾는다.
    • image-20210131125416110
    • image-20210131132413371
    • 수학적 수식은 위와 같다. 자세한 내용은 생략한다. 여기서 w와 x는 백터이다. w*x + b = 0 이 n차원 백터에 대해서 n-1차원 평면식이 되겠다. 자세한 수학적 설명은 강의에서도 가르쳐주지 않으니 직접 찾아서 공부할 것.
    • 위의 백터들은 완벽하게 분류가 되어 있는데, 오차가 있는 경우의 SVM을 찾는 방법은 다른 알고리즘을 사용한다. 그 알고리즘은 Soft margin, C-SVM 이라고 부른다.
    • 하지만 SVM은 선형 분류 알고리즘이다. 백터들을 분류하기 위해 직선(선형식)으로는 분류가 안된다면 SVM을 사용할 수 없다. 물론 차원을 확장하면 선형으로 분리 가능 할 수 있다. 아래의 예제로만 알아두고 정확하게는 나중에 정말 필요하면 그때 찾아서 공부하기.
    • image-20210131132846308
    • 여기서는 z에 관한 방적식으로 차원을 확장했는데, 일반적으로 z는 조금 복잡하다. kernel trick이라는 방법을 통해서 차원 확장을 이루는데, 이것도 필요함 그때 공부.
  6. OpenCV SVM 사용하기

    • 객체 생성 : cv2.ml.SVM_create() -> retval

    • SVM 타입 지정 : cv.ml_SVM.setType(type) -> None

    • SVM 커널 지정 : cv.ml_SVM.setKernel(kernelType) -> None

    • SVM 자동 학습(k-폴드 교차 검증) : cv.ml_SVM.trainAuto(samples, layout, respo) -> retval

    • # svmplane.py
      # trains and labels 임으로 만들어 주기
      trains = np.array([[150, 200], [200, 250], [100, 250], [150, 300], [350, 100], [400, 200], [400, 300], [350, 400]], dtype=np.float32)
      labels = np.array([0, 0, 0, 0, 1, 1, 1, 1])
      # set SVM
      svm = cv2.ml.SVM_create()
      svm.setType(cv2.ml.SVM_C_SVC)
      svm.setKernel(cv2.ml.SVM_LINEAR) # 직선, 평명 찾기
      #svm.setKernel(cv2.ml.SVM_RBF) # 곡선, 곡면 찾기
           
      svm.trainAuto(trains, cv2.ml.ROW_SAMPLE, labels)
           
      print('C:', svm.getC()) # 초평면 계산 결과1
      print('Gamma:', svm.getGamma()) # 초평면 계산 결과2
           
      w, h = 500, 500
      img = np.zeros((h, w, 3), dtype=np.uint8)
      for y in range(h):
          for x in range(w):
              test = np.array([[x, y]], dtype=np.float32)
              _, res = svm.predict(test)  # predict
              ret = int(res[0, 0])
              if ret == 0: img[y, x] = (128, 128, 255) 
              else: img[y, x] = (128, 255, 128) 
      color = [(0, 0, 128), (0, 128, 0)]
      for i in range(trains.shape[0]):
          x = int(trains[i, 0])
          y = int(trains[i, 1])
          l = labels[i]
          cv2.circle(img, (x, y), 5, color[l], -1, cv2.LINE_AA)
      
    • image-20210131133359985
  7. HOG(descriptor = 특징값 vector로 추출)를 이용한, SVM 기반 필기체 숫자 인식

    • image-20210202105940306

    • 이전에는 hog.setSVMDetector를 사용했지만, hog.compute(img) 를 사용한다.

    •  # svmdigits.py 자세한 내용은 코드를 참고하기 (필요하면..)
       hog = cv2.HOGDescriptor((20, 20), (10, 10), (5, 5), (5, 5), 9)
       desc = []
       for img in cells:
       	desc.append(hog.compute(img))
       train_desc = np.array(desc).squeeze().astype(np.float32)
       train_labels = np.repeat(np.arange(10), len(train_desc)/10)
            
       # SVM 학습
       svm = cv2.ml.SVM_create()
       svm.setType(cv2.ml.SVM_C_SVC)
       svm.setKernel(cv2.ml.SVM_RBF)
       svm.setC(2.5)
       svm.setGamma(0.50625)
       svm.train(train_desc, cv2.ml.ROW_SAMPLE, train_labels)
            
       # inference
       test_image = cv2.resize(img, (20, 20), interpolation=cv2.INTER_AREA)
       test_desc = hog.compute(test_image)
       _, res = svm.predict(test_desc.T)
       print(int(res[0, 0]))
      
  8. 숫자 영상 정규화

    • 숫자 이미지는 숫자의 위치, 회전,크기 등등에 따라서 다양한 모습을 갖추기 때문에, 숫자 위치를 정규화할 필요가 있음

    • 숫자 영상의 무게 중심이 전체 영상 중앙이 되도록 위치 정규화

    • 무게 중심을 찾아주는 함수 사용 m = cv2.moments(img)

    • def norm_digit(img):
          # cv2.moments의 사용법은 아래와 같다
          m = cv2.moments(img)
          cx = m['m10'] / m['m00']
          cy = m['m01'] / m['m00']
          h, w = img.shape[:2]
          aff = np.array([[1, 0, w/2 - cx], [0, 1, h/2 - cy]], dtype=np.float32)
          dst = cv2.warpAffine(img, aff, (0, 0))
          return dst
           
      desc = []
      for img in cells:
          img = norm_digit(img)
          desc.append(hog.compute(img))
      # 나머지는 윗 코드와 같다
      
  9. k-평균 알고리즘

    • 주어진 데이터가지고, 이미지를 k개의 구역으로 나누는 군집화 알고리즘 (비지도 학습으로 고려)
    • image-20210208113107195
    • image-20210202114926500
    • 동작순서
      1. 임의의 (최대한 서로 멀리) K개 중심을 선정 (분할 평면도 대충 적용. Ex) 두 임의의 점의 중점을 지나는 수직 선)
      2. 모든 데이터에 대해서 가장 가까운 중심을 선택 (추적 때 배운 평균 이동 알고리즘과 비슷)
      3. 각 군집에 대해 중심을 다시 계산
      4. 중심이 변경되면 2번과 3번 과정을 반복
      5. 중심 변경이 적어지면 종료
    • 컬러 영상 분할
      • 컬러영상에서 대표가 되는 칼라 픽셀 값을 선택하는 알고리즘. 원본 이미지에서 위의 k-mean 알고리즘을 수행하는게 아니라, RGB&HS 공간에서 위의 알고리즘을 수행해 ‘대표필셀값’을 찾는다
      • image-20210202114559908
      • cv2.kmeans(data, K, bestLabels, criteria, attempts) -> retval, bestLabels, centers (자세한 파라메터 설정은 강의나 공식 문서 참고)
      • k-means 알고리즘을 이용한 컬러 영상 분할 예제 실행 결과 - kmeans.py 파일 참고 (코드는 매우 간단)
      • image
  10. 실전 코딩: 문서 필기체 숫자 인식

    • image
    • 앞에서 배운것 만을 잘 활용한다.
    • 순서 요약
      1. 레이블링 이용 → 각 숫자의 바운딩 박스 정보 추출
      2. 숫자 부분 영상을 정규화
      3. HOGDescriptor 로 descriptor 추출
      4. 이미 학습된 SVM으로 predict 수행

chap - 12, 13 딥러닝

  • cv2.dnn 모듈을 사용해서 합습 및 추론
  • 필요하면 그때 공부하자.

© All rights reserved By Junha Song.