--- 본 포스팅은 데이콘 서포터즈 "데이크루 2기" 활동의 일환입니다 ---
- 안녕하세요 데이콘 서포터즈 데이크루 2기 포스(POS)팀의 Rahites입니다 :)
- POS팀은 Python OpenCV Study의 약자로 활동 기간동안 저희 팀은 '파이썬으로 만드는 OpenCV 프로젝트' 책을 가지고 OpenCV를 공부해보고 프로젝트를 진행할 것입니다.
- 자세한 스터디 계획과 운영 방안은 아래의 포스팅에서 확인하실 수 있습니다.
https://dacon.io/codeshare/4759?utm_source=dacrew&utm_medium=432727&utm_campaign=dacrew_2
https://dacon.io/codeshare/5146?utm_source=dacrew&utm_medium=432727&utm_campaign=dacrew_2
지금부터 10주차 활동 시작하겠습니다 ヾ(≧▽≦*)o
파이썬으로 만드는 OpenCV 프로젝트🔥¶
팀 포스¶
위 게시글은 시리즈물📖입니다.¶
1편. OpenCV의 기초, Numpy와 Matplotlib
2편. OpenCV 기초(이미지 입출력, 그리기, 창 관리, 이벤트 처리 등)
3편. 이미지 프로세싱(컬러 스페이스, 스레시홀딩, 이미지 연산, 히스토그램)
4편. OpenCV 기하학적 변환
5편. OpenCV 영상필터
프로젝트1편. Multi-Hand Gesture Recognition(1)
프로젝트2편. Multi-Hand Gesture Recognition(2)
6편. OpenCV 영상분할
7편. OpenCV 영상매칭과 추적
시작하기 앞서,¶
1) 이미지 다운 - https://blog.naver.com/soodagnu/222771919419¶
- 위 블로그에서 img 폴더 안에 이미지를 다운받으세요.
2) 폴더 순서¶
- 이미지 폴더(img)를 다음과 같은 순서로 두고 실습을 진행해주세요.
1. OpenCV와 머신러닝¶
1.1 머신러닝¶
머신러닝은 인공지능의 한 분야로, 컴퓨터가 학습할 수 있도록 하는 알고리즘과 기술을 개발하는 분야입니다. 이는 다른말로 기계가 일일이 코드로 명시하지 않은 동작을 데이터로부터 학습하여 실행가능하도록 알고리즘을 개발하는 연구 분야라고도 합니다. 머신러닝은 특정 작업 (task)에 대하여 꾸준한 경험을 통해 작업의 성능을 높이는 것이 이슈로 두고 일을 진행합니다.
머신러닝은 크게 학습하는 데이터의 값에 따라 회귀와 분류로 나누어 진행됩니다. 개념을 하나씩 살펴봅시다.
1.2 회귀¶
- 회귀 (Regression)는 지도학습 유형 중 하나로 예측 값이 이산값인 것을 의미합니다. 회귀는 여러 개의 독립변수와 한 개의 종속변수 간의 상관관계를 모델링 하는 것이 기본 개념이며 독립변수에 영향을 미치는 회귀 계수 (regression coefficients)와 최적 값을 찾아 종속변수를 예측하는 것입니다.
1.3 분류¶
- 분류 (Classification)란 주어진 데이터를 클래스 별로 구별해내는 과정입니다. 분류 알고리즘을 통해 데이터와 레이블을 학습시키고 모델을 생성하며 학습된 모델을 통해 데이터가 어느 범주에 속한 데이터인지 판단, 예측합니다.
2. k-means 클러스터¶
2.1 k-means¶
- k-means는 비지도학습으로 k개의 군집으로 묶는 알고리즘입니다. 머신러닝의 코드도 있지만 cv2 함수안에서도 kmeans를 이용하여 클러스터링을 할 수 있습니다.
retval, bestlabels, centers = cv2.kmeans(data, K, bestLabels, criteria, attempts, flags)
- data = 처리 대상 데이터 (N x 1, np.float32)
- K = 원하는 묶음 개수
- bestLabels = 결과 데이터
- criteria = 반복 종료 요건
- attempts = 매번 다른 초기 레이블로 실행할 횟수
- flags = 초기 중앙점 선정 방법
- retval = 중앙점과 각 데이터의 거리 합의 제곱
- bestlabels = 결과 레이블(0, 1, ...)
- centers = 각 묶음의 중앙점, 배열
criteria : k-means 알고리즘의 종료 기준; 튜플 (type, max_iter, epsilon) 3개 값
- type) : 종료 기준 타입
- type 1) TERM_CRITERIA_EPS = 정확도가 epsilon에 도달하면 종료
- type 2) TERM_CRITERIA_MAX_ITER = 반복 횟수가 max_iter에 도달하면 종료
- type 3) TERM_CRITERIA_EPS + TERM_CRITERIA_MAX_ITER = 위 두 가지 타입 중 하나라도 만족하면 종료
- epsilon : k-means clustering의 정확도
- max_iter : k-means clustering의 반복 횟수
- type) : 종료 기준 타입
flags : 초기 중앙값 설정 방법; k-means에서 초기 k개의 중앙값을 어떻게 설정하느냐에 따라 결과 차이가 존재
- flag 1) cv2.KMEANS_RANDOM_CENTERS = 초기 중앙값을 랜덤 지정 (가장 기본적인 방법)
- flag 2) cv2.KMEANS_PP_CENTERS = K-means++ 알고리즘을 이용하여 지정 (시간이 소요되지만, 랜덤 지정보다 정확도가 좋음)
- flag 3) cv2.KMEANS_USE_INITIAL_LABELS = 사용자가 k개의 중앙값 지정
데이터를 이용하여 2개의 그룹으로 클러스터링을 하는 k-means를 살펴봅시다.
import numpy as np
import cv2
import matplotlib.pyplot as plt
# 0~150 임의의 숫자 2개 (총 25개)
a = np.random.randint(0, 150, (25, 2))
# 128~255 임의의 숫자 2개 (총 25개)
b = np.random.randint(128, 255, (25, 2))
# a, b를 병합
data = np.vstack((a ,b)).astype(np.float32)
# 반복 중지 요건
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# 평균 클러스터링 적용
## label=결과레이블, center=묶음의중앙점, 2=묶음개수, 10=실행횟수
ret, label, center = cv2.kmeans(data, 2, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# label에 따라 결과 분류
red = data[label.ravel()==0]
blue = data[label.ravel()==1]
# 결과 출력
plt.scatter(red[:,0], red[:,1], c='r')
plt.scatter(blue[:,0], blue[:,1], c='b')
# 각 그룹의 중앙점 출력
plt.scatter(center[0,0], center[0,1], s=100, c='r', marker='s')
plt.scatter(center[1,0], center[1,1], s=100, c='b', marker='s')
plt.show()
대부분 잘 나누어진 것을 확인할 수 있습니다.
다음은 이미지를 그림화하여 출력해보겠습니다.
#이미지 그림화
import numpy as np
import cv2
import matplotlib.pyplot as plt
K = 16 # 군집화 갯수(16컬러) ---①
img = cv2.imread('./img/about.jpeg')
# 군집화를 위한 데이타 구조와 형식 변환 ---②
data = img.reshape((-1,3)).astype(np.float32)
# 반복 중지 요건 ---③
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# 평균 클러스터링 적용 ---④
ret,label,center=cv2.kmeans(data,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# 중심 값을 정수형으로 변환 ---⑤
center = np.uint8(center)
#print(center)
# 각 레이블에 해당하는 중심값으로 픽셀 값 선택 ---⑥
res = center[label.flatten()]
# 원본 영상의 형태로 변환 ---⑦
res = res.reshape((img.shape))
# 결과 출력 ---⑧
merged = np.hstack((img, res))
# 출력 포맷 변경 코드
img = cv2.cvtColor(merged, cv2.COLOR_BGR2RGB)
# 결과 출력
fig = plt.figure(figsize=(10, 8))
plt.imshow(img)
plt.show()
다음 예시는 mnist 손글씨 데이터를 이용하여 손글씨를 클러스터링 해보겠습니다! 먼저, 필요한 함수들을 정의한 py 파일을 다운받아 ipynb 파일과 동일한 경로에 두고 시작하면 됩니다. py파일은 https://blog.naver.com/soodagnu/222771919419 에서 다운받을 수 있습니다!
우리는 mnist_mm.py 파일의 getData() 함수를 이용합니다.
image = cv2.imread('./img/digits.png')
print(image.shape)
(1000, 2000, 3)
# 숫자 손글씨 군집화
import cv2, numpy as np
import matplotlib.pyplot as plt
import mnist_mm
# 공통 모듈로 부터 MINST 전체 이미지 데이타 읽기 ---①
data, _ = mnist_mm.getData()
# 중지 요건
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# 평균 클러스터링 적용, 10개의 그룹으로 묶음 ---②
ret,label,center=cv2.kmeans(data,10,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)
# 중앙점 이미지 출력
for i in range(10):
# 각 중앙점 값으로 이미지 생성 ---③
cent_img = center[i].reshape(20,20).astype(np.uint8)
plt.subplot(2,5, i+1)
plt.imshow(cent_img, 'gray')
plt.xticks([]);plt.yticks([])
plt.show()
print(data)
(1000, 2000, 3)
[[0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] ... [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.] [0. 0. 0. ... 0. 0. 0.]]
3. K-NN 알고리즘¶
3.1 K-NN¶
K-NN란 데이터를 가장 가까운 속성에 따라 분류하여 레이블링하는 알고리즘입니다. 어떤 새로운 데이터로부터 거리가 가까운 K개의 다른 데이터의 레이블을 참고하여 K개의 데이터 중 가장 빈도 수가 높게 나온 데이터의 레이블로 분류하는 알고리즘입니다.
knn = cv2.ml.KNearest_create() : k-NN 알고리즘 객체 생성
- retval, results, neighborResponses, distance = knn.findNearest(samples, k) : 예측
- samples = 입력 데이터
- k = 이웃 범위 지정을 위한 K (1 이상)
- results = 예측 결과 (입력 데이터와 같은 크기의 배열)
- neighborResponses = k 범위 내에 있는 이웃 데이터
- distance = 입력 데이터와 이웃 데이터간 거리
- retval = 예측 결과 데이터 (입력 데이터가 1개일때)
# 영화 장르 분류 문제
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 0~99 사이의 랜덤 값 25x2 ---①
trainData = np.random.randint(0,100,(25,2)).astype(np.float32)
# trainDatat[0]:kick, trainData[1]:kiss, kick > kiss ? 1 : 0 ---②
responses = (trainData[:, 0] >trainData[:,1]).astype(np.float32)
# 0: action : 1romantic ---③
action = trainData[responses==0]
romantic = trainData[responses==1]
# action은 파랑 삼각형, romantic은 빨강색 동그라미로 표시 ---④
plt.scatter(action[:,0],action[:,1], 80, 'b', '^', label='action')
plt.scatter(romantic[:,0],romantic[:,1], 80, 'r', 'o',label="romantic")
# 새로운 데이타 생성, 0~99 랜덤 수 1X2, 초록색 사각형으로 표시 ---⑤
newcomer = np.random.randint(0,100,(1,2)).astype(np.float32)
plt.scatter(newcomer[:,0],newcomer[:,1],200,'g','s', label="new")
# Knearest 알고리즘 생성 및 훈련 --- ⑥
knn = cv2.ml.KNearest_create()
knn.train(trainData, cv2.ml.ROW_SAMPLE, responses)
# 결과 예측 ---⑦
ret, results, neighbours ,dist = knn.findNearest(newcomer, 3)#K=3
print("ret:%s, result:%s, neighbours:%s, dist:%s" \
%(ret, results, neighbours, dist))
# 새로운 결과에 화살표로 표시 ---⑧
anno_x, anno_y = newcomer.ravel()
label = "action" if results == 0 else "romantic"
plt.annotate(label, xy=(anno_x + 1, anno_y+1), \
xytext=(anno_x+5, anno_y+10), arrowprops={'color':'black'})
plt.xlabel('kiss');plt.ylabel('kick')
plt.legend(loc="upper right")
plt.show()
ret:1.0, result:[[1.]], neighbours:[[1. 1. 1.]], dist:[[ 4. 205. 205.]]
화면에 보이는 초록색 네모를 동그라미인 romantic과 같이 분류한 것을 확인할 수 있습니다!
4. SVM¶
- SVM(Support Vector Machine)은 훈련 데이터를 기반으로 클래스를 분류하는 대표적인 분류 알고리즘입니다. 앞서 배운 K-NN 알고리즘은 predict()를 호출하는 시점에 각 요소들 간의 거리 계산이 시작한다면 SVM은 train() 함수에 학습 데이터와 레이블을 전달하면 각 클래스를 구분하는 선을 찾아 그 선을 만족하는 방정식을 구하고 나서 predict() 함수에 예측 데이터를 전달하면 이미 구해 놓은 방정식을 적용해서 어떤 부류인지 예측합니다. SVM은 이 방정식을 모델이라고 하고 한번 구한 모델을 계속해서 재활용 가능합니다. 픽셀 데이터 그 자체를 훈련 데이터로 사용하기도 하지만 픽셀 데이터에서 여러 가지 특징 디스크립터를 추출해서 훈련 데이터로 사용합니다.
SVM 알고리즘¶
- SVM은 각 그룹으로 나뉜 학습 데이터를 받아서 각 그룹의 영역을 나누는 선을 검색
학습 데이터에 이미 각 데이터가 어느 그룹에 속하는지 레이블을 함께 제공하므로 데이터들은 그림과 같이 이미 두 그룹으로 분리
서포트 벡터 : 각 영역의 중심에서 가장 멀고 경계면을 접하고 있는 점
- 서포트 평면 : 각 영역의 서포트 벡터를 지나는 선을 이라고 하고 그 선은 무수히 많음
결정 평면(decision boundary) : 그들 중에 서로의 거리가 가장 먼 선이 지나는 면, 최적 초평면(optimal hyperplane)라고도 함
OpenCV는 SVM을 활용할 수 있는 함수를 제공
svm = cv2.ml.SVM_create() : SVM 알고리즘 객체 생성
svm = cv2.ml.SVM_load(file) : 저장한 SVM 객체를 읽어서 생성
- file : 저장한 파일 경로 svm.setType(type) : SVM 알고리즘 타입 선택
- C_SVC : 파라미터를 이용한 다중 서포트 벡터 분류기
- NU_SVC : Nu 파라미터를 이용한 다중 서포트 벡터 분류기(SVC)
- ONE_SVC : 단일 분류기
- EPS_SVR : 엡실론 서포트 벡터 회귀
- NU_SVR : Nu 서포트 벡터 회귀
svm.setKernel(kernelType=LINEAR) : 커널 타입 선택 - CUSTOM : 사용자 정의 커널
- LINEAR : 선형 커널
- POLY : 다항식 커널
- RBF : 방사형 기저 함수(radial basis function) 커널
- SIGMOID : 시그모이드 함수 커널
- CHI2 : 카이제곱 커널
- INNER : 히스토그램 교차점 커널 svm.setC(val) : C_SVC, EPS_SVR의 C 파라미터 설정 svm.setNu(val) : NU_SVC, NU_SVR의 Nu 파라미터 설정 svm.setP(val) : EPS_SVR의 P 파라미터 설정 svm.setGamma(val) : 커널의 감마 값 설정 svm.setCoef0(val) : 커널의 coeff0 값 설정 svm.setDegree(val) : 커널의 degree 값 설정 svm.trainAuto(trainData, layout, label) : 자동으로 파라미터 설정 및 학습 훈련
SVC(Support Vector Classifier)¶
- 특이점 : 분류가 잘못되거나 서포트 벡터가 상대편 경계면에 너무 가까울 경우
- C_SVC는 setC() 함수에 전달하는 C 파라미터로 특이점이 있을 수 있는 거리를 조정 가능
C 파라미터는 작은 정수에서 매우 큰 정수까지 학습 데이터에 따라 달라질 수 있음
NU_SVC는 숫자로 표현하는 C 파라미터 대신 퍼센트를 표현하는 Nu 파라미터를 setNu() 함수로 전달하고 값은 0.0~1.0 사이의 값만 사용 가능
EPS_SVR은 setP() 함수에 P 파라미터 값을 전달해서 거리 계산의 비용을 판단
현실 데이터는 종종 직선으로 분류할 수 없는 현상이 생기는데 데이터를 직선으로 나눌 수 없고 곡선이나 원형으로 나누어야 할 때는 커널 함수를 써서 데이터의 차원을 바꾸면 처리가 보다 쉽게 가능
setKernel() 함수에 어떤 커널 함수를 사용할지를 지정
Machine 자동 학습¶
- 모든 파라미터 세팅을 자동으로 해주는 trainAuto() 함수가 사용이 편리
- 파라미터를 직접 설정하고 train() 함수를 호출하는 것에 비해 훈련 시간이 훨씬 오래 걸린다는 단점이 존재
- SVM 객체는 save() 함수와 cv2.ml.SVM_load() 함수가 구현되어 있어서 훈련된 객체를 저장했다가 다시 읽어들여서 훈련 과정 없이 사용 가능
import cv2
import numpy as np
import matplotlib.pylab as plt
# 0~158 구간 임의의 수 25 x 2 생성 ---①
a = np.random.randint(0,158,(25,2))
# 98~255 구간 임의의 수 25 x 2 생성 ---②
b = np.random.randint(98, 255,(25,2))
# a, b를 병합, 50 x 2의 임의의 수 생성 ---③
trainData = np.vstack((a, b)).astype(np.float32)
# 0으로 채워진 50개 배열 생성 ---④
responses = np.zeros((50,1), np.int32)
# 25 ~ 50 까지 1로 변경 ---⑤
responses[25:] = 1
# 0과 같은 자리의 학습 데이타는 빨강색 삼각형으로 분류 및 표시 ---⑥
red = trainData[responses.ravel()==0]
plt.scatter(red[:,0],red[:,1],80,'r','^')
# 1과 같은 자리의 학습 데이타는 파랑색 사각형으로 분류 및 표시 ---⑦
blue = trainData[responses.ravel()==1]
plt.scatter(blue[:,0],blue[:,1],80,'b','s')
# 0~255 구간의 새로운 임의의 수 생성 및 초록색 원으로 표시 ---⑧
newcomer = np.random.randint(0,255,(1,2)).astype(np.float32)
plt.scatter(newcomer[:,0],newcomer[:,1],80,'g','o')
# SVM 알고리즘 객체 생성 및 훈련---⑨
svm = cv2.ml.SVM_create()
svm.trainAuto(trainData, cv2.ml.ROW_SAMPLE, responses)
# svm_random.xml 로 저장 ---⑩
svm.save('./svm_random.xml')
# 저장한 모델을 다시 읽기 ---⑪
svm2 = cv2.ml.SVM_load('./svm_random.xml')
# 새로운 임의의 수 예측 ---⑫
ret, results = svm2.predict(newcomer)
# 결과 표시 ---⑬
plt.annotate('red' if results[0]==0 else 'blue', xy=newcomer[0], xytext=(newcomer[0]+1))
print("return:%s, results:%s"%(ret, results))
plt.show()
return:0.0, results:[[0.]]
- 2개의 클래스로 각각 25개씩 난수를 생성
- 이 때 일부 겹치도록 구간을 생성
- SVM 객체를 trainAuto() 함수로 학습
- 새로운 입력 난수를 분류
감사합니다 :)¶
'대외활동 > DACrew 2기' 카테고리의 다른 글
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 9장. OpenCV과 머신러닝편(2) (0) | 2022.06.21 |
---|---|
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 8장. 영상매칭과 추적 (0) | 2022.06.14 |
[ 파이썬으로 만드는 OpenCV 프로젝트🔥] 7장. 영상 분할 (0) | 2022.06.03 |
댓글