이미지 분류 머신러닝을 위한 OpenCV 기초를 실습해 보자.
먼저 로컬 환경이 아니어도 jupyter로 작업할 수 있는 분석 환경을 세팅해 준다. 굳이 필요하지 않다면 이 부분은 스킵하고 본인이 사용하는 jupyter notebook이나 vscode, pycharm 등을 활용하면 된다.
# Miniconda 설치 파일 다운로드
$ wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh
# 다운로드한 스크립트 실행하여 Miniconda 설치
$ bash ./Miniconda3-latest-Linux-x86_64.sh
# 새로 설치된 Conda 초기화
$ source ~/.bashrc
# Mamba 설치
$ conda install mamba -c conda-forge -n base
# 새로운 Conda 환경 생성 및 패키지 설치
$ mamba create -n 원하는 이름 pytorch matplotlib jupyterlab torchvision opencv
# 여기서는 imagetest로 이름 설정
# linux 환경에서는 conda 사용 강력 추천
$ conda activate imagetest
# jupyterlab 설치
$ pip install jupyterlab
# python 실행
$ python
>>> from jupyter_server.auth import passwd
>>> passwd()
Enter password: # 원하는 비밀번호 입력
Verify password: # 다시 한 번 입력
'argon2:$argon2id$v=19$m=10240,t=10,p=8$zovWyLF8PpilW4G4a06GSA$dG8V8xHQbrqUOrwf6EDskNkIMs51bPVhJMILpFPtveA' # 비밀번호마다 다름. 따옴표 안을 복사
>>> exit()
# cmd 창에서 적어도 따옴표는 직접 입력해 주자
# JupyterLab 기초 설정
$ jupyter lab --generate-config Writing default config to: /home/shinyoung/.jupyter/jupyter_lab_config.py
$ vim .jupyter/jupyter_lab_config.py # 또는 nano
# 편집 모드 들어가서 다음 내용 붙여넣기
c = get_config()
c.JupyterApp.config_file_name = 'jupyter_lab_config.py'
c.ServerApp.allow_origin = '*'
c.ServerApp.ip = 'localhost'
c.NotebookApp.password = 'argon2:$argon2id$v=19$m=10240,t=10,p=8$zovWyLF8PpilW4G4a06GSA$dG8V8xHQbrqUOrwf6EDskNkIMs51bPVhJMILpFPtveA'
# 저장 후 편집기 종료
# libGL 라이브러리 설치
$ sudo apt update
$ sudo apt install libgl1-mesa-glx
# 주피터 랩 열기
$ jupyter lab
# 이제 인터넷 창에 localhost:8888, 121.140.172.195:8888, http://121.140.172.195:8888/ 입력하면 jupyter lab 접속 가능
# ip 주소 알면 로컬 아니어도 접속 가능하다, 그래서 conda가 좋은 거
환경 세팅이 완료 되었다면 OpenCV 기초 실습을 시작해 보자.
# OpenCV 및 시각화 라이브러리 불러오기
import cv2
from matplotlib import pyplot as plt
# 이미지 읽어오기, 원하는 파일을 주피터로 업로드하고 인스턴스에 업로드
img = cv2.imread('짱구 하트.jpg')
# 읽어온 이미지를 화면에 표시
img_rgb = cv2. cvtColor(img, cv2.COLOR_BGR2RGB)
# openCV는 기본적으로 RGB(빨강 초록 파랑 순서) 채널이 아닌, BGR(파랑 초록 빨강)
# 순서로 데이터를 저장 - 따라서 RGB로 간단한 내장 명령어를 통한 치환이 필요
plt.imshow(img_rgb)
plt.show()
# openCV 자체 이미지 출력 명령어인 cv2.imshow()를 쓰지 않고, matplotlib를 이용해 간접적으로 출력하는 이유?
# 위에서 세팅한 환경은 주피터는 브라우저 기반 환경으로, 직접적인 GPU를 통한 출력을 지원하지 않는다.
# 해당 명령어를 쓸 경우 커널이 뻗어버리니 주의!
# 해당 환경 설정을 따로 진행하지 않았다면 상관 없다.
![]() | ![]() |
---|
# 비율로 확대 및 축소
big_img = cv2.resize(img_rgb, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)
sml_img = cv2.resize(img_rgb, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_LINEAR)
# 고정 수치로 확대 및 축소
resized_img1 = cv2.resize(img_rgb,(50,200), interpolation=cv2.INTER_AREA)
resized_img2 = cv2.resize(img_rgb,(600,300), interpolation=cv2.INTER_LINEAR)
![]() | ![]() |
---|
# 그레이스케일화 (1차원)
# 색 정보가 중요하지 않은 경우 연산량 줄이기 위해 사용
# GRAY2RGB : 그레이스케일화 하면 다시 RGB로 돌리기 어렵다, 그레이스케일화 된 이미지에 새로운 색을 더할 때 사용(예. 윤곽선 표시)
gray_img = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2GRAY)
plt.imshow(gray_img, cmap='gray')
plt.show()
# plt.imshow(gray_img, cmap='gray’) 에서, 컬러맵을 gray로 지정해 주는 이유는?
# Matplotlib의 디폴트 컬러맵은 ‘viridis’로, 녹색~파란색의 그라데이션을 가지는 cmap
# 별도로 지정하지 않을 경우, 의도하지 않은 이미지가 생성됨
gray_img.shape # → (420, 420)
# 그레이스케일화 하면 (420, 420, 1), 1은 생략 : RGB에 비해 차원이 줄어드는 거
# 데이터 부풀릴 때 보편적으로 쓸 수 있다 (데이터 증강, Data Augmentation)
# 좌우 반전(양수인 정수 넣어준다)
img_flip_lr = cv2.flip(img_rgb, 1)
# 상하 반전(0 넣어준다)
img_flip_ud = cv2.flip(img_rgb, 0)
# 상하좌우 반전(음수인 정수 넣어준다)
img_flip_both = cv2.flip(img_rgb, -1)
# 2x2 subplot 생성
plt.figure(figsize = (10, 10))
# 원본 이미지
plt.subplot(2,2,1) # 세로 2, 가로 2개로 나눠서 1번째 칸
plt.imshow(img_rgb)
plt.title('Original')
plt.axis('off')
# 좌우 반전 이미지
plt.subplot(2,2,2)
plt.imshow(img_flip_lr)
plt.title('Flip Left-Right')
plt.axis('off')
# 상하 이미지
plt.subplot(2,2,3)
plt.imshow(img_flip_ud)
plt.title('Flip Up-Down')
plt.axis('off')
# 상하 좌우 반전 이미지
plt.subplot(2,2,4)
plt.imshow(img_flip_both)
plt.title('Flip Both')
plt.axis('off')
# 이미지 표시
plt.show()
# 1) 회전 행렬 생성 함수로 회전 변환 행렬을 계산
# cv2.getRotationMatrix2D(center, angle, scale) 함수 사용 - center : 회전의 중심, angle : 회전 각도, scale : 이미지 스케일
# 2) 계산한 변환 행렬로 이미지 회전
# cv2.warpAffine(이미지, M, dsize) 함수 사용 - M : 변환 행렬, dsize : 출력 이미지의 크기(패러미터 미기재시 원본크기)
rows, cols = img_rgb.shape[:2] # 행/열/채널 순이므로 행과 열 수만 가져오기
# 회전을 위한 변환행렬 생성
M1 = cv2.getRotationMatrix2D((cols / 2, rows / 2), 45, 1) # 중심점에서 45도 회전, 스케일은 1
M2 = cv2.getRotationMatrix2D((cols / 2, rows / 2), 90, 1) # 중심점에서 90도 회전, 스케일은 1
M3 = cv2.getRotationMatrix2D((cols / 2, rows / 2), 135, 1.5) # 중심점에서 135도 회전, 스케일은 1.5
# scale=1로 주면 빈 공간을 검정으로 채워서 이미지 특징으로 잡을 확률이 높음
# scale을 키워서 사진이 꽉 차도록 만들어준다
# scale 키워서 내가 원하는 특징이 잘린다고 생각되면 그대로 넣어야지 뭐, 대신 여기 채우는 방법 따로 설정해 줄 수 있다
# 변환 행렬을 사용하여 이미지 회전
rotated_img1 = cv2.warpAffine(img_rgb, M1, (cols, rows))
rotated_img2 = cv2.warpAffine(img_rgb, M2, (cols, rows))
rotated_img3 = cv2.warpAffine(img_rgb, M3, (cols, rows))
# 이미지 시각화
plt.figure(figsize = (10, 10))
# 원본 이미지
plt.subplot(2,2,1) # 세로 2, 가로 2, 1번째
plt.imshow(img_rgb)
plt.title('Original')
plt.axis('off')
# 좌우 반전 이미지
plt.subplot(2,2,2)
plt.imshow(rotated_img1)
plt.title('45°, scale=1')
plt.axis('off')
# 상하 이미지
plt.subplot(2,2,3)
plt.imshow(rotated_img2)
plt.title('90°, scale=1')
plt.axis('off')
# 상하 좌우 반전 이미지
plt.subplot(2,2,4)
plt.imshow(rotated_img3)
plt.title('135°, scale=1.5')
plt.axis('off')
# 이미지 표시
plt.show()
crop_img = img_rgb[250:340, 100:190].copy()
# 원본 훼손하지 않고 잘라와서 새로운 인스턴스로 사용하려면 .copy 써야 한다
plt.figure(figsize = (10,10))
plt.imshow(crop_img)
plt.show()
# 원하는 영역이 좁은 공간에 있을 때 사용
# 일반적으로는 잘 사용하지 않는다