
import sys
import cv2
# 영상 불러오기 : imread 함수로
img1 = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE) # grayscale 로
img2 = cv2.imread('cat.bmp', cv2.IMREAD_COLOR) # color 로
if img1 is None or img2 is None:
print('Image load failed!')
sys.exit()
# 영상의 속성 참조
print('type(img1):', type(img1)) # numpy.ndarray 타입.
print('img1.shape:', img1.shape) # shape : (480, 640) : grayscale 로 2차원
print('img2.shape:', img2.shape) # shape : (480, 640, 3) : color 로 3차원
print('img1.dtype:', img1.dtype) # uint8
print('img1.dtype:', img2.dtype) # uint8
# 영상의 크기 참조
'''
shape 값은 (세로, 가로) 순서임. 그래서 보통 변수로 받아서 사용하고 싶은 경우 아래 코드처럼 작성하는데,
print('w x h = {} x {}'.format(w,h))
: 이런 식으로 출력하면, img2 로 받을 때는 오류가 남.
컬러라 3차원인데, 차원 수가 맞지 않아서, 그래서 영상 데이터를 불러와서
세로 크기 가로크기를 h, w 로 받기 위해서는
그냥 shape 이라고 쓰지 않고, img2.shape[:2] 이런 식으로 씀.
'''
h, w = img2.shape[:2] # 영상의 세로, 가로 크기 받아오는 일반적인 방법 기억해두기 !
print('img2 size: {} x {}'.format(w, h))
# 만약, grayscale 인지, color 인지 알고 싶으면 아래 코드로 확인하면 됨.
# if img1.ndim == 2: -> true : grayscale, false : color <- 이 코드도 가능
if len(img1.shape) == 2: # 튜플 값의 길이로도 판별 가능
print('img1 is a grayscale image')
elif len(img1.shape) == 3:
print('img1 is a truecolor image')
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.waitKey()
# 영상의 픽셀 값 참조 예제
# 픽셀 값을 참조하는 방법은, x 값을 가져다가 20, y 를 10 이라고 했을 때
# img1[y, x] 라고 하면 img1 dml (x, y) 위치-행렬 기준이므로 반대-의 픽셀값을 알 수 있음.
x = 20
y = 10
p1 = img1[y, x] # 238
p2 = img2[y, x] # [237 242 232] blue, green, red 성분임.
print(p1, p2) # 238 픽셀 값
'''
for y in range(h):
for x in range(w):
# 이런 식으로 대입을 통해 색 값을 정할 수 있음.
img1[y, x] = 255
img2[y, x] = (0, 0, 255)
'''
# 하지만 for 문을 통해 영상 데이터의 색을 변화시키는 작업은 하지 않는게 좋음
# 그 이유는 매우 느리기 때문, 가급적이면 opencv 나 numpy 에서 제공하는 방법을 이용하기.
# C언어는 위의 방법처럼 해도 됨.
# 아래 코드처럼 범위까지 지정해서 모든 픽셀값을 한번에 세팅하는 방법이 더 빠름
img1[:,:] = 255
img2[:,:] = (0, 0, 255)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.waitKey()
cv2.destroyAllWindows()

import numpy as np
import cv2
# 새 영상 생성하기
img1 = np.empty((240, 320), dtype=np.uint8)
# grayscale image, 가로 : 320, 세로 : 240인 크기의 영상.
# empty 로 생성했으므로 임의의 픽셀값들로 생성됨.
# 이 함수로 만든 영상 데이터는 모든 픽셀값을 다시 세팅해주는 작업 필요.
img2 = np.zeros((240, 320, 3), dtype=np.uint8)
# color image, 모든 픽셀이 0으로 설정됨
img3 = np.ones((240, 320), dtype=np.uint8) * 255
# dark gray, 모든 픽셀이 1로 채워짐. 곱하기 연산으로 색을 지정할 수 있음.
img4 = np.full((240, 320, 3), (0, 255, 255), dtype=np.uint8)
# yellow, 내가 정하는 픽셀값으로 채우는 함수
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.imshow('img4', img4)
cv2.waitKey()
cv2.destroyAllWindows()
# 영상 복사
img1 = cv2.imread('HappyFish.jpg')
img2 = img1 # 이렇게 equal 연산자를 쓰게 되면 img2와 img1 은 똑같은 영상이 됨.
img3 = img1.copy() # 이런 방법도 있음.
# 위의 복사 방법 2가지의 차이는 equal 연산자를 쓰면 img2 가 img1 의 데이터를 공유하는 것.
# 참조와 같은 개념
# img1.copy() 는 변수에 말 그대로 복사를 하는 것.
#img1.fill(255)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey()
cv2.destroyAllWindows()
# 부분 영상 추출 : 인덱싱, 슬라이싱 방법을 쓰면 됨
# 예를 들어서 img1 에서 40번째 행부터 120번째 행까지, 30번째 열에서 150번째 열까지, img2 에다 넣고,
# .copy() 와 equal 연산자를 활용했을때, img2 을 가져다가 fill 을 하면 img1 도 함께 fill 됨 : 공유, 참조
# 그래서 인덱싱, 슬라이싱 방법으로 부분 영상을 추출할 때도 .copy() 를 하면 추출이 됨. 공유하는 것이 아님.
img1 = cv2.imread('HappyFish.jpg')
img2 = img1[40:120, 30:150] # numpy.ndarray의 슬라이싱
img3 = img1[40:120, 30:150].copy()
img2.fill(0) # img2 를 fill 했는데, img1 부분 픽셀도 바뀜.
# 이런 방법을 이용해서 영상에서 특정 부분에 대한 ROI(관심영역) 를 지정해서 처리가능
# 예를 들어, opencv 에서의 circle
cv2.circle(img2, (50,50), 20, (0,0,255),2)
cv2.imshow('img1', img1)
cv2.imshow('img2', img2)
cv2.imshow('img3', img3)
cv2.waitKey()
cv2.destroyAllWindows()
이때도, .copy() 방법을 사용해야 함. 안 그럼 추출한 데이터를 수정하면, 원본 데이터도 추출된 위치가 수정됨.


import sys
import cv2
# 마스크 영상을 이용한 영상 합성
# 지금 작업하고 싶은 것은 src 내용에서 비행기 부분만 잘라내서 dst 부분에다가 붙여넣고 싶은 것.(합성)
# 그럼 src 부분에서 mask 에 흰 색으로 되어 있는 부분만 복사해서 dst 에 합성하고 싶은 것.
src = cv2.imread('airplane.bmp', cv2.IMREAD_COLOR)
mask = cv2.imread('mask_plane.bmp', cv2.IMREAD_GRAYSCALE)
dst = cv2.imread('field.bmp', cv2.IMREAD_COLOR)
if src is None or mask is None or dst is None:
print('Image load failed!')
sys.exit()
# src 영상에서 mask 부분을 이용해서 dst 를 만들고 싶은 것.
cv2.copyTo(src, mask, dst)
'''
그런데 이 dst 를 copyTo 함수의 리턴이 되기 때문에 아래 코드처럼 작성하면 안된다는 의미
dst = cv2.copyTo(src, mask) : background 가 검정이 됨.
꼭 입력에다가 dst 를 줘야 함.
그리고 src, mask, dst 는 모두 size 도 같아야 하고, src 와 dst 는 타입도 같아야 함.
src 가 그레이스케일이면 dst 도 그레이 스케일이어야 함.
mask 는 언제나 그레이 스케일
'''
'''
# 아래 방법은 위의 코드를 numpy 에서 제공하는 불리언 인덱싱 방법임.
dst[mask > 0] = src[mask > 0]
: dst 에서 mask 픽셀값이 0보다 큰 값을 다 찾고, src 영상에서도 mask 값이 0보다 큰 값을 다 찾음
mask > 0 : 이 연산의 결과는 mask 행렬과 동일한 크기로 생성되는데, boolean 값, true or false 행렬이 반환이 됨.
그 행렬을 src 행렬에 대해서 인덱싱 하게 되면, true 가 되어 있는 픽셀만 가져올 수 있음.
그리고 그 픽셀값을 동일하게 dst 에다 세팅을 하는 것.
참조 형태로 복사하는 것.
'''
cv2.imshow('src', src)
cv2.imshow('dst', dst)
cv2.imshow('mask', mask)
cv2.waitKey()
cv2.destroyAllWindows()
# 알파 채널을 마스크 영상으로 이용
# 이런 마스크 영상을 이용한 copy 기능을 이용해서 이번에는 간단하게 투명한 png 파일을 불러와서 다른 영상에 합성하는 것을 해볼 것
src = cv2.imread('cat.bmp', cv2.IMREAD_COLOR)
logo = cv2.imread('opencv-logo-white.png', cv2.IMREAD_UNCHANGED)
# 채널이 4개짜리로 되어 있는 거라서 이를 opencv 로 불러와서 사용하려면 imread unchanged 라고 하는 플래그로 불러와야 함.
# logo 영상에서 앞의 채널 3개는 color 값이고, 마지막 채널 하나는 마스크 영상 형태로 사용할 수 있음.
if src is None or logo is None:
print('Image load failed!')
sys.exit()
mask = logo[:, :, -1]
# mask는 알파 채널로 만든 마스크 영상 : 알파 채널에서 마지막 부분만 가져와서 사용
logo = logo[:, :, :3]
# logo는 b, g, r 3채널로 구성된 컬러 영상 : 0,1,2 채널만 가져와서 쓸 것
h, w = mask.shape[:2]
crop = src[10:10+h, 10:10+w]
# logo, mask와 같은 크기의 부분 영상 추출 : 이 코드에서 crop 을 변경하면 src 도 변경됨.
cv2.copyTo(logo, mask, crop)
# 크기가 맞지 않아서, 그냥 dst 를 쓰면 안됨. dst 에서 일부분을 crop 해야 함.
#crop[mask > 0] = logo[mask > 0]
cv2.imshow('src', src)
cv2.imshow('logo', logo)
cv2.imshow('mask', mask)
cv2.waitKey()
cv2.destroyAllWindows()





import numpy as np
import cv2
# 컬러 영상을 하나 만들기, 흰색으로 채워져 있는 것.
img = np.full((400, 400, 3), 255, np.uint8)
# 직선 그리기
cv2.line(img, (50, 50), (200, 50), (0, 0, 255), 5)
# (x, y) 좌표 순서임. 즉, 가로로 150 픽셀 정도의 직선을 그리는 것, 색은 빨간색, 선 두께는 5픽셀
cv2.line(img, (50, 60), (150, 160), (0, 0, 128))
# 검 붉은 색으로 그림을 그리고 45도 아래로 그리는 형태
# 사각형 그리기
cv2.rectangle(img, (50, 200, 150, 100), (0, 255, 0), 2)
# 사각형의 좌측 상단의 좌표와 사각형의 width, hight를 제공
cv2.rectangle(img, (70, 220), (180, 280), (0, 128, 0), -1)
# 사각형의 대각성에 위치해 있는 두 꼭지점의 좌표, thinkness 를 음수로 지정하면 내부를 채우게 됨.
# 원 그리기
cv2.circle(img, (300, 100), 30, (255, 255, 0), -1, cv2.LINE_AA)
# -1 이므로 내부를 채우는 형태. 그 뒤에 lineType 은 기본 값은 8 인데,
# 이대로 설정하고 그림을 그리면 선이 거친 느낌이 있음.
cv2.circle(img, (300, 100), 60, (255, 0, 0), 3, cv2.LINE_AA)
# 다각형 그리기
pts = np.array([[250, 200], [300, 200], [350, 300], [250, 300]])
# 이 지정 방식 기억하기
# 일단, 이 pts 라고 하는 것은 네 개의 점을 2차원 행렬, numpy ndarray 형태로 만들고
# 그 다음에 이 것을 함수에 입력할 때 그냥 pts 로 입력하는 것이 아니라 [pts] 이렇게 리스트 형태로 감싸서 입력을 해야 함.
cv2.polylines(img, [pts], True, (255, 0, 255), 2)
# 대각선으로 뻗어나가는 형태의 다각형도 linetype 을 AA 로 설정하는 것이 좋음.
# 문자열 출력
text = 'Hello? OpenCV ' + cv2.__version__ # 문자열 데이터 생성
cv2.putText(img, text, (50, 350), cv2.FONT_HERSHEY_SIMPLEX, 0.8,
(0, 0, 255), 1, cv2.LINE_AA) # 문자열 출력도 AA 를 쓰는 것을 추천
cv2.imshow("img", img)
cv2.waitKey()
cv2.destroyAllWindows()






import sys
import cv2
# 카메라 열기, 클래스 객체 생성
cap = cv2.VideoCapture(0) # 아래의 open 함수를 열지 않아도 생성자 함수로 열 수 있음.
#cap.open(0)
# open 을 하고 나서는 cap.isOpend 를 통해 정상적으로 열렸는지 확인하는 것이 좋음.
if not cap.isOpened():
print("Camera open failed!")
sys.exit()
# 카메라 프레임 크기 출력, 카메라 프레임 크기를 알고 싶으면 이런 함수를 사용해서 알아볼 수 있음.
# 이 get 함수에는 다양한 속성 값 존재 ppt 참고
print('Frame width:', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))) # 가로 크기
print('Frame height:', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))) # 세로 크기
# 다만, 이 get 이라는 함수가 리턴하는 타입이 무조건 실수형 double 타입으로 리턴을 해주기 때문에
# int 타입으로 변환해서 변수에 대입하는 게 좋은 경우도 있음.
# set 함수로 카메라 프레임의 크기를 설정할 수 있지만, 카메라와 호환이 되어야 함.
# cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640)
# 카메라 프레임 처리 : while 문으로 프레임을 계속 받아오는 작업
while True:
ret, frame = cap.read() # 프레임을 받아오는 함수. 기억하기 !
# 이 read() 함수에서 중요한 것은 리턴되는 형식임.
# 이 함수는 현재 프레임을 그대로 반환하는 것이 아니라 현재 프레임을 제대로 받아왔는지를 확인하는 boolean 타입의 값도 같이 리턴해줌.
# 그래서 이 함수를 하나의 변수로만 받게 되면 그 변수에는 ndarray 가 저장되는 것이 아니라 true or false 값이 저장됨.
# 그래서 이 함수를 받아올 때는 변수 두 개로 받아와야 함.
if not ret: # ret 가 false 면 break 되도록
break
# 이 부분부터는 동영상에서 프레임을 받아오고, frame 변수에 들어간 상태임
# 그럼 여기서 frame 이라는 정지영상을 처리하는 코드를 작성할 수 있음.
# 예를 들어, 동영상에서 윤곽선을 추출하는 함수 중 canny 함수가 있음 : 나중에 배울 것
edge = cv2.Canny(frame, 50, 150)
# inversed = ~frame # 반전
cv2.imshow('frame', frame) # 프레임을 화면에 보이는 함수
cv2.imshow('edge', edge)
# cv2.imshow('inversed', inversed)
if cv2.waitKey(10) == 27:
# 앞에서는 정지 영상 출력할 때는 waitkey 함수에 인자를 주지 않았지만, 여기서는 줘야 함.
# 10초 기다리고, 그 다음에 다음 프레임을 받아오도록 설정하는 것임.
# 근데 코드를 if 를 쓰지 않고 그냥 작성하게 되면
# while 을 빠져나올 방법이 없기 때문에 if 문을 통해 waitKey 가
# 반환한 값이 27일 경우에 (ESC 키를 눌렀을 때)
# break 를 통해 빠져나오는 것.
break
# 빠져나온 다음에는 cap 을 release 하고
cap.release()
cv2.destroyAllWindows() # 모든 창 닫기
import sys
import cv2
# 비디오 처리 코드는 카메라 처리 코드에서 파일 이름만 입력으로 넣어주면 되는 것.
# 비디오 파일 열기 : filename 입력
cap = cv2.VideoCapture('video1.mp4')
# 비디오가 잘 열렸는지 확인
if not cap.isOpened():
print("Video open failed!")
sys.exit()
# 비디오 프레임 크기, 전체 프레임수, FPS 등 출력
print('Frame width:', int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)))
print('Frame height:', int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)))
print('Frame count:', int(cap.get(cv2.CAP_PROP_FRAME_COUNT)))
fps = cap.get(cv2.CAP_PROP_FPS)
print('FPS:', fps)
delay = round(1000 / fps)
# 비디오 매 프레임 처리
while True:
ret, frame = cap.read()
# 동영상 처리에서 아래의 if 문이 실행된다는 건 동영상 파일의 마지막 부분을 가져와서 더 이상 가져올 frame 이 없을 때 실행되는 것.
if not ret:
break
edge = cv2.Canny(frame, 50, 150)
#inversed = ~frame # 반전
cv2.imshow('frame', frame)
cv2.imshow('edge', edge)
#cv2.imshow('inversed', inversed)
if cv2.waitKey(delay) == 27:
break
cap.release()
cv2.destroyAllWindows()





import sys
import cv2
cap = cv2.VideoCapture(0) # 기본 카메라 열기
# 예외 처리
if not cap.isOpened():
print("Camera open failed!")
sys.exit()
# 카메라 프레임 크기 확인
w = round(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
# 카메라마다 값이 정상적으로 넘어오기도 하고 아니기도 하는데, 보통 초다 30프레임 넘어옴
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
# *'DIVX' == 'D', 'I', 'V', 'X', 정수값을 리턴함.
delay = round(1000 / fps)
# fps 를 이용해서 현 프레임과 그 다음 프레임의 시간 간격을 계산하기 위한 값.
# VideoWriter 객체 생성
out = cv2.VideoWriter('output.avi', fourcc, fps, (w, h))
# output.avi 의 영상을 fourcc 코덱으로, fps 의 초당 프레임으로,
# (w,h) 의 프레임 크기로, 컬러로 저장하겠다는 의미
# 예외 처리 : 정상 열기 확인
if not out.isOpened():
print('File open failed!')
cap.release()
sys.exit()
while True:
ret, frame = cap.read()
if not ret:
break
#inversed = ~frame
edge = cv2.Canny(frame, 50,150)
#out.write(edge) # 이건 저장되지 않음.
# 왜냐하면 edge 는 그레이 스케일 형식으로 리턴이 되기 때문에, color 로 설정하면 저장되지 않음.
# edge 를 저장하고 싶다면 edge 를 color 로 변환해줘야 함.
edge_color = cv2.cvtColor(edge, cv2.COLOR_GRAY2RGB)
out.write(frame)
# VideoWriter 은 영상 데이터만 저장함 소리 데이터는 저장하지 않음.
out.write(edge_color)
# 눈으로 봤을 때는 edge 그레이스케일이나 컬러나 같음. 하지만 꼭 변환해줘야 저장이 됨.
cv2.imshow('edge', edge)
cv2.imshow('frame', frame)
#cv2.imshow('inversed', inversed)
if cv2.waitKey(delay) == 27:
break
cap.release()
out.release()
cv2.destroyAllWindows()

import sys
import numpy as np
import cv2
img = cv2.imread('cat.bmp', cv2.IMREAD_GRAYSCALE)
# 예외 처리
if img is None:
print('Image load failed!')
sys.exit()
cv2.namedWindow('image') # 윈도우 생성
cv2.imshow('image', img) # 화면 출력
while True:
keycode = cv2.waitKey()
# 이런 식으로 사용자가 입력한 리턴 값과 내가 종료 키로 지정하고 싶은 키를 비교해서 종료하도록 하는 것.
# 이 기능을 이용해서 영상 반전의 키를 inverse, I 키를 이용해서 기능을 추가하는 코드
if keycode == ord('i') or keycode == ord('I'):
# ord 는 아스키 코드 값으로 변환해주는 것.
img = ~img # 반전, 비트 반전 연산자임. 반전 연산자는 아니지만,
# unsigned charater 에서는 비트를 반전하겠다는것이
# 픽셀 값을 255에서 뺀 것과 동일한 형태로 동작하기 때문에 이를 자주 이용함.
cv2.imshow('image', img)
elif keycode == 27: # 종료 키 esc
break
cv2.destroyAllWindows()


import sys
import numpy as np
import cv2
oldx = oldy = -1
def on_mouse(event, x, y, flags, param):
# on_mouse 함수 : 이 함수의 인자 중에 사용하지 않는 것이 있어도 꼭 적어줘야 함.
# global img : Circle 그리는 것.
global img, oldx, oldy
if event == cv2.EVENT_LBUTTONDOWN: # 마우스 왼쪽 버튼이 눌려지는 경우
oldx, oldy = x, y
print('EVENT_LBUTTONDOWN: %d, %d' % (x, y)) # 좌표 출력
elif event == cv2.EVENT_LBUTTONUP: # 마우스 왼쪽 버튼이 떼어지는 경우
print('EVENT_LBUTTONUP: %d, %d' % (x, y)) # 좌표 출력
elif event == cv2.EVENT_MOUSEMOVE:
# 이벤트 : 마우스가 움직일 때 : 엄청나게 많이 발생함.
if flags & cv2.EVENT_FLAG_LBUTTON:
# 엄청나게 많이 발생하는 이벤트를 flags 인자를 통해(특정 키보드 키 등) 제어. 키보드 + 마우스 동시에
# flags == cv2.EVENT_FLAG_LBUTTON 으로 하면 안됨. 그럼 특정 키보드를 눌렀을 때 동작이 안됨.
# & 연산자를 쓰면 위의 LBUTTON 이벤트가 1이기 때문에 flags 의 마지막 비트가 1로 세팅이 되어 있는 가 를 보는 형태로 작성하는 것이 좋음.
'''
cv2.circle(img, (x,y), 5, (0,0,255), -1)
cv2.imshow('image', img)
'''
# 이전 좌표와 현재 좌표(x,y) 로 그리기.
cv2.line(img, (oldx, oldy), (x, y), (0, 0, 255), 4, cv2.LINE_AA) # 선 그리기. 그림 그리기
cv2.imshow('image', img)
oldx, oldy = x, y
# line 말고도 circle 함수로 간단하게 그리는 방법도 있음 : 점으로 그리는 느낌. (강의 참고)
# 단, Circle 은 마우스를 빠르게 움직이면 점 형태로 끊김.
# 흰색 창 띄우기 : 컬러형태의 : RGB 값이 모두 255 이므로 흰색
img = np.ones((480, 640, 3), dtype=np.uint8) * 255
cv2.namedWindow('image')
# setMouseCallback 함수를 호출하는 위치가 중요함.
# 이 함수는 windowName 이라는 창이 이미 떠있는 상태에서 호출해야 하기 때문에, namedWindow 함수가 실행되어서 창이 떠 있는 상태
# 또는 cv2.imshow 함수가 호출이 된 이후에 호출되어야 함.
cv2.setMouseCallback('image', on_mouse, img) # on_mouse 라는 함수 생성
cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()

→ pos 인자는 트랙바의 버튼이 위치한 정수값을 인자로 전달onChange(pos) → None
import numpy as np
import cv2
def on_level_change(pos): # 트랙바 이벤트 콜백 함수
value = pos * 16
# 이런 식으로 설정하면 pos 값이 0일 때는 0, 1일 때는 16. 즉, 16 간격으로 형성됨.
if value >= 255: # 마지막 pos 값이 256 이 되면, 0으로 바뀌기 때문에 이렇게 if 문 생성
value = 255
# 위의 if 의 기능을 numpy 에서 제공하는 함수(아래 코드)를 활용해도 됨.
# value = np.clip(level, 0, 255) # 최소, 최대 값을 설정하는 함수
img[:] = value
cv2.imshow('image', img)
# createTrackbar 함수는 namedWindow, imshow 함수 위에다가 생성하면 안됨.
# : 창이 생성된 이후에 사용해야 함.
img = np.zeros((480, 640), np.uint8) # grayscale 형식의 영상
cv2.namedWindow('image')
# 트랙바가 가리키고 있는 위치에 해당하는 grayscale 값을 화면에 보여주는 작업
# graysclae 값을 0 ~ 255 로 트랙바에 나타내면 트랙바는 작은데 값이 커지기 때문에
# 그 값을 16 간격으로 해서 나타낼 것 : 16 간격으로 grayscale 값이 증가하게끔.
cv2.createTrackbar('level', 'image', 0, 16, on_level_change)
# level : 트랙바 이름, image : 트랙바를 생성할 창 이름,
# 0 : 트랙바 위치 초기값, 16 : 트랙바 최댓값
# on_level_change : 트랙바 위치가 변경될 때마다 호출할 콜백 함수 이름.
cv2.imshow('image', img)
cv2.waitKey()
cv2.destroyAllWindows()

import sys
import time
import numpy as np
import cv2
img = cv2.imread('hongkong.jpg') # 영상 열기
if img is None:
print('Imange load failed')
sys.exit()
tm = cv2.TickMeter() # 객체 생성
tm.reset()
tm.start()
t1 = time.time() # 이런 방법으로 측정 가능
edge = cv2.Canny(img, 50, 150) # 시간을 재고 싶은 함수 위 아래로 start, stop 함수를 호출
# 이 연산을 한번만 측정하지 말고, 20번 100번 정도 시행해서 평균적인 속도를 측정하는 것이 좋음.
tm.stop()
ms = tm.getTimeMilli() # 측정한 시간 변수에 저장.
print('time:', (time.time() - t1) * 1000)
print('Elapsed time: {}ms.'.format(tm.getTimeMilli()))
import sys
import numpy as np
import cv2
# 두 개의 동영상을 열어서 cap1, cap2로 지정
cap1 = cv2.VideoCapture('video1.mp4')
cap2 = cv2.VideoCapture('video2.mp4')
# 예외 처리
if not cap1.isOpened() or not cap2.isOpened():
print('video open failed!')
sys.exit()
# 두 동영상의 크기, FPS는 같다고 가정함
# 각각 동영상의 전체 프레임 수를 변수로 저장해놓기.
frame_cnt1 = round(cap1.get(cv2.CAP_PROP_FRAME_COUNT))
frame_cnt2 = round(cap2.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap1.get(cv2.CAP_PROP_FPS)
effect_frames = int(fps * 2)
# 첫 번째 동영상의 끝부분 2초, 두 번째 동영상의 앞 부분 2초가 겹쳐져서 합성이 되게끔 작업하기 위해
'''
실제 구현은 앞 부분 동영상에서 뒷 부분에 있는 48 프레임과 뒤 부분 동영상에서 앞 부분에 있는 48 프레임을
합성하는 코드만 작성하면 됨.
'''
print('frame_cnt1:', frame_cnt1)
print('frame_cnt2:', frame_cnt2)
print('FPS:', fps)
delay = int(1000 / fps)
w = round(cap1.get(cv2.CAP_PROP_FRAME_WIDTH))
h = round(cap1.get(cv2.CAP_PROP_FRAME_HEIGHT))
fourcc = cv2.VideoWriter_fourcc(*'DIVX')
# 출력 동영상 객체 생성
out = cv2.VideoWriter('output.avi', fourcc, fps, (w, h))
# 기본적으로 동영상 2개 받아서 이어서 붙이는 코드 먼저 작성.
# 1번 동영상 복사
for i in range(frame_cnt1 - effect_frames): # 프레임 수 변수를 이용해서 for 문으로
# 뒤에 48 프레임 정도 남겨두고 앞 부분은 그대로 output 동영상에 저장이 됨.
ret1, frame1 = cap1.read()
# 예외 처리
if not ret1:
print('frame read error!')
sys.exit()
out.write(frame1)
print('.', end='')
cv2.imshow('output', frame1)
cv2.waitKey(delay)
# 1번 동영상 뒷부분과 2번 동영상 앞부분을 합성
for i in range(effect_frames): # 여기서는 합성하는 구간인데,
# 이 합성하는 구간에서는 1번 동영상 프레임도 가져오고, 2번 동영상 프레임도 가져오기 때문에
# 아래 코드 두 개로 작성해야 함.
ret1, frame1 = cap1.read()
ret2, frame2 = cap2.read()
if not ret1 or not ret2:
print('frame read error!')
sys.exit()
# 합성하는 과정
# 합성은 전체 프레임에서 앞부분의 일부분을 자르고, 그리고 뒷 부분의 일부분을 잘라서 이어 붙이는데,
# 그럼 i 가 0 ~ effect_frames 형태로 증가하기 때문에 이것을 이용해서
# dx 라고 하는 어느 위치까지 잘라낼 것인지, 잘라내기 위한 그 위치를 dx 라고 정의를 하고,
# 전체 크기를 w 라고 정의 해놓고, (위에서) w 를 effect_Frames 로 나누면 가로가 약 27개 픽셀 단위로 해서 잘리는데
# 그리고 여기에다가 곱하기 i 를 하면 그 단위로 증가하게끔.
dx = int(w / effect_frames) * i
# 그리고 프레임을 하나 만드는데, frame 은 np.zeros 를 이용해서 컬러 영상을 만들고
frame = np.zeros((h, w, 3), dtype=np.uint8)
#이 프레임 영상에서 세로 부분은 전체, 가로는 0~dx 까지를 frame2 의 0 ~ dx 까지를 가져오고,
#그리고 frame 영상에서 y 좌표는 전체, dx ~ w 까지를 frame 1 에서 전체에서 dx ~ w 까지를 받아 옴.
frame[:, 0:dx, :] = frame2[:, 0:dx, :]
frame[:, dx:w, :] = frame1[:, dx:w, :]
# 위의 두 코드로 frame 영상이 구성한 것.
# 만약 합성을 디졸브 형태로 하고 싶다면,
# 두 개의 영상에 대해서 가중치 합을 계산해야 함.
'''
가중치 합을 계산하는데, 처음에는 첫 번째 동영상에 대한 가중치를 높이다가,
점점 시간이 지나면서 두 번째 동영상에서 가져온 프레임에 대한 가중치를 높이는 형태로 코드를 작성하면 됨.
'''
# alpha + (1-alpha) = 1 이 되어야 함.
#alpha = 1.0 - (i / effect_frames) : i 가 0일 때 alpha 는 0이 되어야 함. 그래서 이 코드로.
#frame = cv2.addWeighted(frame1, 1 - alpha, frame2, alpha, 0)
'''
frame1 에서 1-alpha 만큼의 비중을 가져오고, frame2 에서 alpha 만큼의 비중을 가져오고,
감마라는 인자는 추가적으로 더해줄 필요가 없으므로 0으로 놓으면
이 결과를 가져다가 frame 에다가 그대로 대입을 하면 디졸브 합성이 됨.
'''
out.write(frame)
print('.', end='')
cv2.imshow('output', frame)
cv2.waitKey(delay) # 영상 전환을 조금 더 빠르게 하고 싶다면 delay 값을 낮추면 됨/
# 2번 동영상을 복사 : 두번째 동영상 뒷부분을
for i in range(effect_frames, frame_cnt2):
# effect_frames ~ frame_cnt2 (마지막 프레임) 까지
ret2, frame2 = cap2.read()
if not ret2:
print('frame read error!')
sys.exit()
out.write(frame2)
print('.', end='')
cv2.imshow('output', frame2)
cv2.waitKey(delay)
print('\noutput.avi file is successfully generated!')
cap1.release()
cap2.release()
out.release()
cv2.destroyAllWindows()
# 이 프로그램에서 가장 주의 깊게 봐야 하는 부분은 가운데 합성 부분임.
# 중간에 2초 가량만 합성하면 되므로, 계산 부분만 잘 하면 됨.
출처 : 패스트캠퍼스 <OpenCV를 활용한 컴퓨터비전과 딥러닝>