* 교재 : 파이썬을 이용한 머신러닝, 딥러닝 실전 앱 개발
https://github.com/wikibook/python-ml-app-dev/tree/master/ch3/video
* 해당 코드들을 파이썬 파일로 저장 (example.py) 후
아나콘다 프롬프트 창에서 실행
python example.py

1. 웹캠 이미지 실시간 출력
* 사용 함수
- VideiCapture(0) : 표준 웹 카메라 사용
- read() : 이미지 읽어 들이기
- resize() : 이미지 사이즈 조절
- imshow() : 윈도우에 이미지 출력
- waitKey() : 해당 시점에 눌린 키 추출, esc/enter키 일 경우 반복문 종료
import cv2
import numpy as np
# 웹 카메라로부터 입력받기 --- (*1)
cap = cv2.VideoCapture(0)
while True:
# 카메라의 이미지 읽어 들이기 --- (*2)
_, frame = cap.read()
# 이미지를 축소해서 출력하기 --- (*3)
frame = cv2.resize(frame, (500,300))
# 윈도우에 이미지 출력하기 --- (*4)
cv2.imshow('OpenCV Web Camera', frame)
# ESC 또는 Enter 키가 입력되면 반복 종료하기
k = cv2.waitKey(1) # 1msec 대기
if k == 27 or k == 13: break
cap.release() # 카메라 해제하기
cv2.destroyAllWindows() # 윈도우 제거하기
2. 카메라 이미지에서 붉은색 성분만 추출하기
웹 카메라를 기반으로 추출한 이미지 : NumPy 형식의 배열 (ndarray)
RGB의 색공간을 사용해 붉은색만 출력
이미지 데이터에서 파란색과 녹색 성분을 제거해보자.
*1 - NumPy의 인덱스 기능 : 모든 화소에서 파란색과 녹색 성분을 0으로 설정
import cv2
import numpy as np
# 웹 카메라로부터 입력받기
cap = cv2.VideoCapture(0)
while True:
# 이미지 추출하기
_, frame = cap.read()
# 이미지 축소하기
frame = cv2.resize(frame, (500,300))
# 파란색과 녹색 부분 제거하기---(*1)
frame[:, :, 0] = 0 # 파란색을 0으로
frame[:, :, 1] = 0 # 초록색을 0으로
# 윈도우에 이미지 출력하기 --- (*2)
cv2.imshow('RED Camera', frame)
# Enter 키가 입력되면 반복 종료하기
if cv2.waitKey(1) == 13: break
cap.release() # 카메라 해제하기
cv2.destroyAllWindows() # 윈도우 제거하기
실행 화면

HSV 색공간을 사용해 붉은색만 검출
HSV 색공간 : 색 (Hue), 채도(Saturation), 명도(Value Brightness)
RGB와 달리 HSV는 채도와 명도를 이용해 색을 조정하기 때문에 색의 변화를 쉽게 예측할 수 있다.

색상은 360도 원형으로 표현하며, 오른쪽으로 돌아가며 붉은색 -> 녹색 -> 파란색 -> 붉은색으로 색을 표현한다.
* 색공간을 HSV로 변환하고 H, S, V의 각 요소를 분할한다. 그리고 붉은색 부분을 흰색(255)로 칠해 이미지를 화면에 출력한다.
import cv2
import numpy as np
# 웹 카메라로부터 입력받기
cap = cv2.VideoCapture(0)
while True:
# 이미지 추출하고 축소하기
_, frame = cap.read()
frame = cv2.resize(frame, (500,300))
# 색공간을 HSV로 변환하기 --- (*1)
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV_FULL)
# HSV 분할하기 --- (*2)
h = hsv[:, :, 0]
s = hsv[:, :, 1]
v = hsv[:, :, 2]
# 붉은색을 가진 요소만 출력하기 --- (*3)
img = np.zeros(h.shape, dtype=np.uint8)
img[((h < 50) | (h > 200)) & (s > 100)] = 255
# 윈도우에 이미지 출력하기 --- (*4)
cv2.imshow('RED Camera', img)
if cv2.waitKey(1) == 13: break
cap.release() # 카메라 해제하기
cv2.destroyAllWindows() # 윈도우 제거하기
실행화면

3. 화면에 움직임이 있는 부분 추출하기
동영상은 여러 이미지의 연속이므로 각 프레임의 차이를 확인하면 화면에 일어나는 변화를 검출할 수 있다.
*사용 함수
- cv2.absdiff() : 이미지의 차이를 확인
import cv2
cap = cv2.VideoCapture(0)
img_last = None # 이전 프레임을 저장해둘 변수 --- (*1)
green = (0, 255, 0)
while True:
# 이미지 추출하기
_, frame = cap.read()
frame = cv2.resize(frame, (500, 300))
# 흑백 이미지로 변환하기 --- (*2)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (9, 9), 0)
img_b = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY)[1]
# 차이 확인하기
if img_last is None:
img_last = img_b
continue
frame_diff = cv2.absdiff(img_last, img_b) # --- (*3)
cnts = cv2.findContours(frame_diff,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
# 차이가 있는 부분 출력하기 --- (*4)
for pt in cnts:
x, y, w, h = cv2.boundingRect(pt)
if w < 30: continue # 작은 변경은 무시하기
cv2.rectangle(frame, (x, y), (x+w, y+h), green, 2)
# 프레임을 변수에 저장해두기 --- (*5)
img_last = img_b
# 화면에 출력하기
cv2.imshow("Diff Camera", frame)
cv2.imshow("diff data", frame_diff)
if cv2.waitKey(1) == 13: break
cap.release()
cv2.destroyAllWindows()
Error

해결!
cv2.CHAIN_APPROX_SIMPLE)[1] -> cv2.CHAIN_APPROX_SIMPLE)[0]
으로 변경해주면 된다.
코드가 OpenCV 3.x 버전으로 만들어졌는데 사용하고 있는 opencv는 4.x 버전이라 생기는 오류라고 한다.

4. 이미지 파일 쓰기
입력받은 이미지를 연속으로 써서 동영상 파일을 만들어보자.
웹카메라로부터 입력받은 영상을 기록하는 프로그램이다.
*사용 함수
- cv2.VideoWriter : 동영상 쓰기 전용 객체 생성
1) 첫 번째 매개변수 : 파일 이름
2) 두 번째 매개변수 : fmt - 동영상 형식 지정 (mp4v)
3) 세 번째 매개변수 : FPS (1초 동안의 프레임 수)
4) 네 번째 매개변수 : 동영상 화면 크기 지정
- write() : 반복 사용으로 이미지를 동영상에 쓰기
import cv2
import numpy as np
# 카메라 입력받기
cap = cv2.VideoCapture(0)
# 동영상 출력 전용 객체 생성하기
fmt = cv2.VideoWriter_fourcc('m','p','4','v')
fps = 20.0
size = (640, 360)
writer = cv2.VideoWriter('test.m4v', fmt, fps, size) # --- (*1)
while True:
_, frame = cap.read() # 동영상 입력
# 이미지 축소하기
frame = cv2.resize(frame, size)
# 이미지 출력하기 --- (*2)
writer.write(frame)
# 화면에도 출력하기
cv2.imshow('frame', frame)
# Enter 키가 입력되면 반복 종료하기
if cv2.waitKey(1) == 13: break
writer.release()
cap.release()
cv2.destroyAllWindows() # 윈도우 제거하기
실행 결과

5. 동영상에서 열대어가 나오는 부분 검출하기
이제 연습한 걸 토대로 바다 속을 촬영한 영상에서 열대어가 나오는 부분을 추출하는 프로그램을 만들어보자.
fish.mp4 파일을 사용한다.
*사용 함수 및 프로그램 진행과정
- cv2.absdiff() : 이전 프레임과의 차이 확인 -> 열대어일 가능성이 있는 부분을 추출 가능
- 동영상의 각 프레임을 확인하고 움직임이 있는 부분을 exfish라는 디렉터리에 추출해 JPEG 형식으로 저장한다
import cv2, os
img_last = None # 이전 프레임을 저장할 변수
no = 0 # 이미지 장 수
save_dir = "./exfish" # 저장 디렉터리 이름
os.mkdir(save_dir) # 디렉터리 만들기
# 동영상 파일로부터 입력받기 --- (*1)
cap = cv2.VideoCapture("fish.mp4")
while True:
# 이미지 추출하기
is_ok, frame = cap.read()
if not is_ok: break
frame = cv2.resize(frame, (640, 360))
# 흑백 이미지로 변환하기 --- (*2)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (15, 15), 0)
img_b = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
# 차이 확인하기
if not img_last is None:
frame_diff = cv2.absdiff(img_last, img_b) # --- (*3)
cnts = cv2.findContours(frame_diff,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
# 차이가 있는 부분을 파일로 출력하기 --- (*4)
for pt in cnts:
x, y, w, h = cv2.boundingRect(pt)
if w < 100 or w > 500: continue # 노이즈 제거하기
# 추출한 영역 저장하기
imgex = frame[y:y+h, x:x+w]
outfile = save_dir + "/" + str(no) + ".jpg"
cv2.imwrite(outfile, imgex)
no += 1
img_last = img_b
cap.release()
print("ok")
3번과 동일한 에러.
cv2.error: OpenCV(4.5.3) C:\Users\runneradmin\AppData\Local\Temp\pip-req-build-q3d_8t8e\opencv\modules\imgproc\src\shapedescr.cpp:874: error: (-215:Assertion failed) npoints >= 0 && (depth == CV_32F || depth == CV_32S) in function 'cv::pointSetBoundingRect' |
cv2.CHAIN_APPROX_SIMPLE)[1] -> cv2.CHAIN_APPROX_SIMPLE)[0]
실행 결과

6. 머신러닝으로 동영상에서 열대어가 많이 나오는 부분 찾기
(1) 열대어 학습시키기
열대어 이미지 100장 : fish 디렉토리
열대어가 나오지 않는 이미지 150장 : nofish 디렉토리
* Jupyter Notebook 실행 디렉토리 내부에 저장

* 사용함수 및 진행 과정
- 사용자 지정 함수 : read_dir()
(1) 이미지 데이터를 읽어 들이고 배열에 추가 -> 지정한 디렉토리에 있는 JPEG 이미지를 데이터에 추가
(2) 크기 변환 후 리스트 자료형의 변수 X와 Y에 추가 (머신러닝을 할 때는 학습데이터가 같은 크기여야함)
- 데이터를 셔플해 학습 전용 데이터와 테스트 전용 데이터로 나눔
- 데이터 학습 : 랜덤 포레스트 알고리즘 사용
- 학습 데이터의 정답률을 확인 및 출력
- fish.pkl 이름으로 학습 데이터 저장
import cv2
import os, glob
from sklearn.model_selection import train_test_split
from sklearn import datasets, metrics
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import accuracy_score
from sklearn.externals import joblib
# 이미지 학습 크기와 경로 지정하기
image_size = (64, 32)
path = os.path.dirname(os.path.abspath(__file__))
path_fish = path + '/fish'
path_nofish = path + '/nofish'
x = [] # 이미지 데이터
y = [] # 레이블 데이터
# 이미지 데이터를 읽어 들이고 배열에 넣기 --- (*1)
def read_dir(path, label):
files = glob.glob(path + "/*.jpg")
for f in files:
img = cv2.imread(f)
img = cv2.resize(img, image_size)
img_data = img.reshape(-1, ) # 1차원으로 전개하기
x.append(img_data)
y.append(label)
# 이미지 데이터 읽어 들이기
read_dir(path_nofish, 0)
read_dir(path_fish, 1)
# 데이터를 학습 전용과 테스트 전용으로 분리하기 --- (*2)
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2)
# 데이터 학습하기 --- (*3)
clf = RandomForestClassifier()
clf.fit(x_train, y_train)
# 정답률 확인하기 --- (*4)
y_pred = clf.predict(x_test)
print(accuracy_score(y_test, y_pred))
# 데이터 저장하기 --- (*5)
joblib.dump(clf, 'fish.pkl')
ERROR
Traceback (most recent call last): File "example.py", line 7, in <module> from sklearn.externals import joblib ImportError: cannot import name 'joblib' from 'sklearn.externals' (C:\Users\82104\anaconda3\lib\site-packages\sklearn\externals\__init__.py) |
해결!
from sklearn.externals import joblib -> import joblib로 변경
학습된 데이터는 "fish.pkl"이름으로 저장

0.86의 정답률

(2) 동영상 분석하기
이제 실제 동영상 파일에서 열대어가 많이 나오는 부분을 추출하는 프로그램을 만들어보자.
프로그램이 물고기라고 판정하는 영역에 테두리를 출력한다.
* 사용 함수 및 진행 과정
- 동영상 파일을 읽어들인다
- 이전 프레임과 현재 프레임을 비교하는 처리를 수행한다
- 차이가 있는 부분을 추출하고 각 영역에 열대어가 있는지 머신러닝으로 판정한다
- 있다고 판정되면 해당 영역에 녹색 테두리를 렌더링한다
- 이미지 데이터를 전달해 열대어가 있는지 판정한다
- 열대어가 3마리 이상 있다고 판정되는 부분을 bestshot이라는 디렉토리에 저장한다.
import cv2, os, copy
from sklearn.externals import joblib
# 학습한 데이터 읽어 들이기
clf = joblib.load("fish.pkl")
output_dir = "./bestshot"
img_last = None # 이전 프레임을 저장할 변수
fish_th = 3 # 이미지로 출력할 기준이 되는 물고기 수
count = 0
frame_count = 0
if not os.path.isdir(output_dir): os.mkdir(output_dir)
# 동영상 파일로부터 입력받기 --- (*1)
cap = cv2.VideoCapture("fish.mp4")
while True:
# 이미지 추출하기
is_ok, frame = cap.read()
if not is_ok: break
frame = cv2.resize(frame, (640, 360))
frame2 = copy.copy(frame)
frame_count += 1
# 이전 프레임과 비교를 위해 흑백으로 변환하기 --- (*2)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (15, 15), 0)
img_b = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1]
if not img_last is None:
# 차이 추출하기
frame_diff = cv2.absdiff(img_last, img_b)
cnts = cv2.findContours(frame_diff,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)[1]
# 차이가 있는 부분에 물고기가 있는지 확인하기
fish_count = 0
for pt in cnts:
x, y, w, h = cv2.boundingRect(pt)
if w < 100 or w > 500: continue # 노이즈 제거하기
# 추출한 영역에 물고기가 있는지 확인하기 --- (*3)
imgex = frame[y:y+h, x:x+w]
imagex = cv2.resize(imgex, (64, 32))
image_data = imagex.reshape(-1, )
pred_y = clf.predict([image_data]) # --- (*4)
if pred_y[0] == 1:
fish_count += 1
cv2.rectangle(frame2, (x, y), (x+w, y+h), (0,255,0), 2)
# 물고기가 많이 있는지 확인하기 --- (*5)
if fish_count > fish_th:
fname = output_dir + "/fish" + str(count) + ".jpg"
cv2.imwrite(fname, frame)
count += 1
cv2.imshow('FISH!', frame2)
if cv2.waitKey(1) == 13: break
img_last = img_b
cap.release()
cv2.destroyAllWindows()
print("ok", count, "/", frame_count)
3,6번 에러 발생. 각각 고쳐주면 된다

실행 결과

'머신러닝' 카테고리의 다른 글
[사이킷런] 사이킷런 소개와 붓꽃 품종 예측하기 (0) | 2021.06.02 |
---|---|
[Pandas] apply lambda 식으로 데이터 가공 (0) | 2021.06.01 |
[Pandas] 정렬, Aggregation, GroupBy, 결손 데이터 (0) | 2021.05.26 |
[OpenPose/텐서플로우] 윈도우에서 OpenPose 환경 구성 :: 근데 아나콘다를 곁들인 (0) | 2021.05.19 |
[Pandas] Index 객체와 데이터 셀렉션 및 필터링 (0) | 2021.05.19 |