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

 

 

### 차분영상 

#첫번째 영상 - frame들

import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('PETS2000.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

#첫번째 프레임을 배경영상으로 등록
#컬러로 구해도 되지만 현재는 굳이 컬러로 할 필요가 없고, 연산 속도를 올리기 위해서 gray로 변환
_,back = cap.read()
back = cv2.cvtColor(back, cv2.COLOR_BGR2GRAY)

#노이즈 제거위한 가우시안
#ksize= (0,0), 시그마 1 
back =cv2.GaussianBlur(back, (0,0), 1)

while True:
    ret, frame = cap.read() 

    if not ret:
        break
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    #블러처리
    frame =cv2.GaussianBlur(frame, (0,0), 1)
    
    #단순히 뺄셈이 아닌, 뺄셈 후 절대값을 넣어준다
    diff=cv2.absdiff(back, frame)

    #차이값이 30이상이면 255로 만들고 그게 아니면 0으로 만들기
    _,diff=cv2.threshold(diff,30,255, cv2.THRESH_BINARY)

    cv2.imshow('frame',frame)
    cv2.imshow('diff',diff)
    if cv2.waitKey(30)==27:
        break

    '''
    #블러를 하기 전 영상결과#
    결과 영상에서는 같은 물체지만 선에따라 하얀 점들이 반짝인다. 
    빛의 반사라든지 여러 요인으로 인해 튀는 값들이다. (노이즈)
    모폴로지 열기 연산 적용가능하지만 이번실습에서는 가우시안 블러를 이용한다
    '''
    '''
    흰색객체를 레이블링을 통해서 객체의 위치와 크기를 판단 한 후, 해당 위치에 바운딩 박스 그리면
    침입자 프로그램 만들수있다
    '''
cap.release()
cv2.destroyAllWindows()

 

## 차분영상2

import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('PETS2000.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 배경 영상 등록
ret, back = cap.read()

if not ret:
    print('Background image registration failed!')
    sys.exit()

back = cv2.cvtColor(back, cv2.COLOR_BGR2GRAY)
back = cv2.GaussianBlur(back, (0, 0), 1.0)

# 비디오 매 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break

    #그레이 변환 
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    
    #블러처리
    gray = cv2.GaussianBlur(gray, (0, 0), 1.0)

    # 차영상 구하기 & 이진화
    diff = cv2.absdiff(gray, back)

    #임계값 적용 후 레이블링
    _, diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)

    # 레이블링을 이용하여 바운딩 박스 표시
    cnt, _, stats, _ = cv2.connectedComponentsWithStats(diff)

    for i in range(1, cnt):
        #s : 객체 픽셀 사이즈
        x, y, w, h, s = stats[i]

        if s < 100:
            continue

        #본래 영상 frame에다가 박스 그리기
        cv2.rectangle(frame, (x, y, w, h), (0, 0, 255), 2)

    cv2.imshow('frame', frame)
    cv2.imshow('diff', diff)

    if cv2.waitKey(30) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

### 이동평균 배경차분영상

import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('PETS2000.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 첫번째 프레임을 배경 영상 등록
ret, back = cap.read()

if not ret:
    print('Background image registration failed!')
    sys.exit()

# back: uint8 배경, fback: float32 배경
back = cv2.cvtColor(back, cv2.COLOR_BGR2GRAY)

#노이즈 제거 위한 블러처리
back = cv2.GaussianBlur(back, (0, 0), 1.0)
print('back',back)
#업데이트는 미세한 값을 다루기 때문에 float 타입으로 변환 후 진행한다
fback = back.astype(np.float32)

# 비디오 매 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (0, 0), 1.0)

    # fback: float32, back: uint8 배경
    #fback(dst): dst가 입력에도 있고 출력에도 있을경우엔 dst를 인자로 줘야한다. 출력으로 받으면 안된다
    # 알파값:0.01(gray적용), 0.99(fback적용)
    cv2.accumulateWeighted(gray, fback, 0.01)

    #absdiff는 두개의 입력영상 타입이 같아야 한다
    back = fback.astype(np.uint8)
    diff = cv2.absdiff(gray, back)
    _, diff = cv2.threshold(diff, 30, 255, cv2.THRESH_BINARY)

    # 레이블링을 이용하여 바운딩 박스 표시
    cnt, _, stats, _ = cv2.connectedComponentsWithStats(diff)

    for i in range(1, cnt):
        x, y, w, h, s = stats[i]

        if s < 100:
            continue

        cv2.rectangle(frame, (x, y, w, h), (0, 0, 255), 2)

    cv2.imshow('frame', frame)
    cv2.imshow('diff', diff)
    cv2.imshow('back', back)

    if cv2.waitKey(30) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

### MOG 추적

import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('PETS2000.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 배경 차분 알고리즘 객체 생성

# history 기본값500: 과거 몇개의 프레임을 사용할건지 정함(기본값 500프레임)
# detectshadow: True(그림자 검출가능)
# bs = cv2.createBackgroundSubtractorMOG2() #MOG2 원리 이해x

bs = cv2.createBackgroundSubtractorKNN()

#그림자를 나타낸 회색이 없어지고 255로 처리됨
# bs.setDetectShadows(False) 

# 비디오 매 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    #apply 이용해서 현재프레임을 입력(다음 비디오 프레임)으로 넣어서 계속 업데이트
    #fgmask: 0또는 255, 그림자까지 포함한다면 0,128,255 세개의 값으로 구성된 마스크 
    fgmask = bs.apply(gray)
    back = bs.getBackgroundImage() #현재 배경영상이 어떻게 업데이트가 되는지 볼수 있는 함수

    # 레이블링을 이용하여 바운딩 박스 표시
    cnt, _, stats, _ = cv2.connectedComponentsWithStats(fgmask)

    for i in range(1, cnt):
        x, y, w, h, s = stats[i]

        if s < 80: #사각형 안 픽셀 수가 80보다 작으면 무시
            continue

        cv2.rectangle(frame, (x, y, w, h), (0, 0, 255), 2)

    cv2.imshow('frame', frame)
    cv2.imshow('back', back)
    cv2.imshow('fgmask', fgmask)

    if cv2.waitKey(20) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

## 이동평균 추적

import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('camshift.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 초기 사각형 영역: (x, y, w, h)
x, y, w, h = 135, 220, 100, 100 #귤 좌표 설정(ROI selector)
rc = (x, y, w, h)

ret, frame = cap.read()

if not ret:
    print('frame read failed!')
    sys.exit()

roi = frame[y:y+h, x:x+w]
roi_hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV)

# HS 히스토그램 계산
channels = [0, 1] #hue(색상), saturation(채도)
ranges = [0, 180, 0, 256] #hue:0~180, sat: 0~256 (255+1 1더해서 설정해야한다)
#귤이 위치한 좌표 위치에 대해서 히스토그램을 계산
#위 색상,채도 범위를 가졌을때의 히스토그램을 계산
#2차원 히스토그램: 축이 2개이고, 각 축이 만나는 지점의 개수를 표현
hist = cv2.calcHist([roi_hsv], channels, None, [90, 128], ranges)

# Mean Shift 알고리즘 종료 기준
#iteration:10 
#정확도가 1 이하(이동크기가 1픽셀보다 작으면) 종료
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)

# 비디오 매 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break

    # HS 히스토그램에 대한 역투영
    frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)

    #hist를 인자로 입력, 히스토그램 역투영 확률 영상을 반환
    #히스토그램 역투영:관심 영역의 히스토그램과 유사한 히스토그램을 갖는 영역을 찾아내는 기법
    #hist와 같은 컬러를 frame_hsv에서 찾아낸다
    backproj = cv2.calcBackProject([frame_hsv], channels, hist, ranges, 1)

    # Mean Shift
    # 본래는 입력이자 출력은 인자에만 입력해야하지만
    # rc가 튜플이라서 출력 표시를 해야 업데이트가 된다
    _, rc = cv2.meanShift(backproj, rc, term_crit)

    # 추적 결과 화면 출력
    cv2.rectangle(frame, rc, (0, 0, 255), 2)
    cv2.imshow('frame', frame)

    if cv2.waitKey(60) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

### camshift 추적

#색상 정보 트래킹
import sys
import numpy as np
import cv2


# 비디오 파일 열기
cap = cv2.VideoCapture('camshift.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 초기 사각형 영역: (x, y, w, h)
x, y, w, h = 135, 220, 100, 100
rc = (x, y, w, h)

ret, frame = cap.read()

if not ret:
    print('frame read failed!')
    sys.exit()

roi = frame[y:y+h, x:x+w]
roi_hsv = cv2.cvtColor(roi, cv2.COLOR_BGR2HSV) #원색을 추적할때는 HSV가 유리하다 왜?

# HS 히스토그램 계산
channels = [0, 1]
ranges = [0, 180, 0, 256]
hist = cv2.calcHist([roi_hsv], channels, None, [90, 128], ranges)

# CamShift 알고리즘 종료 기준
term_crit = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1)

# 비디오 매 프레임 처리
while True:
    ret, frame = cap.read()

    if not ret:
        break

    # HS 히스토그램에 대한 역투영
    frame_hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    backproj = cv2.calcBackProject([frame_hsv], channels, hist, ranges, 1)

    # CamShift
    # return 값은 retval, window 인데  retval은 교재에 나와있는 것처럼 복잡한 튜플 형식이며
    # 이값을 이용해서 사각형을 회전된 사각형으로 바꿀수 있다
    # probImage=>backproj 확률에 대한 영상 즉, 점들의 분포가 어떻게 되어가고 있는지를 명시해야하는데
    # 여기서는 히스토그램의 역투영을 이용함, 혹은 데이터 분포를 표현 할수 있는 다른 방법을 적용시킬수 있다.(ndarray형태로)
    ret, rc = cv2.CamShift(backproj, rc, term_crit)

    # 추적 결과 화면 출력
    cv2.rectangle(frame, rc, (0, 0, 255), 2)
    #타원그리는 함수
    cv2.ellipse(frame, ret, (0, 255, 0), 2) #회전된 사각형에 내접하는 타원을 그려준다
    cv2.imshow('frame', frame)

    if cv2.waitKey(60) == 27:
        break

cap.release()
cv2.destroyAllWindows()

 

### Optical Flow

import sys
import numpy as np
import cv2

#optical Flow 
'''
x방향으로의 이동속도
y방향으로의 이동속도
한 좌표가 다른 좌표로 이동했을때 overflow 미지수 두개 발생
어떤 N*N Window를 설정하면 객체가 이동하는 속도는 같으므로 N**2개 방정식이 생긴다
'''

src1 = cv2.imread('frame1.jpg')
src2 = cv2.imread('frame2.jpg')

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

#컬러변환->goodFeaturesToTrack는 gray만 입력으로 받는다
gray1 = cv2.cvtColor(src1, cv2.COLOR_BGR2GRAY)

#코너점을 찾는 goodFeaturesToTrack함수
pt1 = cv2.goodFeaturesToTrack(gray1, 50, 0.01, 10)

#src1, src2 에서 움직임 정보를 찾아내서 pt1 코너점 좌표가 어디로 이동했는지 pt2에 정보가 담겨져 있다
#pt2(이동된 점의 좌표)는 None으로 입력하고 출력으로 받는다
pt2, status, err = cv2.calcOpticalFlowPyrLK(src1, src2, pt1, None)

dst = cv2.addWeighted(src1, 0.5, src2, 0.5, 0)

for i in range(pt2.shape[0]):
    #잘못된것은 표현 무시하고, 잘된 것만 표시하도록 해준다
    if status[i, 0] == 0: #status가 0인것은 무시, 움직이지 않고 제자리 인 좌표?
        continue
    print(pt1[i,0]) #x,y 코너점의 좌표
    cv2.circle(dst, tuple(pt1[i, 0]), 4, (0, 255, 255), 2, cv2.LINE_AA) #이동 전 좌표
    cv2.circle(dst, tuple(pt2[i, 0]), 4, (0, 0, 255), 2, cv2.LINE_AA) #이동 후 좌표
    cv2.arrowedLine(dst, tuple(pt1[i, 0]), tuple(pt2[i, 0]), (0, 255, 0), 2)

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

 

728x90

'실습 note' 카테고리의 다른 글

OpenCV_11(딥러닝)  (0) 2021.03.04
OpenCV_10(머신러닝)  (0) 2021.03.03
OpenCV_8(특징점 검출&매칭)  (0) 2021.02.26
OpenCV_7(영상분할&객체검출)  (0) 2021.02.25
OpenCV_6(이진 영상 처리)  (0) 2021.02.24
728x90

2021/02/26 - [기록 note] - 2021-02-26(OpenCV_8)

 

2021-02-26(OpenCV_8)

오늘 특징점에 대해서 진도를 나갔습니다 초반에 너무 졸려서 듣는 둥 마는 둥  아무리 정신을 차려 보려고 해도, 10분을 자도 영 공부가 안되더라구요 게다가 특징점이니 .. keypoint니 해서 이게

ghdrldud329.tistory.com

### 해리스,fast,goodFeaturesToTrack

import sys
import numpy as np
import cv2


src = cv2.imread('building.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

tm = cv2.TickMeter()

# GFTT
tm.start()

#corners: 코너 점들의 좌표(ndarray), shape is (N,1,2)
#maxCorners: 400, qualityLevel:0.01, minDistance:10
corners = cv2.goodFeaturesToTrack(src, 400, 0.01, 10) #특징점 뽑기
    
tm.stop()
print('GFTT: {}ms.'.format(tm.getTimeMilli()))

dst1 = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

if corners is not None:
    for i in range(corners.shape[0]): #corners.shape[0]:코너의 갯수
        pt = (int(corners[i, 0, 0]), int(corners[i, 0, 1])) #[i, 0, 0]:x좌표, [i, 0, 1]:y좌표
        cv2.circle(dst1, pt, 5, (0, 0, 255), 2)

# FAST
tm.reset()
tm.start()
#threshold:60, 중심픽셀과 주변 픽셀 값 차이 입계값, 
#60개 이상 차이가 나는 픽셀이 9개(보통9)있다면 코너로 본다
fast = cv2.FastFeatureDetector_create(60) 
keypoints = fast.detect(src)

tm.stop()
print('FAST: {}ms.'.format(tm.getTimeMilli()))

dst2 = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

for kp in keypoints:
    pt = (int(kp.pt[0]), int(kp.pt[1]))
    cv2.circle(dst2, pt, 5, (0, 0, 255), 2)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()

cv2.destroyAllWindows()

 

### keypoint

import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create()
#feature = cv2.AKAZE_create()
#feature = cv2.ORB_create()

# 특징점 검출
kp1 = feature.detect(src1)
kp2 = feature.detect(src2)

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))

# 검출된 특징점 출력 영상 생성
#출력영상 None
dst1 = cv2.drawKeypoints(src1, kp1, None,
                         flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
dst2 = cv2.drawKeypoints(src2, kp2, None,
                         flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 특징벡터
import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create() #float32 
#feature = cv2.AKAZE_create() #uint8 => 61바이트
#feature = cv2.ORB_create() #uint8  => 32바이트(기록되는건 bit이기 때문에 32*8bit=256비트)

# 특징점 검출 및 기술자 계산
kp1 = feature.detect(src1)  #특징점 검출
_, desc1 = feature.compute(src1, kp1) #특징점 검출 후 ->특징벡터(기술자) 계산

kp2, desc2 = feature.detectAndCompute(src2, None) # 특징점&특징벡터 동시 출력

print('desc1.shape:', desc1.shape)
print('desc1.dtype:', desc1.dtype)
print('desc2.shape:', desc2.shape)
print('desc2.dtype:', desc2.dtype)

# 검출된 특징점 출력 영상 생성
dst1 = cv2.drawKeypoints(src1, kp1, None,
                         flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
dst2 = cv2.drawKeypoints(src2, kp2, None,
                         flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 특징점 매칭

import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create() #실수 기술자=>BFMatcher_create()
#feature = cv2.AKAZE_create() #이진 기술자 => BFMatcher_create(cv2.NORM_HAMMING)
#feature = cv2.ORB_create() #이진 기술자

# 특징점 검출 및 기술자 계산
kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

# 특징점 매칭
matcher = cv2.BFMatcher_create()
#matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)
matches = matcher.match(desc1, desc2) #매칭한결과를 리턴

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))
print('# of matches:', len(matches))

# 특징점 매칭 결과 영상 생성
dst = cv2.drawMatches(src1, kp1, src2, kp2, matches, None)

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

 

### 좋은 매칭선별1

import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create()
#feature = cv2.AKAZE_create()
#feature = cv2.ORB_create()

# 특징점 검출 및 기술자 계산
kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

# 특징점 매칭
matcher = cv2.BFMatcher_create()
#matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)
matches = matcher.match(desc1, desc2)

# 좋은 매칭 결과 선별
# 매칭한 거리값이 작은 순으로 sorted
matches = sorted(matches, key=lambda x: x.distance)
good_matches = matches[:80]

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))
print('# of matches:', len(matches))
print('# of good_matches:', len(good_matches))

# 특징점 매칭 결과 영상 생성
dst = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None)

#WINDOW_NORMAL: 영상에 맞게 창을 자동조절
cv2.namedWindow('dst',cv2.WINDOW_NORMAL)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 좋은 매칭선별2

import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create()
#feature = cv2.AKAZE_create()
#feature = cv2.ORB_create()

# 특징점 검출 및 기술자 계산
kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

# 특징점 매칭
matcher = cv2.BFMatcher_create()
#matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)

#knn매칭
matches = matcher.knnMatch(desc1, desc2, 2)

# 좋은 매칭 결과 선별
good_matches = []
for m in matches:
    if m[0].distance / m[1].distance < 0.7:
        good_matches.append(m[0])

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))
print('# of matches:', len(matches))
print('# of good_matches:', len(good_matches))

# 특징점 매칭 결과 영상 생성
dst = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None)

cv2.namedWindow('dst',cv2.WINDOW_NORMAL)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 호모그래피

import sys
import numpy as np
import cv2


# 영상 불러오기
src1 = cv2.imread('graf1.png', cv2.IMREAD_GRAYSCALE)
src2 = cv2.imread('graf3.png', cv2.IMREAD_GRAYSCALE)
# src1 = cv2.imread('box.png', cv2.IMREAD_GRAYSCALE)
# src2 = cv2.imread('box_in_scene.png', cv2.IMREAD_GRAYSCALE)

if src1 is None or src2 is None:
    print('Image load failed!')
    sys.exit()

# 특징점 알고리즘 객체 생성 (KAZE, AKAZE, ORB 등)
feature = cv2.KAZE_create()
#feature = cv2.AKAZE_create()
#feature = cv2.ORB_create()

# 특징점 검출 및 기술자 계산
kp1, desc1 = feature.detectAndCompute(src1, None)
kp2, desc2 = feature.detectAndCompute(src2, None)

# 특징점 매칭
matcher = cv2.BFMatcher_create()
#matcher = cv2.BFMatcher_create(cv2.NORM_HAMMING)
matches = matcher.match(desc1, desc2)

# 좋은 매칭 결과 선별
matches = sorted(matches, key=lambda x: x.distance)
good_matches = matches[:80]

print('# of kp1:', len(kp1))
print('# of kp2:', len(kp2))
print('# of matches:', len(matches))
print('# of good_matches:', len(good_matches))

# 호모그래피 계산
# good_matches: Dmatch라는 객체의 리스트(80개를 가지고 있다)
# m: Dmatch라는 type
#queryIdx: 1번 이미지의 key point 번호, trainIdx: 2번 이미지 키포인트번호
#kp1[m.queryIdx].pt: m.queryIdx 인덱스를 그대로 kp1 인덱스로 준다
#그러면 1번영상에서 구한 키포인트 중에서 해당하는 인덱스에 .pt, 즉 점의 좌표를 받아온다
#받아온 좌표는 float64형식인데 이를 32로 변경하고, 3차원으로 reshape도 해줘야한다
pts1 = np.array([kp1[m.queryIdx].pt for m in good_matches]
                ).reshape(-1, 1, 2).astype(np.float32)
pts2 = np.array([kp2[m.trainIdx].pt for m in good_matches]
                ).reshape(-1, 1, 2).astype(np.float32)

#pts1,pts2: ndarray형태고, shape=(N,1,2) 형태를 갖도록 해야한다
# _는 mask, H:두 영상간의 perpective 관계를 행렬로 가지고 있다
H, _ = cv2.findHomography(pts1, pts2, cv2.RANSAC)

# 호모그래피를 이용하여 기준 영상 영역 표시
# 1번영상이 2번영상에 어느 위치에 해당하는지를 표시(박스로 표시)
dst = cv2.drawMatches(src1, kp1, src2, kp2, good_matches, None,
                      flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS)

(h, w) = src1.shape[:2]

#H를 받기 위해서는 shape이 같아야 한다
#변경하기 전 => (4,2)에 정수좌표로 설정했으므로 int32이다
corners1 = np.array([[0, 0], [0, h-1], [w-1, h-1], [w-1, 0]]
                    ).reshape(-1, 1, 2).astype(np.float32)

# perspectiveTransform: 투시변환 행렬가지고 이동시키는 함수
#corners2: (4,1,2)
corners2 = cv2.perspectiveTransform(corners1, H)
print('corners2',corners2.shape)
print("H",H.shape)
print("H",H)
print(np.float32([w, 0]))
#np.float32([w, 0]): 2번 영상을 1번 영상 가로만큼 shift
corners2 = corners2 + np.float32([w, 0])
print('int32(corners2)',np.int32(corners2))

cv2.polylines(dst, [np.int32(corners2)], True, (0, 255, 0), 2, cv2.LINE_AA)

cv2.namedWindow('dst',cv2.WINDOW_NORMAL)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
728x90

'실습 note' 카테고리의 다른 글

OpenCV_10(머신러닝)  (0) 2021.03.03
OpenCV_9(객체 추적과 모션벡터)  (0) 2021.03.01
OpenCV_7(영상분할&객체검출)  (0) 2021.02.25
OpenCV_6(이진 영상 처리)  (0) 2021.02.24
OpenCV_5(영상의 특징추출)  (0) 2021.02.23
728x90

 

2021/02/25 - [기록 note] - 2021-02-25(OpenCV_7)

### grabcut1

import sys
import numpy as np
import cv2


# 입력 영상 불러오기
src = cv2.imread('nemo.jpg')

if src is None:
    print('Image load failed!')
    sys.exit()

# 사각형 지정을 통한 초기 분할
rc = cv2.selectROI(src)
mask = np.zeros(src.shape[:2], np.uint8)

#포그라운드,백그라운드 둘다 None을 줘도 함수 내부에서 알아서 생성
#계속 업데이터를 하면서 사용해야한다면 강제로 값을 줘야한다
#iteration:5 
#mask를 내부적으로 계속 업데이트 해준다 0~3 사이의 숫자로 마스크값을 채워준다
#0과2: 백 그라운드 인것과 백 그라운드 인 것같은 것을 0,2로 각각표시
#1과3: 포그라운도 인것과 포그라운드 인것같은 것을 1,3으로 각각표시
#mask는 입력&출력
cv2.grabCut(src, mask, rc, None, None, 5, cv2.GC_INIT_WITH_RECT)

# 0: cv2.GC_BGD, 2: cv2.GC_PR_BGD : 확신하는 백그라운드, 백그라운드로 생각되는 것
#0또는 2이면 0으로 셋팅, 1또는3이면 1로 셋팅
mask2 = np.where((mask == 0) | (mask == 2), 0, 1).astype('uint8')

print(mask2)
print(mask2.shape)
print(mask2[:, :, np.newaxis])
print(mask2[:, :, np.newaxis].shape)
print(src.shape)

#mask2는 0과1로 구성된 행렬
dst = src * mask2[:, :, np.newaxis]

#mask 보기
mask = mask*64 #최대값이 3이니까 3*64 = 최대 192 밝기

# 초기 분할 결과 출력
cv2.imshow('dst', dst)
cv2.imshow('mask', mask)
cv2.waitKey()
cv2.destroyAllWindows()

 

### grabcut2

import sys
import numpy as np
import cv2


# 입력 영상 불러오기
src = cv2.imread('nemo.jpg')

if src is None:
    print('Image load failed!')
    sys.exit()

# 사각형 지정을 통한 초기 분할
mask = np.zeros(src.shape[:2], np.uint8)  # 마스크
bgdModel = np.zeros((1, 65), np.float64)  # 배경 모델, 무조건 1행 65열로 구성
fgdModel = np.zeros((1, 65), np.float64)  # 전경 모델, 무조건 1행 65열로 구성

rc = cv2.selectROI(src)

cv2.grabCut(src, mask, rc, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_RECT)

# 0: cv2.GC_BGD, 2: cv2.GC_PR_BGD
mask2 = np.where((mask == 0) | (mask == 2), 0, 1).astype('uint8')
dst = src * mask2[:, :, np.newaxis]

# 초기 분할 결과 출력
cv2.imshow('dst', dst)

# 마우스 이벤트 처리 함수 등록
def on_mouse(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN: #왼쪽버튼을 눌렀을때는 포그라운드를 지정
        cv2.circle(dst, (x, y), 3, (255, 0, 0), -1)
        cv2.circle(mask, (x, y), 3, cv2.GC_FGD, -1)
        cv2.imshow('dst', dst)
    elif event == cv2.EVENT_RBUTTONDOWN:#오른쪽버튼을 눌렀을때는 백그라운드를 지정
        cv2.circle(dst, (x, y), 3, (0, 0, 255), -1)
        cv2.circle(mask, (x, y), 3, cv2.GC_BGD, -1)
        cv2.imshow('dst', dst)
    elif event == cv2.EVENT_MOUSEMOVE: 
        if flags & cv2.EVENT_FLAG_LBUTTON: #왼쪽버튼이 눌러져 있는 상태로 움직인다면 포그라운드
            cv2.circle(dst, (x, y), 3, (255, 0, 0), -1)
            cv2.circle(mask, (x, y), 3, cv2.GC_FGD, -1)
            cv2.imshow('dst', dst)
        elif flags & cv2.EVENT_FLAG_RBUTTON:#오른쪽버튼이 눌러져 있는 상태로 움직인다면 백그라운드
            cv2.circle(dst, (x, y), 3, (0, 0, 255), -1)
            cv2.circle(mask, (x, y), 3, cv2.GC_BGD, -1)
            cv2.imshow('dst', dst)


cv2.setMouseCallback('dst', on_mouse)

while True:
    key = cv2.waitKey()
    if key == 13:  # ENTER
        # 사용자가 지정한 전경/배경 정보를 활용하여 영상 분할
        cv2.grabCut(src, mask, rc, bgdModel, fgdModel, 1, cv2.GC_INIT_WITH_MASK)
        mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
        dst = src * mask2[:, :, np.newaxis]
        cv2.imshow('dst', dst)
    elif key == 27:
        break

cv2.destroyAllWindows()

 

### 모멘트 기반 객체 검출

import sys
import numpy as np
import cv2


# 영상 불러오기
obj = cv2.imread('spades.png', cv2.IMREAD_GRAYSCALE)
src = cv2.imread('symbols.png', cv2.IMREAD_GRAYSCALE)

if src is None or obj is None:
    print('Image load failed!')
    sys.exit()

# 객체 영상 외곽선 검출
#obj가 애초에 0과 255로 구성된 이진 영상이긴 한데 반전을 하는김에
#128값으로 이진화를 다시한번 해주고 반전 값을 준다(128보다 작으면 0, 크면 255)
#obj의 객체모양이 0으로 구성된 객체라서 반전을 함으로써 배경은 0, 객체는 255로 바꾼다
_, obj_bin = cv2.threshold(obj, 128, 255, cv2.THRESH_BINARY_INV)
obj_contours, _ = cv2.findContours(obj_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
obj_pts = obj_contours[0] #외곽선 좌표정보를 obj_pts 저장

# 입력 영상 분석
_, src_bin = cv2.threshold(src, 128, 255, cv2.THRESH_BINARY_INV)
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

# 결과 영상
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

# 입력 영상의 모든 객체 영역에 대해서
for pts in contours:
    if cv2.contourArea(pts) < 1000:
        continue

    rc = cv2.boundingRect(pts) #바운딩 박스 좌표를 구한다 
    cv2.rectangle(dst, rc, (255, 0, 0), 1) #구한 좌표를 통해 사각형 박스를 그린다

    # 모양 비교
    #obj_pts, pts간의 모양을 비교한다. 결과값은 두 객체간의 거리값이다.
    #거리값이 작을수록 모양이 비슷하다고 보면된다
    #matchShapes 함수는 객체 모양의 변형이 심하면 성능이 떨어진다
    #대칭,회전 같은 변형은 상관없다
    dist = cv2.matchShapes(obj_pts, pts, cv2.CONTOURS_MATCH_I3, 0)

    #각각의 객체 위에다가 거리값이 보이도록 한다
    cv2.putText(dst, str(round(dist, 4)), (rc[0], rc[1] - 3),
                cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 0), 1, cv2.LINE_AA)

    if dist < 0.1:
        cv2.rectangle(dst, rc, (0, 0, 255), 2)

cv2.imshow('obj', obj)
cv2.imshow('dst', dst)
cv2.waitKey(0)

 

### 템플릿매칭 

import sys
import numpy as np
import cv2


# 입력 영상 & 템플릿 영상 불러오기
src = cv2.imread('circuit.bmp', cv2.IMREAD_GRAYSCALE)
templ = cv2.imread('crystal.bmp', cv2.IMREAD_GRAYSCALE)

if src is None or templ is None:
    print('Image load failed!')
    sys.exit()

# 입력 영상 밝기 50증가, 가우시안 잡음(sigma=10) 추가
noise = np.zeros(src.shape, np.int32)
cv2.randn(noise, 50, 10) #randn: 가우시간 값을 반환, 평균이 50,시그마 10인 형태의 노이즈 추가

# CV_8UC3: 채널 3, 8비트
src = cv2.add(src, noise, dtype=cv2.CV_8UC3)

# 템플릿 매칭 & 결과 분석
res = cv2.matchTemplate(src, templ, cv2.TM_CCOEFF_NORMED)
#받은 res는 실수형 행렬-> 최대값 위치를 찾는다
#res는 TM_CCOEFF_NORMED의 영향에 따라 -1~1값을 가진다
#이값을 스케일링해서 아래 res_norm 영상으로 출력

res_norm = cv2.normalize(res, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U) #CV_8U : 8-bit unsigned integer: uchar ( 0..255 )
#NORM_MINMAX값을 줘서 최소가 0되고 최대가  255가 되도록 스케일링
#출력을 CV_8U 그레이스케일 형식으로 설정


#최소값,최대값,최소값위치,최대값위치 순으로 출력
_, maxv, _, maxloc = cv2.minMaxLoc(res)
print('maxv:', maxv)
print('maxloc:', maxloc)

# 매칭 결과를 빨간색 사각형으로 표시
th, tw = templ.shape[:2]
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
cv2.rectangle(dst, maxloc, (maxloc[0] + tw, maxloc[1] + th), (0, 0, 255), 2)

# 결과 영상 화면 출력
cv2.imshow('res_norm', res_norm)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 템플릿 매칭2

import sys
import numpy as np
import cv2


def load_digits():
    img_digits = []

    for i in range(10):
        filename = './digits/digit{}.bmp'.format(i)
        img_digits.append(cv2.imread(filename, cv2.IMREAD_GRAYSCALE))

        if img_digits[i] is None:
            return None

    return img_digits


#img : 부분영상
def find_digit(img, img_digits):
    max_idx = -1
    max_ccoeff = -1

    # 최대 NCC 찾기
    for i in range(10):
        #img : 짤려진 부분영상
        img = cv2.resize(img, (100, 150))

        #img_digits: 10영상이 들어있는 리스트
        #img와 img_digits의 각 영상의 크기가 같으므로 res는 1*1
        #res 값은 하나씩 출력, (1,1) 행렬
        res = cv2.matchTemplate(img, img_digits[i], cv2.TM_CCOEFF_NORMED)

        #값이 가장 큰 값으로 최신화
        if res[0, 0] > max_ccoeff:
            max_idx = i
            max_ccoeff = res[0, 0]

    return max_idx #인덱스 리턴


def main():
    # 입력 영상 불러오기
    src = cv2.imread('digits_print.bmp')

    if src is None:
        print('Image load failed!')
        return

    # 100x150 숫자 영상 불러오기
    img_digits = load_digits()  # list of ndarray

    if img_digits is None:
        print('Digit image load failed!')
        return

    # 입력 영상 이진화 & 레이블링
    src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)

    #받은 영상을 자동 이진화진행(글씨가 검정색이므로 invert 해준다)
    #반전 없이 이진화하면 0픽셀 객체를 볼수 없다
    #이진화: 기준값을 기준으로 작으면 0으로, 크면 255로 이진화
    _, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
    
    #레이블링이나 또는 findContours를 이용해준다-> 각 객체마다 레이블링 
    #총 객체 갯수(cnt),각 객체의 바운딩박스 정보(stats)
    cnt, _, stats, _ = cv2.connectedComponentsWithStats(src_bin)

    # 숫자 인식 결과 영상 생성
    dst = src.copy()
    for i in range(1, cnt): #0은 배경이므로 제외
        (x, y, w, h, s) = stats[i] #s: 픽셀갯수

        if s < 1000:
            continue

        # 가장 유사한 숫자 이미지를 선택

        #digit안에는 가장 유사도가 높은 값의 인덱스 
        #영상을 짜른다-> 입력영상은 숫자가 이어져있으므로 짤라줘서 비교 해야한다
        digit = find_digit(src_gray[y:y+h, x:x+w], img_digits)
        cv2.rectangle(dst, (x, y, w, h), (0, 255, 255))
        cv2.putText(dst, str(digit), (x, y - 4), cv2.FONT_HERSHEY_SIMPLEX,
                    1, (0, 255, 255), 2, cv2.LINE_AA)

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


if __name__ == '__main__':
    main()

 

### 캐스케이드

import sys
import numpy as np
import cv2


src = cv2.imread('lenna.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

classifier = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')

if classifier.empty():
    print('XML load failed!')
    sys.exit()

tm = cv2.TickMeter()
tm.start()

faces = classifier.detectMultiScale(src, scaleFactor=1.2, minSize=(100,100))

tm.stop()
print(tm.getTimeMilli())

for (x, y, w, h) in faces:
    cv2.rectangle(src, (x, y, w, h), (255, 0, 255), 2)

cv2.imshow('src', src)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 캐스케이드2

import sys
import numpy as np
import cv2


src = cv2.imread('lenna.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

face_classifier = cv2.CascadeClassifier('haarcascade_frontalface_alt2.xml')
eye_classifier = cv2.CascadeClassifier('haarcascade_eye.xml')

if face_classifier.empty() or eye_classifier.empty():
    print('XML load failed!')
    sys.exit()

faces = face_classifier.detectMultiScale(src)

for (x1, y1, w1, h1) in faces:
    cv2.rectangle(src, (x1, y1), (x1 + w1, y1 + h1), (255, 0, 255), 2)

    faceROI = src[y1:y1 + h1 // 2, x1:x1 + w1] #얼굴부분의 윗면~반쪽 부분만 짜름(머리~인중)
    eyes = eye_classifier.detectMultiScale(faceROI) #짜른 박스에서 눈 찾기

    for (x2, y2, w2, h2) in eyes:
        center = (x2 + w2 // 2, y2 + h2 // 2)
        cv2.circle(faceROI, center, w2 // 2, (255, 0, 0), 2, cv2.LINE_AA)

cv2.imshow('src', src)
cv2.waitKey()
cv2.destroyAllWindows()

 

### HOG 보행자 검출

import sys
import random
import numpy as np
import cv2


# 동영상 불러오기
cap = cv2.VideoCapture('vtest.avi')

if not cap.isOpened():
    print('Video open failed!')
    sys.exit()

# 보행자 검출을 위한 HOG 기술자 설정
hog = cv2.HOGDescriptor()

#보행자를 검출하기 위해 미리 학습되어 있는 svm coefficient 값들을 불러온다
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

while True:
    ret, frame = cap.read()

    if not ret:
        break

    # 매 프레임마다 보행자 검출
    #weight(신뢰도)정보는 사용 안함
    detected, _ = hog.detectMultiScale(frame)

    # 검출 결과 화면 표시
    for (x, y, w, h) in detected: #(N,4)인 detected 
        c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
        cv2.rectangle(frame, (x, y, w, h), c, 3)

    cv2.imshow('frame', frame)
    if cv2.waitKey(10) == 27:
        break

cv2.destroyAllWindows()

 

 

 

 

 

 

 

728x90

'실습 note' 카테고리의 다른 글

OpenCV_9(객체 추적과 모션벡터)  (0) 2021.03.01
OpenCV_8(특징점 검출&매칭)  (0) 2021.02.26
OpenCV_6(이진 영상 처리)  (0) 2021.02.24
OpenCV_5(영상의 특징추출)  (0) 2021.02.23
OpenCV_4(기하학적 변환)  (0) 2021.02.22
728x90

2021/02/24 - [기록 note] - 2021-02-24(OpenCV_6)

### threshold

import sys
import numpy as np
import cv2


src = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# 임계값이 왜 리턴 될까? 자동으로 임계값을 결정하는 알고리즘에서 필요하게된다
#수동으로 임계값 결정할땐 리턴되는 임계값을 받을 필요가 없다
#dst1은 모든 세포를 검출
_,dst1= cv2.threshold(src, 210,255, cv2.THRESH_BINARY)

#dst2는 세포중에서 염색된 것만 검출
_,dst2= cv2.threshold(src, 100,255, cv2.THRESH_BINARY)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### threshold2

import sys
import numpy as np
import cv2


src = cv2.imread('cells.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


def on_threshold(pos):
    _, dst = cv2.threshold(src, pos, 255, cv2.THRESH_BINARY)
    cv2.imshow('dst', dst)


cv2.imshow('src', src)
cv2.namedWindow('dst') #창을 미리 하나 만들어야 트랙바가 만들어진다
cv2.createTrackbar('Threshold', 'dst', 0, 255, on_threshold)
cv2.setTrackbarPos('Threshold', 'dst', 128)

cv2.waitKey()
cv2.destroyAllWindows()

 

### ostu

import sys
import numpy as np
import cv2


src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

#128대신 아무 숫자 넣어도 이 값으로 계산 안됨, 자동으로 결정된다
#강의에서는 대표 숫자 0을 입력함
#th는 실수형으로 리턴됨
#cv2.THRESH_BINARY | 없어도 똑같이 진행된다
th,dst=cv2.threshold(src, 128, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

print("otsu's threshold:", th)  # 131

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### local_threshold

import sys
import numpy as np
import cv2


# 입력 영상 불러오기
src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# 전역 이진화 by Otsu's method
_, dst1 = cv2.threshold(src, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 지역 이진화 by Otsu's method
dst2 = np.zeros(src.shape, np.uint8)

bw = src.shape[1] // 4 #block width , 128
bh = src.shape[0] // 4 #block height, 128

for y in range(4): #세로 4등분
    for x in range(4): #가로 4등분

        #copy가 아니기 때문에 dst_를 변경하면 dst2도 변경됨을 인지해야한다
        # print(y*bh,'부터',(y+1)*bh, x*bw,'부터',(x+1)*bw)

        #y=0일때  y는 0:128 고정이고, x방향으로 0:128, 128:256, 256:384, 384:512
        #y=1일때  y는 128:256 고정이고, x방향으로 0:128, 128:256, 256:384, 384:512
        src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        dst_ = dst2[y*bh:(y+1)*bh, x*bw:(x+1)*bw]

        #dst_를 인자로써 입력은 dst_를 입력이자 출력으로 사용한다는 것
        #만일 인자로 주지 않고 _,dst_= cv2.thresh~~ 로 작성하면
        #새로운 정보로 채워진 dst_는 기존에 받았던 부분영상 정보가 없어진다
        #src_,dst_ 크기가 같아야한다. 다르면 dst_가 또 정보를 잃게 되어서(dst2와 같은 정보를 공유하는 것을 못하게됨) 아예 새로운 형태를 만든다
        cv2.threshold(src_, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU, dst_)

# 결과 출력
cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### adaptive_threshold

import sys
import numpy as np
import cv2


src = cv2.imread('sudoku.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


def on_trackbar(pos):
    bsize = pos
    if bsize % 2 == 0: #짝수이면 하나를 빼서 홀수로 만듦(kernel은 반드시 홀수)
        bsize = bsize - 1
    if bsize < 3:
        bsize = 3

    dst = cv2.adaptiveThreshold(src, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                                cv2.THRESH_BINARY, bsize, 5)

    cv2.imshow('dst', dst)


cv2.imshow('src', src)
cv2.namedWindow('dst')
cv2.createTrackbar('Block Size', 'dst', 0, 200, on_trackbar)
cv2.setTrackbarPos('Block Size', 'dst', 11)

cv2.waitKey()
cv2.destroyAllWindows()

 

### 모폴로지

import sys
import numpy as np
import cv2


src = cv2.imread('circuit.bmp', cv2.IMREAD_GRAYSCALE) #0또는 255인 기판 이미지

if src is None:
    print('Image load failed!')
    sys.exit()

# 3행 5열짜리의 행렬의 structure element
se = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 3))
dst1 = cv2.erode(src, se)

# 3행 3열의 의미는 상하좌우 방향으로 1픽셀을 팽창한다는 의미
dst2 = cv2.dilate(src, None)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 지역이진화+모폴로지

import sys
import numpy as np
import cv2


src = cv2.imread('rice.png', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

# src 영상에 지역 이진화 수행 (local_th.py 참고)
dst1 = np.zeros(src.shape, np.uint8)

bw = src.shape[1] // 4
bh = src.shape[0] // 4

for y in range(4):
    for x in range(4):
        src_ = src[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        dst_ = dst1[y*bh:(y+1)*bh, x*bw:(x+1)*bw]
        cv2.threshold(src_, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU, dst_)

# connectedComponents: 흰색 덩어리가 몇개가 있는지 알려준다
# 모폴로지 하기전(잡음 제거전)
cnt1, _ = cv2.connectedComponents(dst1)
print('cnt1:', cnt1)

#침식과 팽창을 통해서 노이즈 제거 후 갯수 파악
# dst2 = cv2.morphologyEx(dst1, cv2.MORPH_OPEN, None)
dst2 = cv2.erode(dst1, None) #위에서 지역 이진화 진행한 dst1
dst2 = cv2.dilate(dst2, None)

cnt2, _ = cv2.connectedComponents(dst2)
# 모폴로지 시행 후(잡음 제거 후)
print('cnt2:', cnt2)

cv2.imshow('src', src)
cv2.imshow('dst1', dst1)
cv2.imshow('dst2', dst2)
cv2.waitKey()
cv2.destroyAllWindows()

 

### labeling

import sys
import numpy as np
import cv2


src = cv2.imread('keyboard.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

#1.먼저 이진화 작업
_, src_bin = cv2.threshold(src, 0,255, cv2.THRESH_OTSU)

#2.이진화 된 영상에서 레이블링을 작업
cnt, labels, stats, centroids=cv2.connectedComponentsWithStats(src_bin)

#stats나 centroids 정보를 통해서 각 객체가 어느위치에 어떻게 놓여 있는지 알수있다
#stats로 각각 객체의 바운딩 박스를 알수 있다 -> 각 객체를 구분 영상으로 짤라 낼수도있다

#3.각각의 객체 정보에 접근하기위해서 for문
dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)
for i in range(1,cnt): #1부터 시작한 이유: 배경 0 은 제외한다는 의미
    x,y,w,h,a=stats[i]


    '''
    dst 영상의 빨간색 점들은 원본의 있는 노이즈
    제거하기 위해서는 opening(침식&팽창)도 있지만 
    아래 처럼 간단하게 코드 작성한다
    '''
    #검출한 면적 수가 20보다 작으면 무시
    if a <20:
        continue
    
    #빨간 바운딩 박스
    cv2.rectangle(dst, (x,y,w,h),(0,0,255),2)


cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 외곽선검출

import sys
import random
import numpy as np
import cv2


src = cv2.imread('contours.bmp', cv2.IMREAD_GRAYSCALE) #이진영상 load

if src is None:
    print('Image load failed!')
    sys.exit()

#1.외곽선 검출(좌표구하기)
#RETR_CCOMP: 2level(계층:2)          
#CHAIN_APPROX_NONE: 모든 좌표 정보 저장             
contours, hier = cv2.findContours(src, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE)

dst = cv2.cvtColor(src, cv2.COLOR_GRAY2BGR)

idx = 0 #첫번째 사용할 외곽선 인덱스 0번
while idx >= 0:
    c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    
    #검출한 외곽선좌표를 이용한 외곽선 그리기
    #hier인자를 주면 홀 외곽선도 취급이 가능하다
    #hier인자가 없다면 바깥의 외곽선만 취급한다.
    #contours: 외곽선 좌표정보
    cv2.drawContours(dst, contours, idx, c, 2, cv2.LINE_8, hier)
    
    # hier의 shape가 (1,N,4)형태를 가진다
    #[0, idx, 0], 무조건 첫번째는 0을 주면되고 N자리에 index번호를 준다.
    #N은 외곽선의 갯수이다.
    #맨 마지막 자리에 0부터3사이에 값을 줄수 있는데 0을 주면 계층에서 next로 넘어간다
    #index번호에 따라서 next로 이동을 한다는 의미
    idx = hier[0, idx, 0]

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

    

 

### 외곽선검출2

import sys
import random
import numpy as np
import cv2


src = cv2.imread('milkdrop.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

_, src_bin = cv2.threshold(src, 0, 255, cv2.THRESH_OTSU)

#RETR_LIST: 모든 외곽선을 검출(리스트로 표현)
#hier 인자는 받지 않았음
contours, _ = cv2.findContours(src_bin, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)

h, w = src.shape[:2]
dst = np.zeros((h, w, 3), np.uint8)

#contours는 리스트이기 때문에 contours의 길이는 객체의 갯수와 같다
for i in range(len(contours)):
    c = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255))
    cv2.drawContours(dst, contours, i, c, 1, cv2.LINE_AA)

cv2.imshow('src', src)
cv2.imshow('src_bin', src_bin)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

import math
import cv2


def setLabel(img, pts, label):
    (x, y, w, h) = cv2.boundingRect(pts) #외곽선을 감싸는 가장 작은 크기 사각형 정보출력
    pt1 = (x, y)
    pt2 = (x + w, y + h) #(가로길이,세로길이)
    cv2.rectangle(img, pt1, pt2, (0, 0, 255), 1)
    cv2.putText(img, label, pt1, cv2.FONT_HERSHEY_PLAIN, 1, (0, 0, 255))


def main():
    img = cv2.imread('polygon.bmp', cv2.IMREAD_COLOR)

    if img is None:
        print('Image load failed!')
        return

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    #이진 영상만들기
    #이때 THRESH_BINARY_INV사용해서 반전을 시킨다
    #본 이미지의 도형은 어두운 컬러이고 오히려 바탕이 회색에 가까운 밝은 픽셀이기 때문에 반전 시켜야한다
    _, img_bin = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)
    
    #findContours: 각각의 객체를 찾는다
    #RETR_EXTERNAL: 바깥쪽 외곽선만 검출
    contours, _ = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

    #contours는 ndarray를 원소로 갖고 있는 리스트이다
    #for문을 돌리면 pts가 객체 하나하나의 외곽선 객체를 받아 올수 있게된다
    for pts in contours:

        #노이즈를 제거하기 위한 코드
        #하나하나의 객체의 면적이 400보다 작으면 무시
        #400: 가로세로 20씩 
        if cv2.contourArea(pts) < 400:  #  너무 작으면 무시
            continue

        #근사화를 진행서 단순화 시킨다    
        #근사화된 좌표로 리턴된다
        #만일 좌표10개로 표현된 도형이 마진을 통해 근사화를 거치면 적은 좌표로 표현이 가능
        approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)

        vtc = len(approx)

        #좌표갯수가 3도 아니고 4도 아니면 원 모양인지를 계산하는 코드
        if vtc == 3:
            setLabel(img, pts, 'TRI')
        elif vtc == 4:
            setLabel(img, pts, 'RECT')
        else:
            length = cv2.arcLength(pts, True)
            area = cv2.contourArea(pts)
            ratio = 4. * math.pi * area / (length * length)

            if ratio > 0.85:
                setLabel(img, pts, 'CIR')

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


if __name__ == '__main__':
    main()

 

"""
Tesseract-ocr 설치하기

1. tesseract-ocr-w64-setup-v5.0.0-alpha.20200328 파일 다운로드 
   (https://digi.bib.uni-mannheim.de/tesseract/tesseract-ocr-w64-setup-v5.0.0-alpha.20200328.exe)
2. 설치 시 "Additional script data" 항목에서 "Hangul Script", "Hangul vertical script" 항목 체크,
   "Additional language data" 항목에서 "Korean" 항목 체크.
4. 설치 후 시스템 환경변수 PATH에 Tesseract 설치 폴더 추가
   (e.g.) c:\Program Files\Tesseract-OCR
4. 설치 후 시스템 환경변수에 TESSDATA_PREFIX를 추가하고, 변수 값을 <Tesseract-DIR>\tessdata 로 설정
5. <Tesseract-DIR>\tessdata\script\ 폴더에 있는 Hangul.traineddata, Hangul_vert.traineddata 파일을
   <Tesseract-DIR>\tessdata\ 폴더로 복사
6. 명령 프롬프트 창에서 pip install pytesseract 명령 입력
"""

import sys
import random
import numpy as np
import cv2
import pytesseract


def reorderPts(pts):
    idx = np.lexsort((pts[:, 1], pts[:, 0]))  # 칼럼0 -> 칼럼1 순으로 정렬한 인덱스를 반환
    pts = pts[idx]  # x좌표로 정렬

    if pts[0, 1] > pts[1, 1]: #x좌표로 구분된 두 그룹중 왼쪽그룹에서 y좌표가 더 작은 좌표를 0번인덱스준다
        pts[[0, 1]] = pts[[1, 0]] #두개의 점을 스와핑하는 코드 

    if pts[2, 1] < pts[3, 1]: #x좌표로 구분된 두 그룹중 오른쪽그룹에서 y좌표가 큰게 인덱스2, 나머지 우측상단이 3번
        pts[[2, 3]] = pts[[3, 2]]

    return pts #좌측상단부터 반시계방향으로 돌아가는 pts


# 영상 불러오기
filename = 'namecard1.jpg'
if len(sys.argv) > 1:
    filename = sys.argv[1]

src = cv2.imread(filename)

if src is None:
    print('Image load failed!')
    sys.exit()

# 출력 영상 설정
dw, dh = 720, 400 #명함을 편 상태의 영상크기를 미리 설정

#입력영상의 네개의 모서리를 넣을 자리를 미리 만듦
srcQuad = np.array([[0, 0], [0, 0], [0, 0], [0, 0]], np.float32)

#dw, dh를 이용해서 좌측상단부터 반시계 방향으로 돈다
dstQuad = np.array([[0, 0], [0, dh], [dw, dh], [dw, 0]], np.float32)
dst = np.zeros((dh, dw), np.uint8)

# 입력 영상 전처리_이진화
src_gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
_, src_bin = cv2.threshold(src_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)

# 외곽선 검출 및 명함 검출
contours, _ = cv2.findContours(src_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

cpy = src.copy()
for pts in contours:
    # 너무 작은 객체는 무시
    if cv2.contourArea(pts) < 1000:
        continue

    # 외곽선 근사화
    # approx: 점 네개로 근사화가 된다
    approx = cv2.approxPolyDP(pts, cv2.arcLength(pts, True)*0.02, True)

    # 컨벡스가 아니고, 사각형이 아니면 무시
    if not cv2.isContourConvex(approx) or len(approx) != 4:
        continue

    #polylines통해 외곽선을 그린다    
    cv2.polylines(cpy, [approx], True, (0, 255, 0), 2, cv2.LINE_AA)

    #reorderPts: 각 점의 순서를 수동으로 정해준다
    #approx 점들을 분석해서 좌측상단부터 반시계방향으로 reorder => srcQuad로 넘긴다
    srcQuad = reorderPts(approx.reshape(4, 2).astype(np.float32))

pers = cv2.getPerspectiveTransform(srcQuad, dstQuad)

#똑바로 펴는 작업
dst = cv2.warpPerspective(src, pers, (dw, dh))

dst_gray = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
print(pytesseract.image_to_string(dst_gray, lang='Hangul+eng'))

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

728x90
728x90

 

2021/02/23 - [기록 note] - 2021-02-23(OpenCV_5)

### Sobel2

import sys
import numpy as np
import cv2

'''
src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


dx = cv2.Sobel(src, cv2.CV_32F, 1, 0)
dy = cv2.Sobel(src, cv2.CV_32F,0, 1)

cv2.imshow('src', src)
cv2.imshow('dx', dx) #imshow는 입력받는 변수가(dx,dy) 플롯타입이면, 128을 곱해서 보여준다 즉, 포화상태가된다
cv2.imshow('dy', dy)
cv2.waitKey()

cv2.destroyAllWindows()
'''
# ===================================================
'''
src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()


dx = cv2.Sobel(src, cv2.CV_32F, 1, 0)
dy = cv2.Sobel(src, cv2.CV_32F,0, 1)

#dx,dy라는 미분 행렬을 이용해서 magnitude(크기), 즉 미분값(변화량)의 크기를 보여준다. 
mag = cv2.magnitude(dx,dy) #이상태에서는 mag도 float 형태가된다 => 제대로 볼수가 없다. imshow에서 포화상태가 되기 때문에

#mag 크기가 255보다 커질수가 있기 때문에 saturate적용
mag = np.clip(mag, 0,255).astype(np.uint8)
"""
왼쪽기둥보면 변화량 표현이 잘 되어있다. dx와 dy가 합쳐진 결과이다
밝은 부분은 엣지가 강하고
검정 부분은 엣지가 있지만 약한 부분
"""


cv2.imshow('src', src)
cv2.imshow('src', mag)
cv2.waitKey()
cv2.destroyAllWindows()
'''
# ====================================================

src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

dx = cv2.Sobel(src, cv2.CV_32F, 1, 0)
dy = cv2.Sobel(src, cv2.CV_32F,0, 1)

mag = cv2.magnitude(dx,dy) 
mag = np.clip(mag, 0,255).astype(np.uint8)

#mag를 이용해서 엣지부분을 뽑을수 있다.
# 1. 검정색으로 채워진 엣지 부분을 하나 만든다
edge= np.zeros(mag.shape[:2], np.uint8)

# 2.픽셀값이 120보다 큰 값만 255로 확 밝게 바꾼다
edge[mag>120]=255 #불리언 인덱싱, threshhold 값 120

cv2.imshow('src', src)
cv2.imshow('mag', mag)
cv2.imshow('edge', edge)
cv2.waitKey()
cv2.destroyAllWindows()​

 

### 소베필터


import sys
import numpy as np
import cv2


src = cv2.imread('lenna.bmp', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

#직접 구현
'''
kernel_x=np.array([
    [-1,0,1],
    [-2,0,2],
    [-1,0,1]], dtype= np.float32)
kernel_y= np.array([
	[-1,-2,-1],
    [0,0,0],
    [1,2,1]], dtype =np.float32)
    
dx= cv2.filter2D(src, -1, kernel_x)
dy= cv2.filter2D(src, -1, kernel_y)
'''

'''
좌측 상단을 보면, 밝은 픽셀에서 어두운 픽셀로 급 감소로 한 변화량을 
보였다. 급 감소한 변화량은 음수로 표현이 되는데 이는 saturate 되어서 0으로 바뀐다
그래서 화면을 보게되면 변화가 있음에도 불구하고 검정색으로 표현이 된다.
그레이스케일로 표현하다 보니까 saturate가 된다.
해결방법 중 하나는 추가적인 값을(delta) 더해준다 보통 128
'''
# 해결방법1
# 회색은 값 변화량이 크지 않은부분,
# 검정색 부분은 값이 급격하게 감소한 부분
# 하얀색 부분은 값이 급격하게 증가한 부분
# dx= cv2.filter2D(src, -1, kernel_x, delta=128)
 
#ddepth=-1(입력영상과 같은 타입)
#dx=1, dy=0
dx = cv2.Sobel(src, -1, 1, 0, delta=128)

#dx=0, dy=1
dy = cv2.Sobel(src, -1, 0, 1, delta=128)
'''
dx,dy 왼쪽 기둥부분을 보면
x방향에는 기둥이 보이지만 dy에는 보이지 않는다
왜냐면, x방향기준에는 값의 변동이 있지만 y방향기준에서 볼때는 변동으로
보이지 않기 때문
'''

cv2.imshow('src', src)
cv2.imshow('dx', dx)
cv2.imshow('dy', dy)
cv2.waitKey()

cv2.destroyAllWindows()


 

## canny

import sys
import numpy as np
import cv2

src = cv2.imread('building.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()
#src: 컬러영상도 가능하지만 일반적으로는 그레이스케일사용한다
#컬러영상 3채널을 모두 미분계산 한 다음에 가장 최대값을 적용한다
dst = cv2.Canny(src, 100, 150)

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

cv2.destroyAllWindows()

 

### 허프변환

import sys
import numpy as np
import cv2


src = cv2.imread('building.jpg', cv2.IMREAD_GRAYSCALE)

if src is None:
    print('Image load failed!')
    sys.exit()

edges = cv2.Canny(src, 50, 150) #캐니 엣지 검출기

#임계값 160, rho 값은 1 픽셀 ,각도 값은 1도단위로, threshold 160
lines = cv2.HoughLinesP(edges, 1, np.pi / 180., 160,
                        minLineLength=50, maxLineGap=5)

#컬러변환을 해야지 빨간성분의 선분(0,0,255)을 표현할수가 있다
dst = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

if lines is not None: #직선이 없을수도 있음
    for i in range(lines.shape[0]): #lines.shape[0]: 직선성분의 갯수

                    #x       ,      y
        pt1 = (lines[i][0][0], lines[i][0][1])  # 시작점 좌표
                    #x       ,      y
        pt2 = (lines[i][0][2], lines[i][0][3])  # 끝점 좌표
        cv2.line(dst, pt1, pt2, (0, 0, 255), 2, cv2.LINE_AA)

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

### 허프변환 원 

import sys
import numpy as np
import cv2


# 입력 이미지 불러오기
src = cv2.imread('dial.jpg')

if src is None:
    print('Image open failed!')
    sys.exit()

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
blr = cv2.GaussianBlur(gray, (0, 0), 1.0)
 

def on_trackbar(pos):
    rmin = cv2.getTrackbarPos('minRadius', 'img')
    rmax = cv2.getTrackbarPos('maxRadius', 'img')
    th = cv2.getTrackbarPos('threshold', 'img')

    circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50,
                               param1=120, param2=th, minRadius=rmin, maxRadius=rmax)

    dst = src.copy()
    if circles is not None:
        for i in range(circles.shape[1]):
            cx, cy, radius = circles[0][i]
            print(cx, cy, radius)
            cv2.circle(dst, (cx, cy), radius, (0, 0, 255), 2, cv2.LINE_AA)

    cv2.imshow('img', dst)


# 트랙바 생성
cv2.imshow('img', src)
cv2.createTrackbar('minRadius', 'img', 0, 100, on_trackbar)
cv2.createTrackbar('maxRadius', 'img', 0, 150, on_trackbar)
cv2.createTrackbar('threshold', 'img', 0, 100, on_trackbar)

#트랙바 초기값
cv2.setTrackbarPos('minRadius', 'img', 10)
cv2.setTrackbarPos('maxRadius', 'img', 80)
cv2.setTrackbarPos('threshold', 'img', 40)
cv2.waitKey()

cv2.destroyAllWindows()

 

### 동전계산 실습

import sys
import numpy as np
import cv2


# 입력 이미지 불러오기
src = cv2.imread('coins1.jpg')

if src is None:
    print('Image open failed!')
    sys.exit()

gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
blr = cv2.GaussianBlur(gray, (0, 0), 1) #HoughCircle는 노이즈에 민감하기때문에 블러를 사용해서 노이즈제거 한다

# 허프 변환 원 검출
circles = cv2.HoughCircles(blr, cv2.HOUGH_GRADIENT, 1, 50,
                           param1=150, param2=40, minRadius=20, maxRadius=80)

# 원 검출 결과 및 동전 금액 출력
sum_of_money = 0 
dst = src.copy()
if circles is not None: #원이 하나라도 검출이 됐을경우
    for i in range(circles.shape[1]): # 원에 대한 정보를 추출, 총 11개
        cx, cy, radius = circles[0][i] #반지름은 radius, 중심좌표 cx,cy
        cv2.circle(dst, (cx, cy), int(radius), (0, 0, 255), 2, cv2.LINE_AA)

        # 동전 영역 부분 영상 추출
        #실수형을 정수형으로 컨버젼
        x1 = int(cx - radius) # 원의 맨 좌측(좌)
        y1 = int(cy - radius) # 원의 맨 아래(상)
        x2 = int(cx + radius) # 원의 맨 우측(우)
        y2 = int(cy + radius) # 원의 맨 위  (하)
        radius = int(radius)

        
        # 동전 영역 부분 영상을 입력영상에서 짤라낸다
        crop = dst[y1:y2, x1:x2, :] #3채널 모두에 적용
        ch, cw = crop.shape[:2]
        cv2.imshow('crop',crop)         
        print(crop.shape) 
        print(ch,cw)

        # 동전 영역에 대한 ROI 마스크 영상 생성
        # 히스토그램을 그리기 위한 mask 
        mask = np.zeros((ch, cw), np.uint8) #crob과 동일한 크기의 검정색 형태로 일단 만든다
        # cv2.imshow('mask',mask) #검정색 바탕만 보임      
          
        cv2.circle(mask, (cw//2, ch//2), radius, 255, -1) #반지름 radius을 중심으로 안을 채우는 흰색(255)으로 만든다
        cv2.imshow('mask',mask) 
        cv2.waitKey()
        
        # 동전 구분을 위한 색 성분 작업
        # 동전 영역 Hue 색 성분을 +40 시프트하고, Hue 평균을 계산
        hsv = cv2.cvtColor(crop, cv2.COLOR_BGR2HSV) #crop은 컬러 이며, 동전이 들어갈만한 사각형 크기로 짤려 있고 빨간색 테두리가 있는 영상을 HSV로 변환
        hue, _, _ = cv2.split(hsv) #색 성분 추출

        #십원짜리 동전의 히스토그램이 180근방에서 나왔기때문에 shift 진행
        #180보다 큰 값은 0근방으로 보내기 위해서 나머지 나눗셈 % 사용 
        #결론, 십원짜리는 0~50 픽셀, 백원은 140~160 픽셀을 가지는 값을 갖게된다
        #현재 hue_shift에는 동전 뿐만 아니라 사각형의 모서리까지 픽셀 40을 더해졌다 이 짜투리 부분은 mask를 통해 검정색으로 덮어씌운다
        hue_shift = (hue + 40) % 180 

        #mask를 줘서 원 안에 있는 것들만 계산
        #첫번째 인덱스가 평균값이다.
        #mean_of_hue은 십원인지 백원인지 판정할수 있는 지표가 된다
        #mask를 컬러 동전과 겹친다 -> 바탕이 검정색 
        mean_of_hue = cv2.mean(hue_shift, mask)[0]

        # Hue 평균이 90보다 작으면 10원, 90보다 크면 100원으로 간주
        won = 100 
        if mean_of_hue < 90:
            won = 10

        sum_of_money += won
        
        #글의 위치는 crop 원안에서 (20,50) 위치, 대략 중심위치이다.
        #글자 크기는 0.75, 두께 2 
        cv2.putText(crop, str(won), (20, 50), cv2.FONT_HERSHEY_SIMPLEX,
                    0.75, (255, 0, 0), 2, cv2.LINE_AA)

#전체 금액을 dst 상단에 보여준다
cv2.putText(dst, str(sum_of_money) + ' won', (40, 80),
            cv2.FONT_HERSHEY_DUPLEX, 2, (255, 0, 0), 2, cv2.LINE_AA)

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

cv2.destroyAllWindows()
728x90
728x90

2021/02/22 - [기록 note] - 2021-02-22(OpenCV_4)

 

2021-02-22(OpenCV_4)

오늘은 기하학적 변환을 공부했습니다 좌표계산하는게 고도의 계산력이 필요한건 아니지만 꽤 헷갈렸습니다 상상해 가면서 이해하려고 하니까 시간도 꽤 들었구요 특히, 마지막에 종합실습을

ghdrldud329.tistory.com

 

 

이동변환

### 영상이동변환

import sys
import numpy as np
import cv2


src = cv2.imread('tekapo.bmp') #640*480

if src is None:
    print('Image load failed!')
    sys.exit()

# affine 변환행렬을 먼저 만들어야 한다->변환행렬을 만드는 함수는 getAffineTransform
aff= np.array([[1,0,200],[0,1,100]], dtype=np.float32) #가로 200픽셀, 세로 100픽셀 이동

# warpAffine는 변환행렬을 알고 있는경우, 결과 영상을 보기 위한 함수
dst=cv2.warpAffine(src, aff, (0,0)) #(0.0):입력영상과 동일한 크기 출력

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

전단 변환

import sys
import numpy as np
import cv2


src = cv2.imread('tekapo.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()
                #0.5: x좌표 
aff = np.array([[1,0.5,0], [0,1,0]], dtype=np.float32)
'''
출력영상 크기를 (w+h*0.5,h)로 수정해야 x방향만큼 밀려나간 이미지를 볼수 있다
밀려나간 x값만큼 출력영상에 더해주면 된다
가로크기가 h*0.5만큼 밀렸다. 여기서 (0,0)으로 출력영상을 찍으면 밀린 이미지는 짤려서 보이지 않는다
입력영상은 정수형태로 있어야 하므로 int로 형변환 시킨다.
'''
h, w = src.shape[:2]

dst=cv2.warpAffine(src, aff,(w + int(h * 0.5), h))




cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

크기변환

### 영상의 크기변환

import sys
import numpy as np
import cv2


src = cv2.imread('rose.bmp') # src.shape=(320, 480) = (세로,가로)

if src is None:
    print('Image load failed!')
    sys.exit()

dst1 = cv2.resize(src, (0, 0), fx=4, fy=4, interpolation=cv2.INTER_NEAREST) #INTER_NEAREST: 가장 저품질
dst2 = cv2.resize(src, (1920, 1280))  # cv2.INTER_LINEAR, 480*4=1920, 320*4=1280
dst3 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_CUBIC)
dst4 = cv2.resize(src, (1920, 1280), interpolation=cv2.INTER_LANCZOS4)

cv2.imshow('src', src) 
                  #dst1[세로좌표,가로좌표]
cv2.imshow('dst1', dst1[500:900, 400:800])
cv2.imshow('dst2', dst2[500:900, 400:800])
cv2.imshow('dst3', dst3[500:900, 400:800])
cv2.imshow('dst4', dst4[500:900, 400:800])
cv2.waitKey()
cv2.destroyAllWindows()

 

영상 피라미드 

### 이미지 파라미드

import sys
import numpy as np
import cv2


src = cv2.imread('cat.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

    #(x,y,w,h)
rc = (250, 120, 200, 200)  # rectangle tuple

# 원본 영상에 그리기
cpy = src.copy()
cv2.rectangle(cpy, rc, (0, 0, 255), 2) #빨간색으로 두께가 2픽셀짜리 사각형
cv2.imshow('src', cpy)
cv2.waitKey()

# 피라미드 영상에 그리기
# 이미지다운
for i in range(1, 4):
    src = cv2.pyrDown(src)
    cpy = src.copy()

    #shift: 가로세로를 얼만큼 줄일건지 결정
    #2이면 원본에 2배 줄이고, 3이면 원본에 3배
    cv2.rectangle(cpy, rc, (0, 0, 255), 2, shift=i)
    cv2.imshow('src', cpy)
    cv2.waitKey()
    cv2.destroyWindow('src') #이전src가 닫혔다가 새 src가 열리는 형태
    
#이미지 업
for i in range(1,4):
	cpy=cv2.pyrUp(cpy) #축소된 이미지를 입력으로 받는다
    cv2.imshow('src2',cpy)
    cv2.waitKey()
    cv2.destroyWindow()
  
cv2.destroyWindows()

cv2.destroyAllWindows()

 

회전변환_1

### 회전 변환

import sys
import math
import numpy as np
import cv2


src = cv2.imread('tekapo.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()
#중심점이 좌측상단이다. 주로 이미지의 중심점으로 회전한다 => getRotationmatrix2D
#반시계방향으로 20도
#시계방향으로는 -20도로 음수를 붙인다.
rad = 20 * math.pi / 180 #각도(degreed) 20를 radian으로 고친 과정(단위 변경)
aff = np.array([[math.cos(rad), math.sin(rad),0],
                [-math.sin(rad), math.cos(rad),0]], dtype=np.float64)

dst= cv2.warpAffine(src, aff, (0,0))

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

cv2.destroyAllWindows()

 

회전변환_2

### 회전 변환2

import sys
import numpy as np
import cv2


src = cv2.imread('tekapo.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

#입력영상의 가로,세로 크기를 반으로 나눈다
#순서는 가로, 세로
cp=(src.shape[1]/2, src.shape[0]/2)

'''
getRotationMatrix2D(center,angle,scale)
center : 중심점 좌표
angle: 각도 (음수는 시계방향)
크기조절 scale, 회전만 하고싶은때는 1 입력
'''
rot = cv2.getRotationMatrix2D(cp,20,1)
print(rot)
dst=cv2.warpAffine(src,rot,(0,0))


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

cv2.destroyAllWindows()

 

투시변환

### 투시 변환

import sys
import numpy as np
import cv2


src = cv2.imread('namecard.jpg')

if src is None:
    print('Image load failed!')
    sys.exit()

w, h = 720, 400 #출력영상 크기 정의

                    #[좌측상단],[우측상단],[우측하단],[좌측하단]
srcQuad = np.array([[325, 307], [760, 369], [718, 611], [231, 515]], np.float32)

                    #[좌상단점],[우상단점],[우하단점],[좌하단점]
dstQuad = np.array([[0, 0], [w, 0], [w, h], [0, h]], np.float32) #ndarray로 만든다

pers = cv2.getPerspectiveTransform(srcQuad, dstQuad) #pers 3*3 형태의 투시변환 행렬을 받는다
dst = cv2.warpPerspective(src, pers, (w, h))

cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()

 

리매핑

### 리매핑

import sys
import numpy as np
import cv2

'''
map1: x좌표 정보
map2: y좌표 정보
'''
src = cv2.imread('tekapo.bmp')

if src is None:
    print('Image load failed!')
    sys.exit()

h, w = src.shape[:2]

# indices: x,y 좌표 의 인덱스 값
# map1(x좌표)는 행 원소가 1씩 증가
# map2(y좌표)는 열 원소가 1씩 증가
map2,map1 = np.indices((h,w), dtype=np.float32)
# print("ymap2",map2)
print("xmap1",map1[0:10, 0:10])

#상하(위아래로)로 10픽셀
#여러번 파도가 칠수있도록 map1/32 입력
map2 = map2+10*np.sin(map1/32)

#BORDER_DEFAULT: 영상 바깥쪽의 가상영상을 검정색으로 칠하는게 아니라 주변 픽셀과 비슷한 같으로 대체하여 채워준다
dst = cv2.remap(src, map1, map2, cv2.INTER_CUBIC, borderMode=cv2.BORDER_DEFAULT)

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

cv2.destroyAllWindows()

 

 

찌그러진 영상 펴기

### 종합실습

import sys
import numpy as np
import cv2


def drawROI(img, corners): #corners= np.array([[30, 30], [30, h-30], [w-30, h-30], [w-30, 30]])
    cpy = img.copy()

    c1 = (192, 192, 255) #1번컬러 ==맑은 핑크색, 원
    c2 = (128, 128, 255) #2번컬러 ==탁한 핑크색, 사각형 라인

    for pt in corners:
        #-1: 원의 내부를 채운다
        cv2.circle(cpy, tuple(pt), 25, c1, -1, cv2.LINE_AA)

    #사격형 표현하기
    #corners은 ndarray이므로 그대로 넣어주면 에러발생
    #넘길때 tuple로 묶어서 전달 
    cv2.line(cpy, tuple(corners[0]), tuple(corners[1]), c2, 2, cv2.LINE_AA)
    cv2.line(cpy, tuple(corners[1]), tuple(corners[2]), c2, 2, cv2.LINE_AA)
    cv2.line(cpy, tuple(corners[2]), tuple(corners[3]), c2, 2, cv2.LINE_AA)
    cv2.line(cpy, tuple(corners[3]), tuple(corners[0]), c2, 2, cv2.LINE_AA)

    #원래 이미지 img에 원과 직선들을 그려놓은 이미지 cpy를 addWeighted 이용해서 합성한다
    #가중치가 있으므로 배경이 살짝 비치는 정도(중첩효과)
    #addWeighted는 전체 픽셀을 계산하기 때문에 드래그 이동 시 조금 늦게 따라온다
    disp = cv2.addWeighted(img, 0.3, cpy, 0.7, 0) 

    #return에 그냥 cpy를 넣으면 가중치가 없으므로 img가 비치지 않고 이미지가 위에 덮힌다
    #하지만 빠르게 동작함, 끊김 없음
    return disp #cpy

#콜백함수이므로 아래처럼 다섯개의 파리미터를 갖는다
#flags: 마우스,키보드 상태(클릭하고 있는지 등)
def onMouse(event, x, y, flags, param):
    global srcQuad, dragSrc, ptOld, src # 위 5개 파라미터 이외에 사용되는 것들 불러오기

    #마우스가 눌렸을때의 event
    if event == cv2.EVENT_LBUTTONDOWN:
        for i in range(4):

            #srcQuad: 네개의 원 좌표
            #25는 원의 반지름
            #내가 클릭한 점이 원 안에 있다면 드래그를 시작한다
            #ex) (30,30)과 현재 찍은 좌표의 "거리"가 25미만인 경우에만 드래그
            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]:#True인경우 = 어떤 점을 붙잡고 드래그 하고 있는 경우에만
                dx = x - ptOld[0] #현재 좌표 - 마우스가 이전에 있던 좌표=> dx 변위를 계산
                dy = y - ptOld[1] #즉 이전에 마우스에서 얼만큼 이동했는지 dx,dy를 통해 알수 있다

                #dx,dy만큼 srcQuad(네개 좌표)를이동 
                #-=를 하게되면 드래그 하고자 하는 방향의 반대 방향으로 가게된다
                srcQuad[i] += (dx, dy)

                #이동한 만큼 화면에 보여주기 위해서 아래 처럼 작성
                #아래 코드가 없으면 드래그,클릭 모두 작동 안함
                cpy = drawROI(src, srcQuad)
                cv2.imshow('img', cpy) #클릭, 드래그 한 영상
                
                '''
                ptOld를 x,y로 최신화를 안해주면 값이 크게 벌어진다.
                위에서 맨 처음 ptOld값은 처음 마우스로 반지름 원 25안의 임의의 포인트이다.
                이값을 53줄에 보면 ptOld 변수에 넣었다. 그리고 65줄에서는 x-ptOld[0], 마우스가 옮겨진 좌표-맨처음 딱 클릭한 원 25안의 임의의 좌표이다.
                65줄에서 이동한 거리를 구하고 이값을 srcQuad에 넣어서 더해줌으로써 제대로 이동이 가능해진다
                그리고 아까 65줄에서 뺄셈했던 x,y값을 넣어줘야지
                다음 for문의 65줄에서 (현재 위치 - 바로 직전의 포인트) 식이 성립이 된다.
                만일 계속 ptOld값을 처음 찍었던 임의의 포인트로 고정한다면 드래그를 멀리 이동할수록 아래처럼 값이 커져버린다 아래 예를들면
                ptOld= (10,10)일때 드래그를 쭉 옮긴다면 20-10 -> 40-10 -> 100-10 -> 150-10 순으로  값이 10,30,90,140 갑자기 커지는데 최신화를 하면
                20-10 -> 40-20 -> 100- 40 -> 150 - 100 으로 10,20,60,50 딱 이동한 거리만큼 구해진다
                '''
                ptOld = (x, y) #현재점으로 다시 셋팅
                break


# 입력 이미지 불러오기
src = cv2.imread('scanned.jpg')

if src is None:
    print('Image open failed!')
    sys.exit()

# 입력 영상 크기 및 출력 영상 크기
h, w = src.shape[:2]

dw = 500 #임의로 가로 크기 지정
dh = round(dw * 297 / 210)  # A4 용지 크기: 210x297cm의 비율에 맞게 계산

# 모서리 점들의 좌표, 드래그 상태 여부

#srcQuad: 내가 선택하고자 하는 모서리 네개의 ndarray
#반시계 방향
srcQuad = np.array([[30, 30], [30, h-30], [w-30, h-30], [w-30, 30]], np.float32)

#dstQuad: 반시계방향의 출력영상 네개의 모서리 위치
dstQuad = np.array([[0, 0], [0, dh-1], [dw-1, dh-1], [dw-1, 0]], np.float32)

#srcQuad 네개의 점들중에 현재 어떤 점을 드래그 하고 있는지에 대한 상태정보
dragSrc = [False, False, False, False]

# 모서리점, 사각형 그리기
disp = drawROI(src, srcQuad)

cv2.imshow('img', disp) #맨 처음 나오는 영상
cv2.setMouseCallback('img', onMouse)

while True:
    key = cv2.waitKey()
    if key == 13:  # ENTER 키
        break
    elif key == 27:  # ESC 키
        cv2.destroyWindow('img') 
        sys.exit()  #아예 프로그램을 종료함


#네점의 변환행렬 출력과 영상출력 
# 투시 변환
pers = cv2.getPerspectiveTransform(srcQuad, dstQuad) # 새로운 4개 점이 결정된 변환행렬 
print(pers)
dst = cv2.warpPerspective(src, pers, (dw, dh), flags=cv2.INTER_CUBIC)

# 결과 영상 출력
cv2.imshow('dst', dst)
cv2.waitKey()
cv2.destroyAllWindows()
728x90
728x90
  • 실습한 내용은 데이콘 경진대회 1등 솔루션 책 내용입니다.
  • 제 3장인 버스 승차인원 예측 실습 내용을 포스팅 하겠습니다.
  • 전체적인 진행 설명은 파일안에 기록했습니다

버스 승차인원 예측.ipynb
0.31MB

전에 했던 택시수요예측과 비슷한 부분이 있어서 이해하는데에 큰 어려움이 없었습니다

1장에서는 결측치 핸들링 내용있었다면 3장 실습에서는 결측치 대신 파생변수&변수선택법이 포인트라고 생각합니다

 

종속변수와 관련된 독립변수에 대해서 파생변수를 만드는 과정이 설명되어 있었습니다 

그래서 변수를 만들고 원본에 merge하는 코드가 꽤 많았습니다 

1장에서도 merge 코드 있었지만 3장이 압도적으로 많았습니다 

 

그렇게 늘린 변수에 대해서 A/B테스트로 변수 선택을 진행했습니다 

그리고 또 하나 인상적인 것은 5개의 모델을 앙상블한 부분입니다.

각모델을 앙상블하면 좀더 일반화에 가까우면서 안정성이 높은 모델이 된다고 합니다.

 

실습안의 모델들은 실행하지 못한점 미리 알리면서 마무리하겠습니다

 

2021/02/15 - [기록 note] - 2021-02-15(데이콘_버스 승차인원예측 실습)

2021/02/16 - [기록 note] - 2021-02-15(데이콘_버스 승차인원예측 실습2)

728x90

'실습 note' 카테고리의 다른 글

OpenCV_5(영상의 특징추출)  (0) 2021.02.23
OpenCV_4(기하학적 변환)  (0) 2021.02.22
KBO 타자 OPS 예측 실습(데이콘 경진대회 1등 솔루션)  (0) 2021.02.20
OpenCV_3(필터링)  (0) 2021.02.19
OpenCV_2(기본 영상처리)  (0) 2021.02.18

+ Recent posts