728x90

2021/03/03 - [기록 note] - 2021-03-03(OpenCV_10)

 

2021-03-03(OpenCV_10)

진도가 나갈수록 난이도가 올라가서 강의 9개 분량의 진도와 복습만 하루가 걸리다 못해 밀리고 있습니다 ㅎㅎㅎ 난이도가 올라감에따라 제 한숨도 늘어가고 있답니다 오늘 복습중의 이해가 부

ghdrldud329.tistory.com

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()
728x90

'실습 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

+ Recent posts