2021/03/03 - [기록 note] - 2021-03-03(OpenCV_10)
knn
### KNN
import numpy as np
import cv2
#트랙바
def on_k_changed(pos):
global k_value
k_value = pos
if k_value < 1:
k_value = 1
trainAndDisplay()
def addPoint(x, y, c):
train.append([x, y]) #x,y를 리스트로 묶어서 train에 추가
label.append([c])
def trainAndDisplay():
trainData = np.array(train, dtype=np.float32) #ndarray로 변환(float 타입)
labelData = np.array(label, dtype=np.int32)
#ROW_SAMPLE: 데이터 형태가 한 행에 들어간다는 의미
knn.train(trainData, cv2.ml.ROW_SAMPLE, labelData) #90개 점
#주의
#trainData shape = (90,2), dtype=float32
#labelData shape = (90,1) dtype=int32
h, w = img.shape[:2]
#각각의 좌표 표현을 위한 아래의 for loop는 시간이 오래걸림
for y in range(h):
for x in range(w):
#img(500*500)영상의 각각의 점들을 sample에 넣고 예측, 각 한 점씩 예측한다
#(1,0),(2,0),....,(499,499)
sample = np.array([[x, y]]).astype(np.float32) #샘플 하나짜리, (x,y)
ret, result, _, _ = knn.findNearest(sample, k_value) #예측 코드
# print('ret',ret)
# print('result',result)
'''
ret: 예측한 클래스 정보 들어있음(0 or 1 or 2)
result: 좌표를 하나 예측했으므로 1*1인 레이블 행렬을 반환
현재 점이 하나이기 때문에 ret 값과 result의 (0,0) 값은 같다
ret = result[0,0]
'''
ret = int(ret)
if ret == 0:
img[y, x] = (128, 128, 255) #0번클래스면 빨간색에 근접한 색
elif ret == 1:
img[y, x] = (128, 255, 128) #1번 녹색
elif ret == 2:
img[y, x] = (255, 128, 128) #2번 파란색
#제너레이션된 점(3시그마)들을 화면에 보여주는 코드
for i in range(len(train)):
x, y = train[i]
l = label[i][0]
if l == 0:
cv2.circle(img, (x, y), 5, (0, 0, 128), -1, cv2.LINE_AA)
elif l == 1:
cv2.circle(img, (x, y), 5, (0, 128, 0), -1, cv2.LINE_AA)
elif l == 2:
cv2.circle(img, (x, y), 5, (128, 0, 0), -1, cv2.LINE_AA)
cv2.imshow('knn', img)
'''
2차원 평면에 랜덤하게 점을 찍은 후 3개의 클래스로 구분
'''
# 학습 데이터 & 레이블
train = [] #점들의 좌표
label = []
k_value = 1
img = np.full((500, 500, 3), 255, np.uint8)
knn = cv2.ml.KNearest_create()
# 랜덤 데이터 생성
NUM = 30
rn = np.zeros((NUM, 2), np.int32) #결과를 받을 행렬 생성, 30행2열, 60개요소
# (150, 150) 근방의 점은 0번 클래스로 설정
cv2.randn(rn, 0, 50) #가우시안 랜덤, 평균: 0, 표쥰편차:50
for i in range(NUM):
#addPoint(x,y,해당 점의 클래스)
addPoint(rn[i, 0] + 150, rn[i, 1] + 150, 0)
# (350, 150) 근방의 점은 1번 클래스로 설정
cv2.randn(rn, 0, 50)
for i in range(NUM):
addPoint(rn[i, 0] + 350, rn[i, 1] + 150, 1)
# (250, 400) 근방의 점은 2번 클래스로 설정
cv2.randn(rn, 0, 70)
for i in range(NUM):
addPoint(rn[i, 0] + 250, rn[i, 1] + 400, 2)
# 영상 출력 창 생성 & 트랙바 생성
cv2.namedWindow('knn')
cv2.createTrackbar('k_value', 'knn', 1, 5, on_k_changed)
# KNN 결과 출력
trainAndDisplay()
cv2.waitKey()
cv2.destroyAllWindows()
knn_digit
### knndigit
import sys
import numpy as np
import cv2
oldx, oldy = -1, -1
#직접 숫자를 그리는 프로그램
def on_mouse(event, x, y, flags, _):
global oldx, oldy
if event == cv2.EVENT_LBUTTONDOWN:
oldx, oldy = x, y
elif event == cv2.EVENT_LBUTTONUP:
oldx, oldy = -1, -1
elif event == cv2.EVENT_MOUSEMOVE:
if flags & cv2.EVENT_FLAG_LBUTTON:
cv2.line(img, (oldx, oldy), (x, y), (255, 255, 255), 40, cv2.LINE_AA)
oldx, oldy = x, y
# print('oldx',oldx)
# print('oldy',oldx)
cv2.imshow('img', img)
# 학습 & 레이블 행렬 생성
digits = cv2.imread('digits.png', cv2.IMREAD_GRAYSCALE)
if digits is None:
print('Image load failed!')
sys.exit()
#세로로 50개 숫자 이미지가 있다.
#0~9까지 각각 5개 세로줄, 가로는 2000씩
#0:(5,2000)
h, w = digits.shape[:2] #w: 2000, h:1000
#각각의 숫자를 부분영상으로 짤라내는코드
#np.hsplit(row, w//20): 숫자하나에 20*20짜리 영상이므로 2000을 20으로 나눈다
#np.vsplit(digits, h//20):#h//20:50개로 분할=> 0은 0끼리, 1은1끼리 분할
#가로로 100개, 세로로 50개
#세로 50개 안에는 0이 5개, 1이 5개, 2가 5개 ~ 9가 5개 총 50개
cell = [np.hsplit(row, w//20) for row in np.vsplit(digits, h//20)] #과자를 짜르듯이 세로로 먼저 20간격씩 자르고 가로로 20씩 자른다
cells = np.array(cell) #리스트를 ndarray
#20*20을 400으로 변환
#행이 몇개든 관심없고 일단 가로로 400개씩 나열하는게 먼저
train_images = cells.reshape(-1, 400).astype(np.float32) #(5000,400) dtype=float32
#0이 500개있으므로 이에 맞는 레이블을 repeat를 통해 만든다
train_labels = np.repeat(np.arange(10), len(train_images)/10) #(5000,1)
# KNN 학습
knn = cv2.ml.KNearest_create()
knn.train(train_images, cv2.ml.ROW_SAMPLE, train_labels)
# 사용자 입력 영상에 대해 예측
img = np.zeros((400, 400), np.uint8)
cv2.imshow('img', img)
cv2.setMouseCallback('img', on_mouse)
while True:
key = cv2.waitKey()
if key == 27:
break
elif key == ord(' '):
#400*400이미지를 20*20으로 줄인다
test_image = cv2.resize(img, (20, 20), interpolation=cv2.INTER_AREA)
#1행400열짜리로 만들고 타입변경한다
test_image = test_image.reshape(-1, 400).astype(np.float32)
ret, _, _, _ = knn.findNearest(test_image, 5)
print(int(ret))
img.fill(0) # 그려져 있는 숫자영상을 다시 검정색으로 초기화
cv2.imshow('img', img)
cv2.destroyAllWindows()
SVM
import sys
import numpy as np
import cv2
trains = np.array([[150, 200], [200, 250], #0
[100, 250], [150, 300], #0
[350, 100], [400, 200], #1
[400, 300], [350, 400]], dtype=np.float32) #1
labels = np.array([0, 0, 0, 0, 1, 1, 1, 1])
svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
# svm.setKernel(cv2.ml.SVM_LINEAR)
svm.setKernel(cv2.ml.SVM_RBF)
#trainAuto대신 train으로 변경후 C,Gamma를 따로 셋팅하면 같은결과가 나온다
#감마값을 키울수록 샘플에 의존적인 형태가 나온다
svm.setC(2.5)
svm.setGamma(0.00001)
svm.train(trains, cv2.ml.ROW_SAMPLE, labels) #다양한 C,gamma 값을 테스트 한다
# svm.trainAuto(trains, cv2.ml.ROW_SAMPLE, labels)
print('C:', svm.getC())
print('Gamma:', svm.getGamma())
#시각화 위한코드
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)
ret = int(res[0, 0])
if ret == 0:
img[y, x] = (128, 128, 255) # Red
else:
img[y, x] = (128, 255, 128) # Green
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)
cv2.imshow('svm', img)
cv2.waitKey()
cv2.destroyAllWindows()
HOG&SVM
import sys
import numpy as np
import cv2
oldx, oldy = -1, -1
#학습을 하고난 후 400*400 img가 뜬다->숫자그리기
def on_mouse(event, x, y, flags, _):
global oldx, oldy
if event == cv2.EVENT_LBUTTONDOWN:
oldx, oldy = x, y
elif event == cv2.EVENT_LBUTTONUP:
oldx, oldy = -1, -1
elif event == cv2.EVENT_MOUSEMOVE:
if flags & cv2.EVENT_FLAG_LBUTTON:
cv2.line(img, (oldx, oldy), (x, y), (255, 255, 255), 40, cv2.LINE_AA)
oldx, oldy = x, y
cv2.imshow('img', img)
#정규화_무게중심을 중간으로 이동(치우침을 제거)
#학습,테스트 할때 모두사용
def norm_digit(img): #img: 부분영상 20*20
m = cv2.moments(img)
#cx,cy:무게중심좌표
cx = m['m10'] / m['m00'] #글씨부분 x좌표를 모두 더한 픽셀/전체 픽셀
cy = m['m01'] / m['m00'] #글씨부분 y좌표를 모두 더한 픽셀/전체 픽셀(레이블링 원리 보기)
h, w = img.shape[:2]
#aff(2행 3열)->어파인의 (2,3)행렬 개념 보기
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
# 학습 데이터 & 레이블 행렬 생성
digits = cv2.imread('digits.png', cv2.IMREAD_GRAYSCALE)
if digits is None:
print('Image load failed!')
sys.exit()
h, w = digits.shape[:2]
#p.500 참고
#검출 윈도우 크기 (20,20), 블럭의 크기:(10,10), 셀크기:(5,5), 셀이동크기(stride)=(5,5) 즉,셀단위로 이동, bin=9개
hog = cv2.HOGDescriptor((20, 20), (10, 10), (5, 5), (5, 5), 9)
print('Descriptor Size:', hog.getDescriptorSize()) #블록하나에서의 히스토그램은 4*9=36개, 36*9=324차원의 특징벡터
cells = [np.hsplit(row, w//20) for row in np.vsplit(digits, h//20)]
cell1 = np.array(cells) #(50,100,20,20)
# print('cell1',cell1.shape)
cells = cell1.reshape(-1, 20, 20) # shape=(5000, 20, 20) 20*20짜리가 5000개
#각각의 20*20짜리의 서브이미지에 대해서 호그 특징벡터를 추출
desc = []
for img in cells:
img = norm_digit(img) #정규화
desc.append(hog.compute(img)) #(324,1)
train_desc = np.array(desc) #desc: (324,1)이 5000개 있는 리스트, 따라서 train_desc: (5000,324,1)
#(5000,324,1)중에 1은 필요가 없어서 squeeze()를 사용하여 없앰
#squeeze는 차원 중 사이즈가 1인 것을 찾아 스칼라값으로 바꿔 해당 차원을 제거한다. ex)2차원을 벡터로
train_desc = train_desc.squeeze().astype(np.float32) #(5000,324)인 train_desc 타입은 float32로 넣어준다
train_labels = np.repeat(np.arange(10), len(train_desc)/10)
print('train_desc.shape',train_desc.shape)
print('train_labels.shape',train_labels.shape)
# 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)
#svm.save('svmdigits.yml')
# 사용자 입력 영상에 대해 예측
#마우스로 그릴수 있도록하는 인터페이스 80~83
img = np.zeros((400, 400), np.uint8)
cv2.imshow('img', img)
cv2.setMouseCallback('img', on_mouse)
while True:
key = cv2.waitKey()
if key == 27:
break
elif key == ord(' '):
test_image = cv2.resize(img, (20, 20), interpolation=cv2.INTER_AREA)
test_image = norm_digit(test_image)
test_desc = hog.compute(test_image).T #(324,1)->(1,324), train과 같은 형식으로
_, res = svm.predict(test_desc) #predict에 들어갈 형태는 (1행,D열)
print(int(res[0, 0]))
img.fill(0)
cv2.imshow('img', img)
cv2.destroyAllWindows()
HOG&SVM2
import sys
import numpy as np
import cv2
# 숫자 영상을 20x20 크기 안에 적당히 들어가도록 리사이즈
def norm_img(img): #img는 부분영상
h, w = img.shape[:2]
print('h:',h, 'w:',w) #영상마다 size 다 다름
img = ~img #반전 : 짤라낸 원본이미지는 반전이 필요한 이미지이다.
blr = cv2.GaussianBlur(img, (0, 0), 2) #노이즈 제거
#14픽셀을 h로 나눔
'''
원래는 20./h 해서 resize크기를 세로 20으로 만드는게 맞지만
위,아래 픽셀을 3 픽셀씩 여유를 주기 위해서 숫자가 차지할 자리를 14픽셀로 설정함
'''
sf = 14. / h # scale factor. 위/아래 3픽셀씩 여백 고려. 20-6=14
if w > h:
sf = 14. / w
#가로세로 비율을 맞추기 위해 같은 스케일팩터 적용
img2 = cv2.resize(img, (0, 0), fx=sf, fy=sf, interpolation=cv2.INTER_AREA)
print('img2.shape',img2.shape)
h2, w2 = img2.shape[:2]
print('h2:',h2, 'w2:',w2)
#정 가운데 좌표를 잡기위한 시작점 포인트 설정
a = (20 - w2) // 2
b = (20 - h2) // 2
# print('가로','세로','(',a,',',b,')')
dst = np.zeros((20, 20), dtype=np.uint8)
#dst2를 그냥쓰는게 아니라 dst 가운데에 dst2를 카피한다. -> 모두 같은 사이즈를 얻게됨
dst[b:b+h2, a:a+w2] = img2[:, :] #img2모든 걸 copy
print('b:b+h2, a:a+w2:',b,':',b+h2, a,':',a+w2)
cv2.imshow('img',img)
# cv2.imshow('dst',dst)
return dst
# 입력 필기체 숫자 이미지 불러오기
src = cv2.imread('handwritten2.png')
if src is None:
print('Image load failed!')
sys.exit()
# HOG 객체 생성
hog = cv2.HOGDescriptor((20, 20), (10, 10), (5, 5), (5, 5), 9)
# 미리 학습된 SVM 데이터 불러오기
svm = cv2.ml.SVM_load('svmdigits.yml')
# 이진화 & 레이블링
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
#src: 흰색 배경에 검정 글 ->INV 반전 시킨다, opencv에서의 객체는 흰색 픽셀이다
_, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
#이진화된 영상을 레이블링->바운딩 박스 추출
cnt, _, stats, _ = cv2.connectedComponentsWithStats(src_bin)
dst = src.copy()
for i in range(1, cnt):
x, y, w, h, s = stats[i]
if s < 100: #3.141592 에서 . 을 삭제하기 위함
continue
# 각각의 숫자 부분 영상을 정규화한 후 HOG&SVM 숫자 인식
'''
필기체 중 1의 바운딩 박스는 세로로 긴 박스일것이다. 이 박스를 20*20으로 resize해서
정 가운데에 이쁘게 오게끔 만드는게 목적->norm_img
'''
digit = norm_img(src_gray[y:y+h, x:x+w]) #객체를 인식한 바운딩 박스를 사용해서 짤라내기!!
test_desc = hog.compute(digit).T
# print('test_desc.shape',test_desc.shape)
_, res = svm.predict(test_desc)
# HOG&SVM 숫자 인식 결과 출력
cv2.rectangle(dst, (x, y, w, h), (0, 0, 255))
cv2.putText(dst, str(int(res[0, 0])), (x, y - 5),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, cv2.LINE_AA)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
Kmeans
import sys
import numpy as np
import cv2
# 입력 영상 불러오기
src = cv2.imread('flowers.jpg')
if src is None:
print('Image load failed')
sys.exit()
# 차원 변환 & np.float32 자료형 변환
data = src.reshape((-1, 3)).astype(np.float32) #data.shape:(세로*가로,3) 이차원형태
# K-means 알고리즘
#최대 10번 반복하고, 1픽셀 이하로 움직임이 멈추면 종료
criteria = (cv2.TERM_CRITERIA_EPS|cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
for K in range(2, 9):
print('K:', K)
#label: 각각의 점이 몇번 클래스 인지 알려줌(몇번 군집에 속하는가)
#label shape: (307200,1) = 전체 픽셀 개수
#그 군집의 중심점 좌표가 center
ret, label, center = cv2.kmeans(data, K, None, criteria, 10,cv2.KMEANS_RANDOM_CENTERS)
# 군집화 결과를 이용하여 출력 영상 생성
center = np.uint8(center)
dst = center[label.flatten()] # 각 픽셀을 K개 군집 중심 색상으로 치환, 2차원->1차원
print('center',center)
print('center.shape',center.shape)
# print('dst',dst)
print('dst.shape ',dst.shape)
print('label',label.flatten())
#dst를 그냥쓰게 되면 세로가 307200이므로 입력영상과 같게 reshape
dst = dst.reshape((src.shape))
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
'실습 note' 카테고리의 다른 글
OpenCV_12(딥러닝2) (0) | 2021.03.05 |
---|---|
OpenCV_11(딥러닝) (0) | 2021.03.04 |
OpenCV_9(객체 추적과 모션벡터) (0) | 2021.03.01 |
OpenCV_8(특징점 검출&매칭) (0) | 2021.02.26 |
OpenCV_7(영상분할&객체검출) (0) | 2021.02.25 |