728x90
2021/02/22 - [기록 note] - 2021-02-22(OpenCV_4)
이동변환
### 영상이동변환
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
'실습 note' 카테고리의 다른 글
OpenCV_6(이진 영상 처리) (0) | 2021.02.24 |
---|---|
OpenCV_5(영상의 특징추출) (0) | 2021.02.23 |
버스 승차인원 예측 실습(데이콘 경진대회 1등 솔루션) (0) | 2021.02.20 |
KBO 타자 OPS 예측 실습(데이콘 경진대회 1등 솔루션) (0) | 2021.02.20 |
OpenCV_3(필터링) (0) | 2021.02.19 |