본문 바로가기

ICT 학습

이미지 처리로 시작하는 딥러닝 - 2주차

[ 오늘의 학습 목표 ]

- 이미지 처리하는 방법을 맛보자.

내 휴대폰의 다양한 이미지를 유명화가의 화풍을 따라하는 인공지능 만들기

딥러닝 모델을 이용하는 방법을 이해하자.

 

[ 꿀팁 - 인공지능 vs. 머신러닝 vs. 딥러닝 ]

- 인공지능 이란?

spartacodingclub 참조

- 인공지능(Artificial Intelligence, AI) 이란?

기계가 인간과 같이 생각하고 행동하는 것, (알파고, 로봇)과 같은 가장 넓은 범위의 범주

 

- 머신러닝(Machine Learning, ML) 이란?

인간이 생각하는 것이나 행동하는 것의 규칙을 컴퓨터가 찾아내는 일 (알파고가 학습을 통해 이길 수 있는 규칙을 찾는 일 등)

 

- 딥러닝(Deep Learning, DL) 이란?

머신러닝과 비슷하지만 머신러닝의 한 분야로 규칙을 찾아내기 위한 알고리즘 (알파고가 학습하기 위해 설계된 방법)

 

 

 

- 머신러닝 이란?

조직의 의사결정을 내리는데 있어서 인간이 아닌 컴퓨터가 규칙을 발견하여 도움을 줄 수 있지 않을까?라고 생각해서 만들어진 개념.

나이브 베이즈(확률적 모델링), 랜덤포레스트, 서포트벡터머신

고전적으로 확률적 모델링(naive Bayes classifier), 랜덤 포레스트(random forest), 서포트 벡터 머신(support vector machine, SVM) 등의 기법을 이용합니다.

 

    - 나이브 베이즈 : 조건부 확률 ( 확률적 모델링), 스팸메일 분류에 자주쓰임

    - 랜덤포레스트 : 여러가지 나무가 숲을 이루는 모양, 설문조사

    - 서포트 벡터 머신 : 가장 최신의 모델이지만 뉴럴 네트워크에 비해 한계점이 있음

 

 

- 딥러닝 이란?

인간의 뇌와 같이 기계를 만들면 인간과 같이 행동하지 않을까? 라고 생각해서 만든 것이 뉴럴 네트워크임.

spartacodingclub 참조

신경 단위인 뉴런으로 이루어져 있고 처음과 끝으로 이어진 구조에서 신경물질(중간 처리 데이터)이 전달되고 뉴런과 뉴런은 서로 이어져 있습니다. 뇌의 뉴런과 같이 모든 네트워크가 서로 이어져 있고 이 구조를 뉴럴 네트워크 ( 딥러닝 ) 라고 함.

 

딥러닝의 발전은 H/W의 발달과 알고리즘의 향상, 데이터셋과 공유 네트워크의 발달

 

딥러닝 분야에는 크게 두가지로 나눌 수 있음.

 

자연어처리(Natural Language Processing)
    한국어의 자연어 처리는 굉장히 어려워요.. 의미를 가지는 최소단위인 형태소 분리가 다른 언어들 보다 힘들기 때문
    응용분야 : 음성인식 AI ( 카카오미니, 기가지니 ..), 자동번역, 최근 유행하는 GPT3 등이 있음.

    GPT3에 대해서는 5주차 꿀팁 강의에서 추가 확인 예정.
    많은 곳에서 형태소 분리를 하여 연구를 하고 있고 공유를 하고 있어 자연어 처리도 나날히 성장하고 있음.

이미지처리(Image Processing)
    이미지 처리는 자율주행, 스노우카메라, 출입자 감시, 차량번호 조회 등등 활용 분야가 많음.

 

 

 

- Python 언어를 사용하는 이유?

직관적이고 간결

# Java Code
public class Sparta {
	public static void main(String[] args) {
		System.out.println("Hello, Sparta");
	}
}

# Python Code
print("Hello,sparta")

 

많은 내장/외장 라이브러리(패키지) 제공

머신러닝/딥러닝 라이브러리에는 Tensorflow, Keras, Pytorch 등이 있으며, 우리는 이 중 Tensorflow를 이용해 모델을 적용해볼 것입니다.

 

Numpy : 파이썬에는 배열을 담을 수 있는 라이브러리를 따로 제공하지 않습니다. Numpy는 배열을 통해 계산, 연산 등을 도와주며 필수 라이브러리입니다. (참고로, 사전과제에 사용한 아나콘다를 만든 사람이 Numpy를 만들었습니다)

시각화도구 : Matplotlib , 데이터처리 : Pandas 등이 있음. 그 외에도 많음.

 

 

다양한 머신러닝(딥러닝) 예제

초보자 입문부터 논문을 이용한 실습까지 tensorflow에서 제공을 하고 있음.

https://www.tensorflow.org/?hl=ko

 

 

 

[ 2주차 실습 환경 준비하기 ]

- VSCode에서 File > Open folder > week2(나는 D:\SPARTA_WEEK2 폴더를 사전에 만들어서 선택)

 

- main.py 파일 만들기

 

- 가상환경 설정하기

    1주차때 만들어 놓은 python 가상환경 'sparta_project':conda를 선택해서 python 가상 개발 환경을 선택한다.

 

- workspace 폴더(디렉토리)에 models와 imgs 폴더(디렉토리)를 만든다.

 

작업 환경을 생성 후 테스트 코드를 실행까지 완료한 화면 캡쳐

 

 

 

 

[ 딥러닝 모델과 이미지 다운로드(개인이 만들고자하는 이미지로 대신 사용해도됨. ]

- 커피머신(딥러닝 모델) 준비하기

eccv16 폴더에 아래 파일 다운로드해서 모델 정보 가져오기

https://drive.google.com/file/d/14MPjd8hj7pHV_LlHNoG4eQcwYrVwjJW9/view?usp=sharing

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

eccv16.zip

 

drive.google.com

 

instance_norm폴더에 아래 파일 다운로드해서 모델 정보 가져오기

https://drive.google.com/file/d/1PtXIGh_BJhMjiweLlnWZho0I6pZ_BjEO/view?usp=sharing 

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

instance_norm.zip

 

drive.google.com

2개의 샘플 폴더의 파일들은 유명화가의 그림제목과 같으며, 각 모델을 사용하면 유명화가의 화풍을 따라하는 인공지능을 만들수 있음.

 

 

- 커피머신(딥러닝 모델)에 넣을 원두(이미지) 준비하기

모델링 테스트를 위한 샘플 이미지를 작업공간의 imgs 폴더에 복사

https://drive.google.com/file/d/1lLetxZEI7utgqEjYKkSNgMOJ-8M8ctsP/view?usp=sharing 

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

01.jpg

 

drive.google.com

추가적으로 개인 사진 등을 모델링 샘플로 테스트를 해 보고 싶으면, 작업폴더\imgs 폴더내에 넣어 놓고 테스트 하면 됨.

 

 

 

[ 이미지 출력 ]

- 패키지 불러오기

main.py 파일에 학습에 필요한 주요 패키지(라이브러리)를 불러온다.

import cv2
import numpy as np

 

- 다운로드 받은 딥러닝 모델 로드하기

고흐의 "별이 빛나는 밤" 화풍을 따라하느 모델을 로드

net = cv2.dnn.readNetFromTorch('models/eccv16/starry_night.t7')

고흐의 별이 빛나는 밤

 

 

- 이미지 출력하기(모델 적용 전)

이미지를 불러와서 윈도우로 띄우기

img = cv2.imread('imgs/01.jpg')

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

이미지 불러오기..

 

 

[ 전처리(Preprocessing) ]

- 이미지 전처리하기 ( Image Preprocessing )

딥러닝에서의 전처리는 모델의 성능을 높이기 위해서 수행하는 일련의 모든 처리 단계 및 작업들을 통칭하여 부름.

이미지 전처리란 특정 이미지를 모델에 적용했을때, 원하는 형태의 이미지 정보를 확보하기 위한 사전의 모든 작업들이며,

우리가 현재 이 과정에서 하고자 하는 부분은 특정 이미지가 얼마나 화풍에 맞도록 이미지 변환이 잘 따라 변하는지를 평가되는 성능 지표가 됨.

 

전처리를 수행하면 일반적인 이미지를 볼 수 있는 코드 구조가 아닌 computer가 이미지 처리를 최적화 할 수 있는 구조로 데이터 형태가 변형되기 때문에 해당 전처리된 이미지 데이터를 다시 일반적인 이미지로 확인하기 위해서는 후처리 과정(Postprocessing)을 통해서 확인 가능함.

 

- 전처리 코드

# 이미지의 shape 정보를 읽어들임. 높이, 넓이, color 채널값
h, w, c = img.shape 

# 이미지의 크기를 모델 학습에 적합하도록(빠른 속도, 다만 원래의 데이터로 테스트가 꼭 필요한 경우에는 그대로 실행)
img = cv2.resize(img, dsize=(500, int(h / w * 500))) 

# 연구원들이 실제 사용한 전처리 방법, 이미지의 각 픽셀에서 해당 값들을 빼서 성능을 높임
MEAN_VALUE = [103.939, 116.779, 123.680] 

# MEAN_VALUE 빼기 연산
# 딥러닝 모델에 넣기 위한 이미지 데이터 차원 변형
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

 

- 팁 : 이미지 비율 유지하면서 크기 변경하기

이미지의 비율을 유지하면서 크기 변경 예시.

 

 

- 팁 : 이미지의 차원 변형

딥러닝 모델에 넣기 위해선 이미지의 차원 변형이 필요, img와 blob의 shape을 출력해서 비교해 보면 아래와 같음.

print(img.shape) # (325, 500, 3) = (높이, 넓이, 채널값)

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

print(blob.shape) # (1, 3, 325, 500) = (배치 사이즈, 채널값, 높이, 넓이)

데이터의 형태만 변경된 것, 값이 변형되지는 않음.

 

[주요]

차원 변형 후 (1, 채널, 높이, 너비) 모양에서 1은 딥러닝에서 배치 사이즈를 의미.

딥러닝 모델을 학습시키는 과정에서 이미지를 한 장씩 학습시키지 않고 여러 이미지를 한꺼번에 학습시키죠.

만약 이미지를 32개씩 묶어서 학습시킨다면 배치 사이즈는 32가 됩니다. (32, 채널, 높이, 너비)

우리는 교육 실습에서는 1개의 테스트 이미지를 사용함으로 배치 사이즈가 1이 됩니다. 따라서 (1, 채널, 높이, 너비) 형태가 되는 것입니다.

 

 

 

[ 추론 결과 보기 ]

- 결과 추론하기 (Inference)

전처리 과정이 끝난 이미지를 실제 모델을 통해 추론하는 과정

# 전처리한 이미지(blob)을 모델(net)에 넣고 추론(forward) 합니다.
net.setInput(blob)

# 추론한 결과를 output에 저장
output = net.forward()

 

 

- 결과 후처리 하기 (Postprocessing)

전처리 과정이 끝난 이미지는 컴퓨터가 처리하기 쉬운 일련의 숫자의 배열로 바뀌게 되는데 해당 값들을 다시 이미지로 나타내기 위한 과정을 후처리하고 보면 된다.

# 변형된 차원을 다시 원래의 형태(높이, 너비, 채널)로 돌려 놓는다.
# squeeze()를 사용하여 추가했던 첫 번째 차원을 삭제 = (1, 채널, 높이, 너비)값에서 (1,) 부분을 없애서 (채널, 높이, 너비) 형태로 변형
# transpose()를 사용해서 (채널, 높이, 너비)값의 순서를 (높이, 너비, 채널) 값의 순서로 변형
output = output.squeeze().transpose((1, 2, 0))

# MEAN_VALUE 더하기 : 전처리시 각 픽셀마다 뺏었던 MEAN_VALUE 값을 후처리시에는 다시 더해줌.
output += MEAN_VALUE

# 범위 넘어가는 값 잘라내기 : 이미지 픽셀값은 0-255 사이여야 하는데, 계산 과정에서 간혹 픽셀 값이 0 미만이거나, 
#                            255를 초과하는 픽셀이 발생. 이런 값들을 np.clip()를 사용해서 제거.
output = np.clip(output, 0, 255)

# 자료형 바꾸기 : 이미지의 자료형을 일반 이미지에서 쓰이는 정수형(unsigned integer 8bit) 값으로 바꿔 줌.
output = output.astype('uint8')

 

 

- 결과 이미지 출력하기

모든 단계 처리가 끝난 이미지를 변환 전의 이미지와 비교하기 위해 함께 출력

cv2.imshow('img', img)
cv2.imshow('result', output)
cv2.waitKey(0)

모델 적용전의 이미지와 모델 적용후 이미지 비교

 

 

- 전체 완성 코드(주석 제거 버전)

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/eccv16/starry_night.t7')

img = cv2.imread('imgs/01.jpg')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

net.setInput(blob)
output = net.forward()

output = output.squeeze().transpose((1, 2, 0))

output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

cv2.imshow('img', img)
cv2.imshow('result', output)
cv2.waitKey(0)

 

 

 

 

[ 새로운 이미지로 다시 추론하기 ]

- 새로운 이미지 준비하기

https://drive.google.com/file/d/15wSx_NFyhoj9DC1o_wwtmQ05WfIs9Wfn/view?usp=sharing 

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

02.jpg

 

drive.google.com

테스트에 사용된 이미지

 

기존 모델을 새로운 이미지에 그대로 적용한 결과

여기서는 새로운 이미지에 특정 부분을 잘라내서 잘라낸 부분만 추론하여 그 결과값을 확인하도록 함.

 

 

 

- 이미지 불러와서 액자 부분만 잘라내기

# 새롭게 테스트 할 이미지를 로딩
img = cv2.imread('imgs/02.jpg')

# 불러온 이미지의 쉐입 정보를 높이, 넓이, 채널 값으로 저장
h, w, c = img.shape

# 불러온 이미지의 사이즈를 크기를 조정
img = cv2.resize(img, dsize=(500, int(h / w * 500)))

# 이미지를 clopping 
img = img[162:513, 185:428]

 

 

- 잘라낸 이미지만 추론하기

추론 코드는 기본적으로 동일, models/eccv16/la_muse.t7  모델을 사용해서 테스트.

net = cv2.dnn.readNetFromTorch('models/eccv16/la_muse.t7')

 

 

 

- 결과 확인

자르기 전 전체 이미지로 모델 적용해 보기
자른 부위만으로 모델 적용 결과

- 전체 코드 (주석 제거 버전)

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/eccv16/la_muse.t7')

img = cv2.imread('imgs/02.jpg')
h, w, c = img.shape
img = cv2.resize(img, dsize=(500, int(h / w * 500)))
img = img[162:513, 185:428]

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

net.setInput(blob)
output = net.forward()

output = output.squeeze().transpose((1, 2, 0))

output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

cv2.imshow('img', img)
cv2.imshow('result', output)
cv2.waitKey(0)

 

 

 

 

[ 반반 적용하기 ]

이미지를 좌,우 반반으로 다른 모델을 적용해 보기

 

- 새로운 이미지 준비하기

https://drive.google.com/file/d/1tKUF_rH8AO-G9hxr6RATRNv1nmqkZDzL/view?usp=sharing 

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

03.jpg

 

drive.google.com

 

 

 

 

- 기본 코드

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/mosaic.t7')

img = cv2.imread('imgs/03.jpg')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))

MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

net.setInput(blob)
output = net.forward()

output = output.squeeze().transpose((1, 2, 0))

output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

cv2.imshow('img', img)
cv2.imshow('result', output)
cv2.waitKey(0)

실행 결과

 

 

 

- 두번째 모델 로드하기

net2 = cv2.dnn.readNetFromTorch('models/instance_norm/the_scream.t7')

 

 

- 두번째 모델 추론(Inference) 하기

net2.setInput(blob)
output2 = net2.forward()

output2 = output2.squeeze().transpose((1, 2, 0))
output2 = output2 + MEAN_VALUE

output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')

 

 

 

- 두개의 결과를 절반으로 잘라 이어 붙이기

output3 = np.concatenate([output[:, :250], output2[:, 250:]], axis=1)

cv2.imshow('output3', output3)

둘가지 모델을 한개의 이미지에 함께 표현

 

 

 

 

 

 

 

[ 과제 수행하기 ]

- 액자 부분만 crop해서 추론하기

- 이미지 가로로 잘라 반반 적용하기

- 동영상에 적용해 보기

 

- 새로운 이미지 준비하기

https://drive.google.com/file/d/1MlF4n2zqsBovDigx3Xt7iOTCltfkC458/view?usp=sharing 

(원래 강의 자료 링크가 깨질 수 있어서 개인 구글 드라이브로 링크 걸어 놓음.)

 

hw.jpg

 

drive.google.com

 

 

 

- 액자 부분만 crop해서 추론하기

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch(
    'models/eccv16/la_muse.t7')  # Torch로 만들어진 모델을 읽어 들이는 코드

img = cv2.imread('imgs/hw.jpg')


h, w, c = img.shape

# img = cv2.resize(img, dsize=(500, int(h / w * 500)))  # 이미지의 비율을 유지하면서 크기 변경하는식

# 이미지를 clopping
cropped_img = img[144:367, 481:811]

MEAN_VALUE = [103.939, 116.779, 123.680]  # 연구원들이 사전에 연구를 통해 확인해 놓은 값.
# MEAN_VALUE를 이용해서 차원 변형을 수행
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)
#blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# [주요]
# 차원 변형 후 (1, 채널, 높이, 너비) 모양에서 1은 딥러닝에서 배치 사이즈를 의미.
# 딥러닝 모델을 학습시키는 과정에서 이미지를 한 장씩 학습시키지 않고 여러 이미지를 한꺼번에 학습시키죠.
# 만약 이미지를 32개씩 묶어서 학습시킨다면 배치 사이즈는 32가 됩니다. (32, 채널, 높이, 너비)
# 우리는 교육 실습에서는 1개의 테스트 이미지를 사용함으로 배치 사이즈가 1이 됩니다. 따라서 (1, 채널, 높이, 너비) 형태가 되는 것입니다.


# 차원 변형 전후의 img의 shape 값의 차이점 확인
# print(img.shape) # (325, 500, 3) = (높이, 넓이, 채널값)

# MEAN_VALUE = [103.939, 116.779, 123.680]
# blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# print(blob.shape) # (1, 3, 325, 500) = (배치 사이즈, 채널값, 높이, 넓이)


# 전처리한 이미지(blob)을 모델(net)에 넣고 추론(forward) 합니다.
net.setInput(blob)

# 추론한 결과를 output에 저장
output = net.forward()

# 변형된 차원을 다시 원래의 형태(높이, 너비, 채널)로 돌려 놓는다.
# squeeze()를 사용하여 추가했던 첫 번째 차원을 삭제 = (1, 채널, 높이, 너비)값에서 (1,) 부분을 없애서 (채널, 높이, 너비) 형태로 변형
# transpose()를 사용해서 (채널, 높이, 너비)값의 순서를 (높이, 너비, 채널) 값의 순서로 변형
output = output.squeeze().transpose((1, 2, 0))

# MEAN_VALUE 더하기 : 전처리시 각 픽셀마다 뺏었던 MEAN_VALUE 값을 후처리시에는 다시 더해줌.
output += MEAN_VALUE

# 범위 넘어가는 값 잘라내기 : 이미지 픽셀값은 0-255 사이여야 하는데, 계산 과정에서 간혹 픽셀 값이 0 미만이거나,
#                            255를 초과하는 픽셀이 발생. 이런 값들을 np.clip()를 사용해서 제거.
output = np.clip(output, 0, 255)

# 자료형 바꾸기 : 이미지의 자료형을 일반 이미지에서 쓰이는 정수형(unsigned integer 8bit) 값으로 바꿔 줌.
output = output.astype('uint8')


#cv2.imshow('img', img)
cv2.imshow('result', output)
cv2.waitKey(0)

액자 부분만 crop 처리 후 모델 적용 결과

 

 

 

- 액자 부분만 crop해서 추론하기 - 응용 #1

다양한 모델에 이미지 적용

import os
import sys
import cv2
import numpy as np

netfilelist1 = os.listdir('models/eccv16')
netfilelist2 = os.listdir('models/instance_norm')

netlist = []
for netfile in range(len(netfilelist1)):
    netlist.append(cv2.dnn.readNetFromTorch(
        'models/eccv16/' + str(netfilelist1[netfile])))

for netfile2 in range(len(netfilelist2)):
    netlist.append(cv2.dnn.readNetFromTorch(
        'models/instance_norm/' + str(netfilelist2[netfile2])))


img = cv2.imread('imgs/hw.jpg')


h, w, c = img.shape

# img = cv2.resize(img, dsize=(500, int(h / w * 500)))  # 이미지의 비율을 유지하면서 크기 변경하는식

# 이미지를 clopping
cropped_img = img[144:367, 481:811]

MEAN_VALUE = [103.939, 116.779, 123.680]  # 연구원들이 사전에 연구를 통해 확인해 놓은 값.
# MEAN_VALUE를 이용해서 차원 변형을 수행
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)
#blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# [주요]
# 차원 변형 후 (1, 채널, 높이, 너비) 모양에서 1은 딥러닝에서 배치 사이즈를 의미.
# 딥러닝 모델을 학습시키는 과정에서 이미지를 한 장씩 학습시키지 않고 여러 이미지를 한꺼번에 학습시키죠.
# 만약 이미지를 32개씩 묶어서 학습시킨다면 배치 사이즈는 32가 됩니다. (32, 채널, 높이, 너비)
# 우리는 교육 실습에서는 1개의 테스트 이미지를 사용함으로 배치 사이즈가 1이 됩니다. 따라서 (1, 채널, 높이, 너비) 형태가 되는 것입니다.


# 차원 변형 전후의 img의 shape 값의 차이점 확인
# print(img.shape) # (325, 500, 3) = (높이, 넓이, 채널값)

# MEAN_VALUE = [103.939, 116.779, 123.680]
# blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# print(blob.shape) # (1, 3, 325, 500) = (배치 사이즈, 채널값, 높이, 넓이)


# 전처리한 이미지(blob)을 모델(net)에 넣고 추론(forward) 합니다.
# net.setInput(blob) <=== for 문을 위해 임시 편집

# for netindex in range(len(netlist)): <=== for 문을 위해 임시 편집
#     netlist[netindex].setInput(blob) <=== for 문을 위해 임시 편집

# 추론한 결과를 output에 저장
# output = net.forward() <=== for 문을 위해 임시 편집
# output = netlist[0].forward()

output = []
for netindex in range(len(netlist)):
    netlist[netindex].setInput(blob)
    output.append(netlist[netindex].forward())
    output[netindex] = output[netindex].squeeze().transpose((1, 2, 0))
    output[netindex] += MEAN_VALUE
    output[netindex] = np.clip(output[netindex], 0, 255)
    output[netindex] = output[netindex].astype('uint8')
    cv2.imshow('result'+str(netindex), output[netindex])
    # cv2.waitKey(0)


# 변형된 차원을 다시 원래의 형태(높이, 너비, 채널)로 돌려 놓는다.
# squeeze()를 사용하여 추가했던 첫 번째 차원을 삭제 = (1, 채널, 높이, 너비)값에서 (1,) 부분을 없애서 (채널, 높이, 너비) 형태로 변형
# transpose()를 사용해서 (채널, 높이, 너비)값의 순서를 (높이, 너비, 채널) 값의 순서로 변형
# output = output.squeeze().transpose((1, 2, 0)) <=== for 문을 위해 임시 편집


# MEAN_VALUE 더하기 : 전처리시 각 픽셀마다 뺏었던 MEAN_VALUE 값을 후처리시에는 다시 더해줌.
# output += MEAN_VALUE <=== for 문을 위해 임시 편집

# 범위 넘어가는 값 잘라내기 : 이미지 픽셀값은 0-255 사이여야 하는데, 계산 과정에서 간혹 픽셀 값이 0 미만이거나,
#                            255를 초과하는 픽셀이 발생. 이런 값들을 np.clip()를 사용해서 제거.
# output = np.clip(output, 0, 255) <=== for 문을 위해 임시 편집

# 자료형 바꾸기 : 이미지의 자료형을 일반 이미지에서 쓰이는 정수형(unsigned integer 8bit) 값으로 바꿔 줌.
# output = output.astype('uint8') <=== for 문을 위해 임시 편집


#cv2.imshow('img', img)

# cv2.imshow('result', output)  <=== for 문을 위해 임시 편집
cv2.waitKey(0)

 

다양한 모델에 이미지 적용 결과

For 문을 이용해서 모델 불러오기 등의 작업을 일괄 처리한 결과

 

 

 

- 액자 부분만 crop해서 추론하기 - 응용 #1

바뀐 이미지를 다시 원본이미지의 액자 안에 집어넣기

import os
import sys
import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/candy.t7')

img = cv2.imread('imgs/hw.jpg')


h, w, c = img.shape

# img = cv2.resize(img, dsize=(500, int(h / w * 500)))  # 이미지의 비율을 유지하면서 크기 변경하는식

# 이미지를 clopping
cropped_img = img[144:367, 481:811]

MEAN_VALUE = [103.939, 116.779, 123.680]  # 연구원들이 사전에 연구를 통해 확인해 놓은 값.
# MEAN_VALUE를 이용해서 차원 변형을 수행
blob = cv2.dnn.blobFromImage(cropped_img, mean=MEAN_VALUE)
#blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# [주요]
# 차원 변형 후 (1, 채널, 높이, 너비) 모양에서 1은 딥러닝에서 배치 사이즈를 의미.
# 딥러닝 모델을 학습시키는 과정에서 이미지를 한 장씩 학습시키지 않고 여러 이미지를 한꺼번에 학습시키죠.
# 만약 이미지를 32개씩 묶어서 학습시킨다면 배치 사이즈는 32가 됩니다. (32, 채널, 높이, 너비)
# 우리는 교육 실습에서는 1개의 테스트 이미지를 사용함으로 배치 사이즈가 1이 됩니다. 따라서 (1, 채널, 높이, 너비) 형태가 되는 것입니다.


# 차원 변형 전후의 img의 shape 값의 차이점 확인
# print(img.shape) # (325, 500, 3) = (높이, 넓이, 채널값)

# MEAN_VALUE = [103.939, 116.779, 123.680]
# blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# print(blob.shape) # (1, 3, 325, 500) = (배치 사이즈, 채널값, 높이, 넓이)


# 전처리한 이미지(blob)을 모델(net)에 넣고 추론(forward) 합니다.
net.setInput(blob)


# 추론한 결과를 output에 저장
output = net.forward()


# 변형된 차원을 다시 원래의 형태(높이, 너비, 채널)로 돌려 놓는다.
# squeeze()를 사용하여 추가했던 첫 번째 차원을 삭제 = (1, 채널, 높이, 너비)값에서 (1,) 부분을 없애서 (채널, 높이, 너비) 형태로 변형
# transpose()를 사용해서 (채널, 높이, 너비)값의 순서를 (높이, 너비, 채널) 값의 순서로 변형
output = output.squeeze().transpose((1, 2, 0))


# MEAN_VALUE 더하기 : 전처리시 각 픽셀마다 뺏었던 MEAN_VALUE 값을 후처리시에는 다시 더해줌.
output += MEAN_VALUE

# 범위 넘어가는 값 잘라내기 : 이미지 픽셀값은 0-255 사이여야 하는데, 계산 과정에서 간혹 픽셀 값이 0 미만이거나,
#                            255를 초과하는 픽셀이 발생. 이런 값들을 np.clip()를 사용해서 제거.
output = np.clip(output, 0, 255)

# 자료형 바꾸기 : 이미지의 자료형을 일반 이미지에서 쓰이는 정수형(unsigned integer 8bit) 값으로 바꿔 줌.
output = output.astype('uint8')

#img[144:367, 481:811] = output
img[143:367, 479:811] = output

cv2.imshow('img', img)

#cv2.imshow('result', output)
cv2.waitKey(0)

 

바뀐 이미지(모델 적용된 이미지클립)를 다시 원본이미지의 액자 안에 집어넣기 결과

 

 

 

 

- 세로가 아닌 가로로 반반 잘라 적용하기

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/mosaic.t7')
net2 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/the_scream.t7')  # 두번째 모델 로드하기

img = cv2.imread('imgs/hw.jpg')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))


MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)


# 첫번째 모델 추론(Inference) 하기
net.setInput(blob)
output = net.forward()

output = output.squeeze().transpose((1, 2, 0))

output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')


# 두번째 모델 추론(Inference) 하기
net2.setInput(blob)
output2 = net2.forward()

output2 = output2.squeeze().transpose((1, 2, 0))
output2 = output2 + MEAN_VALUE

output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')

# output3 = np.concatenate([output[:, :250], output2[:, 250:]], axis=1)
output3 = np.concatenate(
    [output[:140, :], output2[140:, :]], axis=0)

cv2.imshow('output3', output3)


# cv2.imshow('img', img)
# cv2.imshow('result', output)
cv2.waitKey(0)

- 코드 적용 결과

 

 

- 세로가 아닌 가로로 반반 잘라 적용하기 - 응용하기 #1

가운데가 아닌 다른 곳으로 잘라 적용하기

특정키 (w,s,a,d) 키를 누를 경우 적용 경계의 이동을 위,아래,좌,우 방향으로 옮겨가며, 해당 이미지를 적용

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/mosaic.t7')
net2 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/the_scream.t7')  # 두번째 모델 로드하기

img = cv2.imread('imgs/hw.jpg')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))


MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# 첫번째 모델 추론(Inference) 하기
net.setInput(blob)
output = net.forward()
output = output.squeeze().transpose((1, 2, 0))
output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

# 두번째 모델 추론(Inference) 하기
net2.setInput(blob)
output2 = net2.forward()
output2 = output2.squeeze().transpose((1, 2, 0))
output2 = output2 + MEAN_VALUE
output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')

# output3 = np.concatenate([output[:, :250], output2[:, 250:]], axis=1) #세로로 반반 적용
output3 = np.concatenate(
    [output[:140, :], output2[140:, :]], axis=0)  # 가로로 반반 적용

# cv2.imshow('output3', output3)

y = int(img.shape[0] / 2)
x = int(img.shape[1] / 2)
axis_val = 0
while True:
    key = cv2.waitKey(0) & 0xFF  # 키보드 입력을 무한 대기, 8비트 마스크처리
    print(key, chr(key))        # 키보드 입력 값,  문자 값 출력
    if key == ord('w'):         # 'w' 키 이면 위로 이동
        axis_val = 0
        y += -1
        output3 = np.concatenate(
            [output[:y, :], output2[y:, :]], axis=axis_val)
    elif key == ord('s'):       # 's' 키 이면 아래로 분할 이동
        axis_val = 0
        y += +1
        output3 = np.concatenate(
            [output[:y, :], output2[y:, :]], axis=axis_val)
    elif key == ord('a'):       # 'a' 키 이면 좌측 분할 이동
        axis_val = 1
        x += -1
        output3 = np.concatenate(
            [output[:, :x], output2[:, x:]], axis=axis_val)
    elif key == ord('d'):       # 'd' 키 이면 우측 분할 이동
        axis_val = 1
        x += +1
        output3 = np.concatenate(
            [output[:, :x], output2[:, x:]], axis=axis_val)
    elif key == ord('q') or key == 27:  # 'q' 이거나 'esc' 이면 종료
        break
        cv2.destroyAllWindows()

    cv2.imshow('output3', output3)   # 새로운 분할 값으로 창 표시


# cv2.imshow('img', img)
# cv2.imshow('result', output)
# key = cv2.waitKey(0)  # 키보드 입력을 무한 대기, 8비트 마스크처리
# print(key)

 

 

- 세로가 아닌 가로로 반반 잘라 적용하기 - 응용하기 #2

2개가 아닌 여러개로 나눠서 잘라 적용하기

좌,우 3 등분하여 표현하기, w,s,a,d 키를 눌러서 적용 범위 조절하기

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/mosaic.t7')
net2 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/the_scream.t7')  # 두번째 모델 로드하기
net3 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/udnie.t7')  # 세번째 모델 로드하기
img = cv2.imread('imgs/hw.jpg')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))


MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# 첫번째 모델 추론(Inference) 하기
net.setInput(blob)
output = net.forward()
output = output.squeeze().transpose((1, 2, 0))
output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

# 두번째 모델 추론(Inference) 하기
net2.setInput(blob)
output2 = net2.forward()
output2 = output2.squeeze().transpose((1, 2, 0))
output2 = output2 + MEAN_VALUE
output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')

# 첫번째 모델 추론(Inference) 하기
net3.setInput(blob)
output3 = net3.forward()
output3 = output3.squeeze().transpose((1, 2, 0))
output3 += MEAN_VALUE
output3 = np.clip(output3, 0, 255)
output3 = output3.astype('uint8')

# output3 = np.concatenate([output[:, :250], output2[:, 250:]], axis=1) #세로로 반반 적용
output4 = np.concatenate(
    [output[:93, :], output2[93:187, :], output3[187:, :]], axis=0)  # 가로로 반반 적용

# cv2.imshow('output3', output3)

y_div = 3
x_div = 3
y = int(img.shape[0] / y_div)
x = int(img.shape[1] / x_div)

#print(str(y) + ' ' + str(x))
# quit()

axis_val = 0
while True:
    key = cv2.waitKey(0) & 0xFF  # 키보드 입력을 무한 대기, 8비트 마스크처리
    print(key, chr(key))        # 키보드 입력 값,  문자 값 출력
    if key == ord('1'):         # '1' 키 이면 변환하기 전의 원본이미지로 출력
        output4 = cv2.imread('imgs/hw.jpg')
    elif key == ord('w'):       # 's' 키 이면 아래로 분할 이동
        axis_val = 0
        y += -1
        output4 = np.concatenate(
            [output[:y, :], output2[y:y+int(img.shape[0]/3), :], output3[y+int(img.shape[0]/3):, :]], axis=axis_val)
    elif key == ord('s'):       # 's' 키 이면 아래로 분할 이동
        axis_val = 0
        y += +1
        output4 = np.concatenate(
            [output[:y, :], output2[y:y+int(img.shape[0]/3), :], output3[y+int(img.shape[0]/3):, :]], axis=axis_val)
    elif key == ord('a'):       # 'a' 키 이면 좌측 분할 이동
        axis_val = 1
        x += -1
        output4 = np.concatenate(
            [output[:, :x], output2[:, x:x+int(img.shape[1]/3)], output3[:, x+int(img.shape[1]/3):]], axis=axis_val)
    elif key == ord('d'):       # 'd' 키 이면 우측 분할 이동
        axis_val = 1
        x += +1
        output4 = np.concatenate(
            [output[:, :x], output2[:, x:x+int(img.shape[1]/3)], output3[:, x+int(img.shape[1]/3):]], axis=axis_val)
    elif key == ord('q') or key == 27:  # 'q' 이거나 'esc' 이면 종료
        break
        cv2.destroyAllWindows()

    cv2.imshow('output4', output4)   # 새로운 분할 값으로 창 표시


# cv2.imshow('img', img)
# cv2.imshow('result', output)
# key = cv2.waitKey(0)  # 키보드 입력을 무한 대기, 8비트 마스크처리
# print(key)

 

 

 

 

- 동영상  파일로 다양한 모델 적용하기 - 바로 응용간다.

https://drive.google.com/file/d/1aQxghXbYgyxecNrTtyJROraI_OImWVyN/view?usp=sharing 

지금까지 수행한 대부분의 기능을 동영상에도 적용해 봅시다.

 

03.mp4

 

drive.google.com

- 응용 단계 프로그램 소개

w,s,a,d 키를 누르면 이미지 모델링 영역의 영역 구분이 상,하,좌,우 방향으로 이동되며,

i,k,j,l 키를 누르면 해당 동영상의 이미지 영역 전체가 위,아래,왼쪽,오른쪽 방향으로 전체 이동한다.

숫자 0 ~ 9까지의 키를 누르면 동영상 속도를 조절 할 수 있다.

import cv2
import numpy as np

net = cv2.dnn.readNetFromTorch('models/instance_norm/mosaic.t7')
net2 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/the_scream.t7')  # 두번째 모델 로드하기
net3 = cv2.dnn.readNetFromTorch(
    'models/instance_norm/udnie.t7')  # 세번째 모델 로드하기
img = cv2.imread('imgs/hw.jpg')

cap = cv2.VideoCapture('imgs/03.mp4')

h, w, c = img.shape

img = cv2.resize(img, dsize=(500, int(h / w * 500)))


MEAN_VALUE = [103.939, 116.779, 123.680]
blob = cv2.dnn.blobFromImage(img, mean=MEAN_VALUE)

# 첫번째 모델 추론(Inference) 하기
net.setInput(blob)
output = net.forward()
output = output.squeeze().transpose((1, 2, 0))
output += MEAN_VALUE
output = np.clip(output, 0, 255)
output = output.astype('uint8')

# 두번째 모델 추론(Inference) 하기
net2.setInput(blob)
output2 = net2.forward()
output2 = output2.squeeze().transpose((1, 2, 0))
output2 = output2 + MEAN_VALUE
output2 = np.clip(output2, 0, 255)
output2 = output2.astype('uint8')

# 첫번째 모델 추론(Inference) 하기
net3.setInput(blob)
output3 = net3.forward()
output3 = output3.squeeze().transpose((1, 2, 0))
output3 += MEAN_VALUE
output3 = np.clip(output3, 0, 255)
output3 = output3.astype('uint8')

# output3 = np.concatenate([output[:, :250], output2[:, 250:]], axis=1) #세로로 반반 적용
output4 = np.concatenate(
    [output[:93, :], output2[93:187, :], output3[187:, :]], axis=0)  # 가로로 반반 적용

# cv2.imshow('output3', output3)

y_div = 3
x_div = 3
y = int(img.shape[0] / y_div)
x = int(img.shape[1] / x_div)

key_rate = 100  # 비디오 frame 동작 속도 , 디폴트 100ms
#print(str(y) + ' ' + str(x))
# quit()

y1 = 250
y2 = 534
x1 = 100
x2 = 600


axis_val = 0
while True:
    ret, cap_img = cap.read()

    if ret == False:
        break

    key = cv2.waitKey(key_rate) & 0xFF  # 키보드 입력을 무한 대기, 8비트 마스크처리
    print(key, chr(key))        # 키보드 입력 값,  문자 값 출력

    if key == ord('w'):       # 'w' 키 이면 아래로 분할 이동
        axis_val = 0
        y += -1
        output4 = np.concatenate(
            [output[:y, :], output2[y:y+int(img.shape[0]/3), :], output3[y+int(img.shape[0]/3):, :]], axis=axis_val)
    elif key == ord('s'):       # 's' 키 이면 아래로 분할 이동
        axis_val = 0
        y += +1
        output4 = np.concatenate(
            [output[:y, :], output2[y:y+int(img.shape[0]/3), :], output3[y+int(img.shape[0]/3):, :]], axis=axis_val)
    elif key == ord('a'):       # 'a' 키 이면 좌측 분할 이동
        axis_val = 1
        x += -1
        output4 = np.concatenate(
            [output[:, :x], output2[:, x:x+int(img.shape[1]/3)], output3[:, x+int(img.shape[1]/3):]], axis=axis_val)
    elif key == ord('d'):       # 'd' 키 이면 우측 분할 이동
        axis_val = 1
        x += +1
        output4 = np.concatenate(
            [output[:, :x], output2[:, x:x+int(img.shape[1]/3)], output3[:, x+int(img.shape[1]/3):]], axis=axis_val)
    elif key == ord('i'):       # 'i' 키 이면 비디오의 윗쪽으로 이동
        y1 -= 1
        y2 -= 1
    elif key == ord('k'):       # 'k' 키 이면 비디오의 아래쪽으로 이동
        y1 += 1
        y2 += 1
    elif key == ord('j'):       # 'j' 키 이면 비디오의 왼쪽으로 이동
        x1 -= 1
        x2 -= 1
    elif key == ord('l'):       # 'l' 키 이면 비디오의 오른쪽으로 이동
        x1 += 1
        x2 += 1
    elif key == ord('0'):       # '0' 키 이면 비디오 동작 속도를 1ms으로 동작
        key_rate = 1
    elif key == ord('1'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 10
    elif key == ord('2'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 20
    elif key == ord('3'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 30
    elif key == ord('4'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 40
    elif key == ord('5'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 50
    elif key == ord('6'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 60
    elif key == ord('7'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 70
    elif key == ord('8'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 80
    elif key == ord('9'):       # '1' 키 이면 비디오 동작 속도를 10ms으로 동작
        key_rate = 90
    elif key == ord('q') or key == 27:  # 'q' 이거나 'esc' 이면 종료
        break
        cv2.destroyAllWindows()

    # h2, w2, c2 = output4.shape
    # print(output4.shape)

    #output4_resize = cv2.resize(output4, dsize=(150, int(h / w * 150)))
    #cap_img[100:200, 250:400] = output4_resize
    cap_img[y1:y2, x1:x2] = output4  # y1:y2 좌표 값 , x1:x2 좌표값
    cv2.imshow('cap_img', cap_img)   # 새로운 분할 값으로 창 표시


# cv2.imshow('img', img)
# cv2.imshow('result', output)
# key = cv2.waitKey(0)  # 키보드 입력을 무한 대기, 8비트 마스크처리
# print(key)

 

- 수행 결과 동영상 촬영본.

https://drive.google.com/file/d/1KBBhMlGeS7kc8jBcUZfxdpgaeRTowQ5q/view?usp=sharing 

 

homework-03.mkv

 

drive.google.com

 

 

 

 

[ 딥러닝 관련 논문/코드 찾는 팁 ]

https://paperswithcode.com/

 

Papers with Code - The latest in Machine Learning

Papers With Code highlights trending Machine Learning research and the code to implement it.

paperswithcode.com

성능이 좋은 모델을 찾고 싶다. 시간이 필요.

Computer Vision, NLP 등..  

 

특정 논문의 경우, 해당 논문명을 구글에서 입력해서 다양한 언어 버전으로 리뷰를 확인 가능함.