이미지 분류 머신러닝을 위한 OpenCV 기초 실습 1

신 영·2024년 4월 1일
1

Python

목록 보기
3/4
post-thumbnail

이미지 분류 머신러닝을 위한 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 기초

✅이미지 로드

# 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를 통한 출력을 지원하지 않는다.
# 해당 명령어를 쓸 경우 커널이 뻗어버리니 주의!
# 해당 환경 설정을 따로 진행하지 않았다면 상관 없다.

✅이미지 확대 & 축소

  • cv2.resize(이미지, dsize=None, fx=n, fy=n, interpolation=속성)
  • dsize 패러미터는 절대크기 변경에 쓰이는 패러미터이므로, None으로 둔다
  • fx : 가로 변경 비율, fy : 세로 변경 비율, interpolation : 보간법(몇몇 점의 값을 알고 있을 때, 그 사이에 있는 점의 값을 예측하는 기술)
  • 이미지를 확대하는 경우: 바이큐빅 보간법, 쌍 선형 보간법
  • 사용 이미지를 축소하는 경우: 영역 보간법 사용
# 비율로 확대 및 축소
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)

✅색상 공간 변환

  • cvtColor를 이용하면, 그레이스케일 이외에도 다양한 색 공간 간에 변환을 진행할 수 있다
  • 고채널→저채널 변환이나 상호 호환되지 않는 채널간 변환은 비가역적(되돌릴 수 없음)
# 그레이스케일화 (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()

# 원하는 영역이 좁은 공간에 있을 때 사용
# 일반적으로는 잘 사용하지 않는다

0개의 댓글