💖 星野アイ (◍•ᴗ•◍)♡ ✧*。
-
SKIN_Project
3일껄로 할까나
Google Colab
Dataset
구글 드라이브 > 내 드라이브 > SKIN > dataset_skin
Code : SKIN_Project.ipynb
import matplotlib.pyplot as plt
import numpy as np
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import models, layers
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# ✅ Google Drive 마운트
from google.colab import drive
drive.mount('/content/drive')
# ✅ 경로 설정
dataset_path = '/content/drive/MyDrive/SKIN/dataset_skin' # 너가 올린 경로로 수정
model_save_path = '/content/drive/MyDrive/SKIN/skin_model.h5' # 원하는 저장 경로
# ✅ 데이터 증강 설정 (rotation_range는 15도만 줌)
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2,
rotation_range=15,
width_shift_range=0.05,
height_shift_range=0.05,
shear_range=0.1,
zoom_range=0.1,
brightness_range=[0.8, 1.2],
horizontal_flip=True,
fill_mode='nearest'
)
# ✅ 데이터 로딩
train_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96), # MobileNetV2는 최소 96x96부터 가능
batch_size=32,
class_mode='categorical',
subset='training',
shuffle=True
)
val_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='validation',
shuffle=True
)
# ✅ 클래스 이름 자동 추출
class_names = list(train_generator.class_indices.keys())
print("클래스 인덱스:", train_generator.class_indices)
# ✅ MobileNetV2 기반 모델 구성
base_model = MobileNetV2(input_shape=(96, 96, 3), include_top=False, weights='imagenet')
base_model.trainable = False
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(len(class_names), activation='softmax')
])
model.compile(optimizer=Adam(learning_rate=1e-4),
loss='categorical_crossentropy',
metrics=['accuracy'])
# ✅ 콜백 설정
early_stop = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True)
# ✅ 학습 실행
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=50,
callbacks=[early_stop, checkpoint],
verbose=2
)
# ✅ 결과 시각화
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.show()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
# ✅ 학습 이미지 예시
x_batch, y_batch = next(train_generator)
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.xticks([]); plt.yticks([]); plt.grid(False)
plt.imshow(x_batch[i])
label_idx = np.argmax(y_batch[i])
plt.xlabel(class_names[label_idx])
plt.tight_layout()
plt.show()
# ✅ 모델 저장 (.h5 파일)
model.save(model_save_path)
print(f"모델이 저장되었습니다: {model_save_path}")
Result
Ubuntu
Terminal
# 작업할 디렉토리 생성 후 이동
mkdir skin_project
cd skin_project
# 가상환경 생성 및 활성화
python3 -m venv venv
source venv/bin/activate
# 필요한 패키지 설치
pip install tensorflow opencv-python numpy
# skin_names.json 생성
vi skin_names.json
// skin_names.json
[
"기저세포암",
"표피낭종",
"혈관종",
"비립종",
"흑색점",
"편평세포암",
"사마귀"
]
# NanumGothic 설치
sudo apt install fonts-nanum
fc-list | grep NanumGothic
# Pillow 패키지 설치
pip install Pillow
# predict_cam.py 생성
vi predict_cam.py
# predict_cam.py
import cv2
import numpy as np
import tensorflow as tf
import json
from PIL import ImageFont, ImageDraw, Image
# 클래스 이름 (한글) 불러오기
with open("skin_names.json", "r") as f:
class_names = json.load(f)
# 모델 로드
model = tf.keras.models.load_model("skin_model.h5")
# 한글 폰트 경로 (Ubuntu에서 사용 가능)
FONT_PATH = "/usr/share/fonts/truetype/nanum/NanumGothic.ttf"
font = ImageFont.truetype(FONT_PATH, 32)
# 웹캠 시작
cap = cv2.VideoCapture(2)
print("Press 'q' to quit.")
while True:
ret, frame = cap.read()
if not ret:
break
# 예측을 위한 전처리
img = cv2.resize(frame, (96, 96))
img_array = np.expand_dims(img / 255.0, axis=0)
pred = model.predict(img_array)[0]
label = class_names[np.argmax(pred)]
confidence = np.max(pred)
# 원본 프레임을 Pillow 이미지로 변환
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(frame_rgb)
draw = ImageDraw.Draw(pil_img)
text = f"{label} ({confidence*100:.1f}%)"
# 텍스트 출력
draw.text((10, 30), text, font=font, fill=(0, 255, 0)) # 초록색
# 다시 OpenCV 형식으로 변환하여 출력
frame_bgr = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
cv2.imshow("Skin Classifier", frame_bgr)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Comment
웹캠으로 인식한 결과 흑색점을 제대로 인식하지 못해서 빼야될 것 같음
기저세포암 o
표피낭종 o
혈관종 o
비립종 o
흑색점 x
편평세포암 o
사마귀 o
-
SKIN_Project
최종 한거 노션에
체크
Google Colab
Dataset
구글 드라이브 > 내 드라이브 > SKIN > dataset_skin
Code : SKIN_Project.ipynb
import matplotlib.pyplot as plt
import numpy as np
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import models, layers
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# ✅ Google Drive 마운트
from google.colab import drive
drive.mount('/content/drive')
# ✅ 경로 설정
dataset_path = '/content/drive/MyDrive/SKIN/dataset_skin' # 너가 올린 경로로 수정
model_save_path = '/content/drive/MyDrive/SKIN/skin_model.h5' # 원하는 저장 경로
# ✅ 데이터 증강 설정 (rotation_range는 15도만 줌)
datagen = ImageDataGenerator(
rescale=1./255,
validation_split=0.2,
rotation_range=15,
width_shift_range=0.05,
height_shift_range=0.05,
shear_range=0.1,
zoom_range=0.1,
brightness_range=[0.8, 1.2],
horizontal_flip=True,
fill_mode='nearest'
)
# ✅ 데이터 로딩
train_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96), # MobileNetV2는 최소 96x96부터 가능
batch_size=32,
class_mode='categorical',
subset='training',
shuffle=True
)
val_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='validation',
shuffle=True
)
# ✅ 클래스 이름 자동 추출
class_names = list(train_generator.class_indices.keys())
print("클래스 인덱스:", train_generator.class_indices)
# ✅ MobileNetV2 기반 모델 구성
base_model = MobileNetV2(input_shape=(96, 96, 3), include_top=False, weights='imagenet')
base_model.trainable = False
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(len(class_names), activation='softmax')
])
model.compile(optimizer=Adam(learning_rate=1e-4),
loss='categorical_crossentropy',
metrics=['accuracy'])
# ✅ 콜백 설정
early_stop = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True)
# ✅ 학습 실행
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=50,
callbacks=[early_stop, checkpoint],
verbose=2
)
# ✅ 결과 시각화
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.show()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
# ✅ 학습 이미지 예시
x_batch, y_batch = next(train_generator)
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.xticks([]); plt.yticks([]); plt.grid(False)
plt.imshow(x_batch[i])
label_idx = np.argmax(y_batch[i])
plt.xlabel(class_names[label_idx])
plt.tight_layout()
plt.show()
# ✅ 모델 저장 (.h5 파일)
model.save(model_save_path)
print(f"모델이 저장되었습니다: {model_save_path}")
Result
Ubuntu
Terminal
# 작업할 디렉토리 생성 후 이동
mkdir skin_project
cd skin_project
# 가상환경 생성 및 활성화
python3 -m venv venv
source venv/bin/activate
# 필요한 패키지 설치
pip install tensorflow opencv-python numpy
# skin_names.json 생성
vi skin_names.json
// skin_names.json
[
"기저세포암",
"표피낭종",
"혈관종",
"비립종",
"흑색점",
"편평세포암",
"사마귀"
]
# NanumGothic 설치
sudo apt install fonts-nanum
fc-list | grep NanumGothic
# Pillow 패키지 설치
pip install Pillow
# predict_cam.py 생성
vi predict_cam.py
# predict_cam.py
import cv2
import numpy as np
import tensorflow as tf
import json
from PIL import ImageFont, ImageDraw, Image
# 클래스 이름 (한글) 불러오기
with open("skin_names.json", "r") as f:
class_names = json.load(f)
# 모델 로드
model = tf.keras.models.load_model("skin_model.h5")
# 한글 폰트 경로 (Ubuntu에서 사용 가능)
FONT_PATH = "/usr/share/fonts/truetype/nanum/NanumGothic.ttf"
font = ImageFont.truetype(FONT_PATH, 32)
# 웹캠 시작
cap = cv2.VideoCapture(2)
print("Press 'q' to quit.")
while True:
ret, frame = cap.read()
if not ret:
break
# 예측을 위한 전처리
img = cv2.resize(frame, (96, 96))
img_array = np.expand_dims(img / 255.0, axis=0)
pred = model.predict(img_array)[0]
label = class_names[np.argmax(pred)]
confidence = np.max(pred)
# 원본 프레임을 Pillow 이미지로 변환
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(frame_rgb)
draw = ImageDraw.Draw(pil_img)
text = f"{label} ({confidence*100:.1f}%)"
# 텍스트 출력
draw.text((10, 30), text, font=font, fill=(0, 255, 0)) # 초록색
# 다시 OpenCV 형식으로 변환하여 출력
frame_bgr = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
cv2.imshow("Skin Classifier", frame_bgr)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Comment
웹캠으로 인식한 결과 흑색점을 제대로 인식하지 못해서 빼야될 것 같음
기저세포암 o
표피낭종 o
혈관종 o
비립종 o
흑색점 x
편평세포암 o
사마귀 o
-
Day8 : Project_Plan
🧱 라즈베리파이 환경 구축
ctrl + alt + t
→ 터미널 실행 단축키
htop
→ 실시간 시스템 리소스(메모리, CPU 사용량 등) 확인 툴 실행
sudo apt update
→ 패키지 목록 업데이트 (최신 버전 확인용)
sudo apt upgrade
→ 설치된 패키지들을 최신 버전으로 업그레이드
sudo apt install ibus ibus-hangul
→ 한글 입력기(iBus + Hangul) 설치
sudo apt install fonts-nanum fonts-unfonts-core
→ 나눔글꼴, 윤글꼴 등 한글 폰트 설치
sudo update-alternatives --config x-www-browser
→ 기본 웹 브라우저 설정 변경
mkdir YOLO
→ 'YOLO'라는 새 폴더 생성
cd YOLO/
→ YOLO 폴더로 이동
python -m venv .yolo
→ '.yolo'라는 이름의 가상환경(virtualenv) 생성
source .yolo/bin/activate
→ 가상환경 활성화
git clone https://github.com/ultralytics/yolov5
→ YOLOv5 저장소를 GitHub에서 복제
cd yolov5/
→ 복제한 yolov5 디렉토리로 이동
pip install -U pip
→ pip(패키지 관리자) 최신 버전으로 업데이트
pip install ultralytics
→ Ultralytics 패키지 설치 (YOLOv5 실행용 라이브러리 포함)
python detect.py --weights yolov5s.pt --source 0 --img 160
→ YOLOv5s 모델로 웹캠(0번)을 실시간 객체 탐지 (해상도: 160)
sudo apt install openssh-server
→ SSH 서버 설치 (다른 PC에서 원격 접속 가능하게 함)
curl -fsSL https://ollama.com/install.sh | sh
→ Ollama 설치 스크립트 실행 (로컬 AI 모델 실행 도구)
ollama run gemma3:1b
→ Gemma 3 1B 모델 실행 (로컬 LLM 테스트)
# PC에서 원격 접속
ssh hhhong@10.10.15.183
🤝 팀원들과 프로젝트 계획 수립
라즈베리파이를 활용한 On-Device AI 프로젝트 방향성 논의
김민규: 수어 인식 (Sign Language Recognition, SLR)
엄찬하: 머리카락 두께 및 탈모 초기 진단 + 맞춤형 예방 관리 가이드
임재홍: 딥페이크 감지 시스템
임재홍: 거짓 뉴스 판독 시스템
💬 회고
실시간 객체 탐지와 로컬 AI 모델이 동시에 가능함을 확인함
라즈베리파이 환경 세팅 중 허브를 통해 LAN 케이블을 분할하는 과정에서 PC에서 네트워크를 잡지 못하는 문제가 발생했으나, 수업 종료 후 정상적으로 연결되어 IP 충돌 문제였음을 파악함
💡 강사님의 조언
“CNN 모델을 C++ 수준으로 뽑으면 가장 좋음.”
프로젝트 성공 여부와 상관없이, 구현 수준을 높이는 것이 더 중요하다는 취지의 말씀
✍️ 해석 및 나의 이해
단순히 모델을 “돌리는 것”을 넘어서, 구조를 정확히 이해하고 직접 구현해보는 경험이 중요하다는 뜻
PyTorch, TensorFlow 같은 프레임워크를 쓸 수도 있지만, 기초 수학/로직을 바탕으로 CNN을 직접 구현(C++ 수준의 저수준 접근)해보는 것이 실력 향상에 도움됨
실무 또는 온디바이스 환경(Raspberry Pi 등)에서는 최적화된 저수준 코드로 모델을 이식하는 능력도 요구됨
🔎 앞으로의 학습 방향에 반영
CNN 레이어 구성 및 연산 방식(ReLU, Conv, Pooling 등)을 직접 구현해보는 연습
Python으로 구현 → 가능하다면 C++로 구조만이라도 옮겨보기
PyTorch에서 .model.eval()처럼 내부 동작이 어떻게 구성되는지 역으로 살펴보기
-
Day7 : HARIBO_Mini_Project
🚀 Model Improvement Strategy
기존 CNN 모델에 다음 전략을 통합하여 성능을 향상시킨 구조를 구현함:
데이터 증강 적용: 다양한 이미지 변형을 통해 학습 데이터 다양성 확보
전이학습 도입: MobileNetV2의 사전학습된 특징 추출기 사용
Dropout 및 Dense 레이어 추가: 오버피팅 방지 및 모델 표현력 향상
EarlyStopping, ModelCheckpoint 적용: 과적합 방지 및 최적 모델 저장
데이터 증강 강화: 회전, 이동, 확대/축소, 반전 등 복합적 증강 적용
🍬 HARIBO_Dataset Preparation
5가지 하리보 젤리 종류(bear, cola, egg, heart, ring)를 직접 촬영하여 이미지 데이터셋 생성
다양한 각도·조명·배경에서 수집된 이미지 총 500장 (각 클래스당 100장 내외)
구글 드라이브에 업로드 후, Google Colab 환경에서 실습용으로 연동
💡 Code : CNN with Transfer Learning & Augmentation
import matplotlib.pyplot as plt
import numpy as np
import os
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from tensorflow.keras.applications import MobileNetV2
from tensorflow.keras import models, layers
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# ✅ 구글 드라이브 마운트
from google.colab import drive
drive.mount('/content/drive')
# ✅ 경로 설정
dataset_path = '/content/drive/MyDrive/haribo_dataset'
model_save_path = '/content/drive/MyDrive/haribo_model.h5'
# ✅ 데이터 증강 설정
datagen = ImageDataGenerator(
rescale=1./255, # 픽셀 값을 0~1 범위로 정규화
validation_split=0.2, # 전체 데이터 중 20%를 검증용으로 사용
rotation_range=90, # 최대 ±90도 범위 내에서 무작위 회전
width_shift_range=0.1, # 전체 너비의 10%만큼 좌우 이동
height_shift_range=0.1, # 전체 높이의 10%만큼 상하 이동
shear_range=0.1, # 전단 변환 (이미지를 기울이는 효과)
zoom_range=0.1, # 10% 범위 내 무작위 확대/축소
horizontal_flip=True, # 이미지를 좌우로 무작위 반전
fill_mode='nearest' # 변환 후 생긴 빈 영역을 가장 가까운 픽셀로 채움
)
# ✅ 데이터 로딩
train_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='training',
shuffle=True
)
val_generator = datagen.flow_from_directory(
dataset_path,
target_size=(96, 96),
batch_size=32,
class_mode='categorical',
subset='validation',
shuffle=True
)
# ✅ 클래스 이름 자동 추출
class_names = list(train_generator.class_indices.keys())
print("클래스 인덱스:", train_generator.class_indices)
# ✅ MobileNetV2 기반 모델 구성
base_model = MobileNetV2(input_shape=(96, 96, 3), include_top=False, weights='imagenet')
base_model.trainable = False
model = models.Sequential([
base_model,
layers.GlobalAveragePooling2D(),
layers.Dense(128, activation='relu'),
layers.Dropout(0.5),
layers.Dense(len(class_names), activation='softmax') # 클래스 수 자동 반영
])
model.compile(optimizer=Adam(learning_rate=1e-4),
loss='categorical_crossentropy',
metrics=['accuracy'])
# ✅ 콜백 설정
early_stop = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
checkpoint = ModelCheckpoint('best_model.h5', save_best_only=True)
# ✅ 학습 실행
history = model.fit(
train_generator,
validation_data=val_generator,
epochs=50,
callbacks=[early_stop, checkpoint],
verbose=2
)
# ✅ 결과 시각화
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.show()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
# ✅ 학습 이미지 예시
x_batch, y_batch = next(train_generator)
plt.figure(figsize=(10, 10))
for i in range(25):
plt.subplot(5, 5, i + 1)
plt.xticks([]); plt.yticks([]); plt.grid(False)
plt.imshow(x_batch[i])
label_idx = np.argmax(y_batch[i])
plt.xlabel(class_names[label_idx])
plt.tight_layout()
plt.show()
# ✅ 모델 저장 (.h5 파일)
model.save(model_save_path)
print(f"모델이 저장되었습니다: {model_save_path}")
✅ Result : 학습 결과 시각화 및 예측 확인
🔍 Summary
MobileNetV2를 기반으로 한 전이학습 모델이 적은 데이터셋에서도 좋은 성능을 보임
실시간 예측 환경에도 최적화된 모델 구조로 전환 가능 (On-Device AI 적용 가능)
💻 Real-Time Inference Setup on Terminal
📁 1. 디렉토리 구성
mkdir haribo_cam_classifier
cd haribo_cam_classifier
🐍 2. 가상환경 생성 및 패키지 설치
python3 -m venv venv
source venv/bin/activate
pip install tensorflow opencv-python-headless numpy
📥 3. 학습한 모델(.h5)을 Google Drive에서 다운로드하여 복사
haribo_model.h5 파일을 Google Drive에서 다운받아 haribo_cam_classifier 디렉토리에 위치시킴
🖼️ 4. 클래스 이름 파일 생성 (class_names.json)
["bear", "cola", "egg", "heart", "ring"]
💡 5. 실시간 분류 코드 작성 (predict_cam.py)
import cv2
import numpy as np
import tensorflow as tf
import json
# 모델과 클래스 이름 로드
model = tf.keras.models.load_model('haribo_model.h5')
with open('class_names.json', 'r') as f:
class_names = json.load(f)
def preprocess(frame):
img = cv2.resize(frame, (96, 96))
img = img.astype('float32') / 255.0
return np.expand_dims(img, axis=0)
cap = cv2.VideoCapture(2)
if not cap.isOpened():
print("카메라를 열 수 없습니다.")
exit()
print("젤리 분류 시작! (Q 키를 누르면 종료)")
while True:
ret, frame = cap.read()
if not ret:
break
input_img = preprocess(frame)
pred = model.predict(input_img)
label = class_names[np.argmax(pred)]
# 예측 결과 화면에 출력
cv2.putText(frame, f'Prediction: {label}', (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow('Haribo Classifier', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
🧩 6. OpenCV 설치 (GUI 지원 포함)
pip install opencv-python
▶️ 7. 실시간 예측 실행
python3 predict_cam.py
✅ 8. 결과 정리
🍬 모델 예측을 위한 5개 클래스 하리보 샘플 전체 이미지
🧪 예측 예시: heart
🧪 예측 예시: ring
🧪 예측 예시: cola
🧪 예측 예시: egg
🧪 예측 예시: bear
-
Day6 : CNN
📌 CNN란?
CNN(Convolutional Neural Network)은 이미지 인식과 분류에 특화된 인공신경망으로, 사람의 시각 처리 방식과 유사하게 국소적인 영역을 중심으로 특징(feature)을 추출하고 학습한다. 기존의 MLP보다 이미지 구조를 더 잘 반영하며, 컴퓨터 비전(CV) 분야에서 널리 사용된다.
<합성곱 층 - Convolution Layer>
입력 이미지에 필터(커널)를 적용해 특징 맵(Feature Map) 생성
보통 3×3 크기의 필터 사용 (VGGNet 등) → 작을수록 다양한 feature 추출 가능
필터의 두께는 입력 데이터(예: RGB → 3)에 자동 맞춰짐
Stride: 필터 이동 간격, 작을수록 정밀하고 클수록 빠르게 처리됨
Padding: 출력 feature map 크기를 유지하려면 padding=same 설정
합성곱 연산 뒤에는 활성화 함수(ReLU)를 적용해 비선형성 도입
<풀링 층 - Pooling Layer>
MaxPooling: 풀링 영역의 최대값 → 주요 특징만 강조
AveragePooling: 영역 내 평균값 사용
GlobalAveragePooling: Flatten 없이 전체 평균만 뽑아내는 방식 (GoogLeNet)
연산량 감소 + 과적합 방지 + 공간 구조 요약
<밀집층 - Fully Connected Layer>
Flatten 레이어로 feature map을 1차원 벡터로 변환
이후 Fully Connected Layer를 거쳐 클래스별 출력값 생성
주로 softmax를 출력층 활성화 함수로 사용해 확률값 도출
🧮 example flow
예: 최종 출력값이 (0.7, 0)이고 정답이 (1, 0)인 경우 → 0.3 오차
이 오차를 역전파(backpropagation)로 전파하며 가중치 업데이트
경사하강법(gradient descent) 등 최적화 알고리즘 사용
👁️ CNN 모델과 인간 시각 처리의 유사성
인간의 시각 피질도 단순한 시각 정보 → 복잡한 특징 순으로 처리
CNN도 층이 깊어질수록 복잡한 feature를 추출
저차원 edge → 고차원 패턴 추출 흐름이 시각 정보 처리와 닮음
🧠 대표 CNN 구조들
AlexNet (2012): CNN을 유명하게 만든 최초의 구조, 8층 구성
VGGNet (2014): 3×3 필터 반복 사용, 구조 단순 & 효과적
GoogLeNet: Inception 구조 + Global Average Pooling 사용
ResNet: Residual Block 사용 → 층이 깊어져도 성능 유지
🔁 전이학습 (Transfer Learning)
기존 사전학습된 모델의 가중치를 재사용하여 적은 데이터로도 학습 가능
Feature Extraction: 기존 구조 유지, 출력층만 새로 학습
Fine-Tuning: 일부 층은 고정, 나머지는 재학습
적은 데이터 상황에서 강력한 성능 발휘 가능
👨💻 실습
💡 Code : CNN Layer 구현
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
# 합성곱 함수 구현
def conv(a, b):
c = np.array(a) * np.array(b)
return np.sum(c)
# MaxPooling 함수 구현(한 개의 map 계산)
def MaxPooling(nimg): # 2d input
nimg = np.array(nimg)
i0, j0 = nimg.shape # i0 = nimg.shape[0], j0 = nimg.shape[1]
i1 = int((i0 + 1) / 2)
j1 = int((j0 + 1) / 2)
output = np.zeros((i1, j1))
if i0 % 2 == 1:
i0 += 1
tmp = np.zeros((1, j0))
nimg = np.concatenate([nimg, tmp], axis=0)
if j0 % 2 == 1:
j0 += 1
tmp = np.zeros((i0, 1))
nimg = np.concatenate([nimg, tmp], axis=1)
for i in range(output.shape[0]):
for j in range(output.shape[1]):
a = np.array(nimg[2*i:2*i+2, 2*j:2*j+2])
output[i, j] = a.max()
return output
# 합성곱 출력 층(reature map) 함수 구현(한 개의 filter 계산)
def featuring(nimg, filters):
feature = np.zeros((nimg.shape[0] - 2, nimg.shape[1] - 2))
for i in range(feature.shape[0]):
for j in range(feature.shape[1]):
a = nimg[i:i+3, j:j+3]
feature[i, j] = conv(a, filters)
return feature
# MaxPooling 출력 층 함수 구현(여러 map 계산)
def Pooling(nimg):
nimg = np.array(nimg)
pool0 = []
for i in range(len(nimg)):
pool0.append(MaxPooling(nimg[i]))
return pool0
# 배열을 그림으로 변환
def to_img(nimg):
nimg = np.array(nimg)
nimg = np.uint8(np.round(nimg))
fimg = []
for i in range(len(nimg)):
fimg.append(Image.fromarray(nimg[i]))
return fimg
# feature map 생성(여러 filter 계산)
def ConvD(nimg, filters):
nimg = np.array(nimg)
feat0 = []
for i in range(len(filters)):
feat0.append(featuring(nimg, filters[i]))
return feat0
# ReLU 활성화 함수
def ReLU(fo):
fo = np.array(fo)
fo = (fo > 0) * fo
return fo
# CNN Layer 함수 : Conv + ReLU + MaxPooling
def ConvMax(nimg, filters):
nimg = np.array(nimg)
f0 = ConvD(nimg, filters)
f0 = ReLU(f0)
fg = Pooling(f0)
return f0, fg
# 그림 그리기 : 합성곱 후의 상태와 MaxPooling 후의 상태를 그림으로 그리기
def draw(f0, fg0, size=(12, 8), k=-1): # size와 k는 기본값 설정
plt.figure(figsize=size)
for i in range(len(f0)):
plt.subplot(2, len(f0), i + 1)
plt.gca().set_title('Conv' + str(k) + '-' + str(i))
plt.imshow(f0[i])
for i in range(len(fg0)):
plt.subplot(2, len(fg0), len(f0) + i + 1)
plt.gca().set_title('MaxP' + str(k) + '-' + str(i))
plt.imshow(fg0[i])
if k != -1: # k=-1이 아니면 그림을 저장
plt.savefig('conv' + str(k) + '.png')
# 3개의 activation map 합치기 : MaxPooling 후의 결과 map들을 하나의 데이터로 통합
def join(mm):
mm = np.array(mm)
m1 = np.zeros((mm.shape[1], mm.shape[2], mm.shape[0]))
for i in range(mm.shape[1]):
for j in range(mm.shape[2]):
for k in range(mm.shape[0]):
m1[i][j][k] = mm[k][i][j]
return m1
# CNN Layer 과정을 계산하고 결과를 그림으로 출력
def ConvDraw(p0, filters, size=(12, 8), k=-1):
f0, fg0 = ConvMax(p0, filters)
f0_img = to_img(f0)
fg1_img = to_img(fg0)
draw(f0, fg0, size, k)
p1 = join(fg0)
return p1
# 테스트 실행
nimg31 = np.random.rand(10, 10)
filters = [np.ones((3, 3))] * 3
m0 = ConvDraw(nimg31, filters, (12, 10), 0)
✅ Result : CNN Layer 구현
🍓 라즈베리파이 환경 구축
✅ 설치 및 이미지 설정
sudo apt install rpi-imager : 라즈베리파이 이미지 도구 설치
rpi-imager : GUI 실행 후 OS 이미지 다운로드 및 설치 가능
⚙️ 설정 정보
운영체제: Raspberry Pi OS (64-bit)
저장소: Mass Storage Device - 62.5 GB
-
Day5 : 선형모델, 신경망모델
📘 선형 모델 요약
🧠 1. 선형 모델 개요
입력 데이터를 벡터 형태로 처리하는 가장 단순한 형태의 머신러닝 모델
선형 변환 + 간단한 결정 함수로 분류 수행
🧱 2. 벡터화 (Vectorization)
선형 모델은 1차원 벡터 형태의 입력만 처리 가능
따라서, 2D/3D 이미지를 1D 벡터로 변환해야 함
예: 4x4 픽셀 이미지를 (1,16) 벡터로 변환
🧮 3. 선형 분류기 - Score 함수
입력 벡터와 가중치 행렬의 곱으로 각 클래스의 점수(score) 계산
Score 계산은 행렬 곱을 통해 병렬 처리 가능
📊 4. Softmax 분류기
각 클래스의 score를 확률 값으로 변환
softmax 출력은 각 클래스에 대한 확률 분포를 나타냄
📉 5. 손실 함수 - Cross Entropy Loss
예측 확률과 정답 클래스 간의 거리 계산
정답 클래스에 해당하는 softmax 값에 -log를 취해 손실 계산
⚙️ 6. 최적화 - SGD (Stochastic Gradient Descent)
전체 데이터를 한 번에 학습하지 않고, 미니 배치 단위로 경사 하강법 적용
계산 효율성과 빠른 수렴을 위해 사용됨
🧪 7. 실습 개요 (MNIST)
MNIST 숫자 이미지 데이터를 선형 모델에 적용하여 학습
정확도 및 손실 곡선을 시각화하여 학습 결과 분석
✅ 요약
선형 모델은 가장 기본적인 분류기이며, 기초 개념(벡터화, softmax, cross entropy, SGD 등)을 학습하는 데 중요함
이후 신경망 모델을 이해하기 위한 기반 지식을 제공함
👨💻 실습
💡 Code : 벡터화 코드
이미지를 벡터화할 때, numpy를 사용하는 경우 flatten 또는 reshape을 사용해 벡터화 할 수 있다.
import numpy as np
# random 함수로 0~255 사이의 임의의 정수를 성분으로 갖는 4x4 행렬을 만든다.
a = np.random.randint(0, 255, (4, 4))
a
array([[ 38, 223, 157, 213],
[104, 79, 231, 31],
[117, 10, 48, 72],
[128, 41, 6, 178]])
# flatten을 사용해 1차원 행렬(벡터)로 만든다.
b = a.flatten()
b
array([ 38, 223, 157, 213, 104, 79, 231, 31, 117, 10, 48, 72, 128,
41, 6, 178])
# reshape를 사용해 행렬 크기를 바꾼다. -1은 자동으로 계산한다는 의미이고 이 경우 16을 적는 것과 같다.
# 만약 (2, 8)의 행렬로 바꾸려 한다면 reshape(2, -1) 또는 reshape(2, 8) 둘 다 같은 결과이다.
c = a.reshape(-1)
c
array([ 38, 223, 157, 213, 104, 79, 231, 31, 117, 10, 48, 72, 128,
41, 6, 178])
💡 Code : Mnist 실습
# 1. 기본 라이브러리 불러오기
import numpy as np
import pandas as pd
# 2 데이터셋 불러오기
from tensorflow.keras.datasets.mnist import load_data
(train_x, train_y), (test_x, test_y) = load_data()
# 2-1 데이터 확인하기
train_x.shape, train_y.shape # train 데이터 크기 확인
test_x.shape, test_y.shape # test 데이터 크기 확인
((10000, 28, 28), (10000,))
# 2-2 이미지 확인하기
from PIL import Image
img = train_x[0]
import matplotlib.pyplot as plt
img1 = Image.fromarray(img, mode='L')
plt.imshow(img1)
train_y[0] # 첫번째 데이터 확인
np.uint8(5)
# 3 데이터 전처리
# 3-1 입력 형태 변환: 3차원 → 2차원
# 데이터를 2차원 형태로 변환: 입력 데이터가 선형모델에서는 벡터 형태
train_x1 = train_x.reshape(60000, -1)
test_x1 = test_x.reshape(10000, -1)
# 3-2 데이터 값의 크기 조절: 0~1 사이 값으로 변환
train_x2 = train_x1 / 255
test_x2 = test_x1 / 255
# 4 모델 설정
# 4-1 모델 설정용 라이브러리 불러오기
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
# 4-2 모델 설정
md = Sequential()
md.add(Dense(10, activation='softmax', input_shape=(28*28,)))
md.summary() # 모델 요약
/usr/local/lib/python3.11/dist-packages/keras/src/layers/core/dense.py:87: UserWarning: Do not pass an `input_shape`/`input_dim` argument to a layer. When using Sequential models, prefer using an `Input(shape)` object as the first layer in the model instead.
super().__init__(activity_regularizer=activity_regularizer, **kwargs)
Model: "sequential_6"
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓
┃ Layer (type) ┃ Output Shape ┃ Param # ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩
│ dense_6 (Dense) │ (None, 10) │ 7,850 │
└─────────────────────────────────┴────────────────────────┴───────────────┘
Total params: 7,850 (30.66 KB)
Trainable params: 7,850 (30.66 KB)
Non-trainable params: 0 (0.00 B)
# 5 모델 학습 진행
# 5-1 모델 compile: 손실 함수, 최적화 함수, 측정 함수 설정
md.compile(loss = 'sparse_categorical_crossentropy', optimizer = 'sgd', metrics = ['acc'])
# 5-2 모델 학습: 학습 횟수, batch_size, 검증용 데이터 설정
hist = md.fit(train_x2, train_y, epochs=30, batch_size=64, validation_split=0.2)
acc = hist.history['acc']
val_acc = hist.history['val_acc']
epoch = np.arange(1, len(acc) + 1)
# 학습결과 분석 : 학습 곡선 그리기
plt.figure(figsize=(10,8))
plt.plot(epoch, acc, 'b', label='Training accuracy')
plt.plot(epoch, val_acc, 'r', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.xlabel('Epochs')
plt.ylabel('Accuracy')
plt.legend()
plt.show()
# 6 테스트용 데이터 평가
md.evaluate(test_x2, test_y)
# 7 가중치 저장
weight = md.get_weights()
weight
[array([[-0.01809908, 0.04423299, -0.00407743, ..., 0.06332525,
-0.00251734, 0.00196751],
[-0.050407 , -0.05998353, -0.07465094, ..., -0.01433843,
0.01071206, 0.03646336],
[ 0.06986522, -0.0116923 , -0.07076468, ..., 0.02445704,
-0.05563192, 0.0041509 ],
...,
[ 0.03118712, -0.04921252, 0.00195412, ..., -0.00605295,
-0.00202944, 0.07754893],
[ 0.08052733, -0.0327304 , 0.02389491, ..., 0.00695625,
0.06758214, 0.03055982],
[-0.05485652, 0.03522244, 0.03506895, ..., 0.00976416,
-0.06175685, 0.04081956]], dtype=float32),
array([-0.23029914, 0.30129498, 0.01726046, -0.17140533, 0.06494795,
0.772551 , -0.05629169, 0.3972938 , -0.94089097, -0.15446219],
dtype=float32)]
# Model Loss 시각화
plt.plot(hist.history['loss'], label='loss')
plt.plot(hist.history['val_loss'], label='val_loss')
plt.title('model loss')
plt.ylabel('loss')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
CIFAR10 실습
2025-06-27_2
보고서 참고
-
Day4 : Deep Learning
🧠 딥러닝 개요
딥러닝은 인간의 뇌를 모방한 인공신경망(ANN)을 기반으로 한 기계학습 기술로, 다층 구조의 신경망을 통해 복잡한 패턴을 학습하고 예측하는 데 사용된다.
🔍 인공신경망(ANN)의 개념
생물학적 뉴런 구조에서 착안.
입력 → 가중치 → 활성화 함수 → 출력 흐름으로 동작.
각 신호의 강도는 가중치(Weight)로 표현됨.
🧬 딥러닝(Deep Learning)이란?
은닉층이 여러 개인 심층 신경망(Deep Neural Network, DNN)을 통해 학습하는 방식.
심층 학습(Deep Learning)이라고도 함.
🛠️ 신경망 구성 요소
구성 요소
설명
입력층
학습 데이터가 들어오는 층
은닉층
가중합 계산 및 비선형 변환 수행
출력층
최종 예측값을 출력
가중치
입력의 중요도를 결정
편향
가중합에 더해지는 상수로 출력 조절
➕ 가중합 (Weighted Sum)
각 입력값 × 가중치 + 편향
수식: z = w₁x₁ + w₂x₂ + … + b
⚙️ 활성화 함수 (Activation Function)
함수명
특징
Sigmoid
S자 형태, 출력값 [0, 1], 기울기 소실 문제
Tanh
출력 [-1, 1], 평균 0, sigmoid보다 우수
ReLU
0 이하 → 0, 0 초과 → 그대로 출력, 빠른 학습
LeakyReLU
ReLU의 음수 입력 무반응 문제 해결
Softmax
확률 분포 출력, 다중 클래스 분류에 사용
🧭 학습 과정 (Training Flow)
1️⃣ 순전파 (Forward Propagation)
입력 → 은닉층 → 출력층으로 예측값 도출
2️⃣ 손실 함수 (Loss Function)
예측값과 실제값의 차이를 계산
회귀: MSE
분류: Cross Entropy
3️⃣ 옵티마이저 (Optimizer)
경사하강법 기반으로 가중치 최적화
전체/미니 배치 방식 사용
4️⃣ 역전파 (Backpropagation)
오차를 역방향 전파해 가중치 업데이트
각 층의 가중치에 대해 미분값 기반 보정
🧱 딥러닝 모델의 유형
유형
설명
DFN (순방향 신경망)
기본 구조, 고정 입력 처리
RNN (순환 신경망)
시계열 데이터 처리, 과거 정보 반영
LSTM
RNN 개선, 장기 기억 유지
CNN
이미지 분석 특화, 합성곱 및 풀링 활용
🧠 CNN의 구조
합성곱층: 필터를 통해 특징 추출
풀링층: 데이터 크기 축소, 핵심 정보 보존
완전연결층: 최종 분류 수행
🔄 비교 요약 (DFN vs RNN vs CNN)
항목
DFN
RNN
CNN
입력
정적
시계열
이미지/시계열
특징
단방향
순환 연결
지역적 특징
학습
쉬움
어려움
중간
효율
낮음
낮음
높음
💬 워드 임베딩 (Word Embedding)
방식
설명
One-hot Encoding
희소 벡터, 단순 구조
Word2Vec
주변 문맥 → 중심 단어 예측 (CBOW/Skip-gram)
TF-IDF
단어 중요도 가중치 부여
FastText
부분 단어 기반, OOV 문제 해결
GloVe
단어 동시 등장 통계 기반
ELMo
문맥에 따라 벡터가 달라지는 동적 임베딩
🎨 적대적 생성 신경망 (GAN)
두 네트워크가 경쟁:
Generator: 진짜 같은 가짜 데이터 생성
Discriminator: 진짜와 가짜 구별
예술, 이미지 생성 등에서 강력한 성능
✅ 요약
딥러닝은 인공신경망을 확장한 구조로, 다양한 문제 해결에 적용 가능
활성화 함수, 학습 알고리즘, 모델 구조에 따라 성능이 좌우됨
CNN, RNN, GAN, Word Embedding 등은 실전 문제에 맞는 딥러닝 기법 선택의 기준이 된다.
🛠️ 작업할 디렉토리 생성 및 환경 설정
# 1. 작업 디렉토리 생성
mkdir F_MNIST # 디렉토리 이름: F_MNIST
cd F_MNIST # 해당 디렉토리로 이동
# 2. 가상 환경 생성 및 활성화
python3 -m venv .fmnist # 가상 환경 생성 (폴더 이름: .fmnist)
source .fmnist/bin/activate # 가상 환경 활성화
# 3. 패키지 설치
pip install -U pip # pip 최신 버전으로 업그레이드
pip install tensorflow # TensorFlow (딥러닝 프레임워크)
pip install matplotlib # Matplotlib (시각화 라이브러리)
pip install PyQt5 # PyQt5 (Matplotlib GUI 백엔드용)
pip install scikit_learn # scikit-learn (머신러닝 및 평가 도구)
# 4. Qt GUI 백엔드 설정 (Wayland 환경에서 필수)
export QT_QPA_PLATFORM=wayland # Qt GUI를 Wayland에서 정상 동작하게 설정
👨💻 실습
💡 Code : Fashion MNIST
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# dataset load
fashion_mnist = keras.datasets.fashion_mnist
# spilt data (train / test)
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()
print(train_images.shape)
print(train_labels.shape)
print(test_images.shape)
print(test_labels.shape)
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
matplotlib.use('Qt5Agg')
NUM=20
plt.figure(figsize=(15,15))
plt.subplots_adjust(hspace=1)
for idx in range(NUM):
sp = plt.subplot(5,5,idx+1)
plt.imshow(train_images[idx])
plt.title(f'{class_names[train_labels[idx]]}')
plt.show()
plt.figure()
plt.imshow(train_images[0])
plt.colorbar()
plt.grid(False)
plt.show()
# 간단한 이미지 전처리 (for ANN)
train_images = train_images / 255.0
test_images = test_images / 255.0
class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']
plt.figure(figsize=(10,8))
for i in range(20):
plt.subplot(4,5,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(train_images[i], cmap=plt.cm.binary)
plt.xlabel(class_names[train_labels[i]])
plt.show()
model = keras.Sequential ([
keras.layers.Flatten(input_shape=(28,28)),
keras.layers.Dense(128, activation='relu'),
keras.layers.Dense(10, activation='softmax'),
])
model.summary()
model.compile(optimizer='adam',
loss='sparse_categorical_crossentropy',
metrics=['accuracy'])
model.fit(train_images, train_labels, epochs=20)
predictions = model.predict(test_images)
predictions[0]
np.argmax(predictions[0])
test_labels[0]
def plot_image(i, predictions_array, true_label, img):
predictions_array, true_label, img = predictions_array[i], true_label[i], img[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
plt.imshow(img, cmap=plt.cm.binary)
predicted_label = np.argmax(predictions_array)
if predicted_label == true_label:
color = 'blue'
else:
color = 'red'
plt.xlabel("{} {:2.0f}% ({})".format(class_names[predicted_label],
100*np.max(predictions_array),
class_names[true_label]),
color=color)
def plot_value_array(i, predictions_array, true_label):
predictions_array, true_label = predictions_array[i], true_label[i]
plt.grid(False)
plt.xticks([])
plt.yticks([])
thisplot = plt.bar(range(10), predictions_array, color="#777777")
plt.ylim([0, 1])
predicted_label = np.argmax(predictions_array)
thisplot[predicted_label].set_color('red')
thisplot[true_label].set_color('blue')
num_rows = 5
num_cols = 3
num_images = num_rows*num_cols
plt.figure(figsize=(2*2*num_cols, 2*num_rows))
for i in range(num_images):
plt.subplot(num_rows, 2*num_cols, 2*i+1)
plot_image(i, predictions, test_labels, test_images)
plt.subplot(num_rows, 2*num_cols, 2*i+2)
plot_value_array(i, predictions, test_labels)
plt.show()
from sklearn.metrics import accuracy_score
print('accuracy score : ', accuracy_score(tf.math.argmax(predictions, -1), test_labels))
-
Day3 : Perceptron
Report : Perceptron
1. 퍼셉트론이란?
생물학적 뉴런을 모방한 가장 기본적인 인공신경망
입력 값 × 가중치 + 바이어스를 통해 선형결합을 만들고, 단위 계단 함수(step function) 로 0 또는 1을 출력
간단한 구조지만, 선형 분리 가능한 문제는 완벽하게 해결할 수 있음
하나의 직선으로 나눌 수 있으면 → 선형 분리 가능
단위 계산 함수 : 입력값이 0보다 크면 1을 출력하고, 그 외엔 0을 출력하는 함수
2. 퍼셉트론 학습 방식 (코드 중심)
# AND & OR & NAND & XOR Gate Perceptron
import numpy as np
import matplotlib.pyplot as plt
class Perceptron:
def __init__(self, input_size, lr=0.1, epochs=10):
self.weights = np.zeros(input_size)
self.bias = 0
self.lr = lr
self.epochs = epochs
self.errors = []
def activation(self, x):
return np.where(x > 0, 1, 0)
def predict(self, x):
linear_output = np.dot(x, self.weights) + self.bias
return self.activation(linear_output)
def train(self, X, y):
for epoch in range(self.epochs):
total_error = 0
for xi, target in zip(X, y):
prediction = self.predict(xi)
update = self.lr * (target - prediction)
self.weights += update * xi
self.bias += update
total_error += int(update != 0.0)
self.errors.append(total_error)
print(f"Epoch {epoch+1}/{self.epochs}, Errors: {total_error}")
# AND 게이트 데이터 및 학습
X_and = np.array([[0,0],[0,1],[1,0],[1,1]])
y_and = np.array([0,0,0,1])
print(" AND Gate Training")
ppn_and = Perceptron(input_size=2)
ppn_and.train(X_and, y_and)
print("\n AND Gate Test:")
for x in X_and:
print(f"Input: {x}, Predicted Output: {ppn_and.predict(x)}")
# OR 게이트 데이터 및 학습
X_or = np.array([[0,0],[0,1],[1,0],[1,1]])
y_or = np.array([0,1,1,1])
print("\n OR Gate Training")
ppn_or = Perceptron(input_size=2)
ppn_or.train(X_or, y_or)
print("\n OR Gate Test:")
for x in X_or:
print(f"Input: {x}, Predicted Output: {ppn_or.predict(x)}")
# NAND 게이트 데이터 및 학습
X_nand = np.array([[0,0],[0,1],[1,0],[1,1]])
y_nand = np.array([1,1,1,0]) # AND와 반대
print("\n NAND Gate Training")
ppn_nand = Perceptron(input_size=2)
ppn_nand.train(X_nand, y_nand)
print("\n NAND Gate Test:")
for x in X_nand:
print(f"Input: {x}, Predicted Output: {ppn_nand.predict(x)}")
# XOR 게이트 데이터 및 학습
X_xor = np.array([[0,0],[0,1],[1,0],[1,1]])
y_xor = np.array([0,1,1,0]) # 선형 분리 불가능
print("\n XOR Gate Training")
ppn_xor = Perceptron(input_size=2)
ppn_xor.train(X_xor, y_xor)
print("\n XOR Gate Test:")
for x in X_xor:
print(f"Input: {x}, Predicted Output: {ppn_xor.predict(x)}")
2-1. train() 함수 호출
ppn_and.train(X_and, y_and)
X_and: 입력 데이터 배열 (예: [0,0], [1,1] 등)
y_and: 각 입력에 대한 정답 출력값
2-2. 전체 반복 (epoch) 시작
for epoch in range(self.epochs): # 총 10번 반복
한 epoch는 전체 데이터를 한 번 학습하는 주기
총 10번 반복하면서 조금씩 가중치를 조정
2-3. 한 epoch 내 샘플 반복
for xi, target in zip(X, y):
각 데이터 xi와 정답 target을 하나씩 꺼내 순차 학습
2-4. 예측 과정
prediction = self.predict(xi)
predict() 내부에서 다음 순서로 작동:
linear_output = w·x + b
activation() → 0 또는 1 반환
2-5. 오차 계산 및 가중치/바이어스 업데이트
update = self.lr * (target - prediction)
예측이 정답보다 작으면 → update > 0: 가중치 증가
예측이 정답보다 크면 → update < 0: 가중치 감소
예측이 정확하면 → update == 0: 가중치 변화 없음
self.weights += update * xi
self.bias += update
각 입력 값에 따라 가중치 조정
항상 바이어스도 같이 업데이트
2-6. 에러 카운트
total_error += int(update != 0.0)
예측이 틀렸을 때만 에러로 집계
2-7. 학습 결과 출력
self.errors.append(total_error)
print(f"Epoch {epoch+1}/{self.epochs}, Errors: {total_error}")
학습이 진행될수록 Errors가 줄어드는지 확인 가능
하지만 XOR은 줄지 않음 → 선형 분리 불가능 문제
2-8. 최종 예측 결과 확인
각 게이트에 대해 학습이 끝나면 다음을 수행:
for x in X_and:
print(f"Input: {x}, Predicted Output: {ppn_and.predict(x)}")
학습된 가중치로 새로운 입력을 테스트해보는 과정
2-9. 요약: 퍼셉트론 학습 흐름
입력 X, 정답 y
→ 가중합 계산 (w·x + b)
→ 계단 함수로 예측
→ 오차 계산 (target - predict)
→ w, b 업데이트
→ 에러 기록
→ epoch 반복
→ 학습 완료 후 테스트
3. XOR 게이트가 퍼셉트론으로 안 되는 이유
퍼셉트론은 직선 하나로 출력을 나누는 모델
XOR은 어떤 직선으로도 0과 1을 나눌 수 없음
즉, 선형 분리 불가능 문제 → 퍼셉트론 한계
해결책: 다층 퍼셉트론 (MLP)
비선형성을 처리하기 위해 은닉층 + 비선형 활성화 함수 (예: sigmoid, ReLU)를 활용하고,
오차 역전파(Backpropagation)로 학습함
-
Day3 : Perceptron
📌 Perceptron란?
퍼셉트론(Perceptron)은 생물학적 뉴런을 수학적으로 모델링한 인공 뉴런 모델로, 여러 입력 신호를 받아 각 입력에 대한 가중치(Weight)를 곱한 후, 이들의 가중합(Weighted Sum)을 계산하고, 활성화 함수(Activation Function)를 통해 최종 출력을 결정하는 구조이다.
🔧 구조 (Perceptron Structure)
입력(x) → 가중치(w) → 가중합(∑) → 활성화 함수(f) → 출력(y)
입력 (Input): AND, OR 등 논리 연산을 위한 입력 신호.
가중치 (Weight): 입력 신호의 중요도를 결정하며, 학습을 통해 조정됨.
가중합 (Weighted Sum): 각 입력과 그에 대응하는 가중치의 곱을 모두 더한 값.
활성화 함수 (Activation Function): 가중합이 임계값을 넘으면 1, 넘지 못하면 0을 출력하는 함수. 대표적으로 단위 계단 함수 사용.
출력 (Output): 최종 결과값 (보통 0 또는 1의 이진 출력).
🎯 요약
퍼셉트론은 이진 분류 문제를 해결할 수 있는 가장 기본적인 신경망 구조이다.
학습을 통해 입력 신호의 중요도를 나타내는 가중치가 조정된다.
단층 퍼셉트론은 선형 분리 가능한 문제만 해결할 수 있다.
👨💻 실습
💡 Code : AND & OR & NAND & XOR Gate Perceptron
# AND & OR & NAND & XOR Gate Perceptron
import numpy as np
import matplotlib.pyplot as plt
class Perceptron:
def __init__(self, input_size, lr=0.1, epochs=10):
self.weights = np.zeros(input_size)
self.bias = 0
self.lr = lr
self.epochs = epochs
self.errors = []
def activation(self, x):
return np.where(x > 0, 1, 0)
def predict(self, x):
linear_output = np.dot(x, self.weights) + self.bias
return self.activation(linear_output)
def train(self, X, y):
for epoch in range(self.epochs):
total_error = 0
for xi, target in zip(X, y):
prediction = self.predict(xi)
update = self.lr * (target - prediction)
self.weights += update * xi
self.bias += update
total_error += int(update != 0.0)
self.errors.append(total_error)
print(f"Epoch {epoch+1}/{self.epochs}, Errors: {total_error}")
# AND 게이트 데이터 및 학습
X_and = np.array([[0,0],[0,1],[1,0],[1,1]])
y_and = np.array([0,0,0,1])
print(" AND Gate Training")
ppn_and = Perceptron(input_size=2)
ppn_and.train(X_and, y_and)
print("\n AND Gate Test:")
for x in X_and:
print(f"Input: {x}, Predicted Output: {ppn_and.predict(x)}")
# OR 게이트 데이터 및 학습
X_or = np.array([[0,0],[0,1],[1,0],[1,1]])
y_or = np.array([0,1,1,1])
print("\n OR Gate Training")
ppn_or = Perceptron(input_size=2)
ppn_or.train(X_or, y_or)
print("\n OR Gate Test:")
for x in X_or:
print(f"Input: {x}, Predicted Output: {ppn_or.predict(x)}")
# NAND 게이트 데이터 및 학습
X_nand = np.array([[0,0],[0,1],[1,0],[1,1]])
y_nand = np.array([1,1,1,0]) # AND와 반대
print("\n NAND Gate Training")
ppn_nand = Perceptron(input_size=2)
ppn_nand.train(X_nand, y_nand)
print("\n NAND Gate Test:")
for x in X_nand:
print(f"Input: {x}, Predicted Output: {ppn_nand.predict(x)}")
# XOR 게이트 데이터 및 학습
X_xor = np.array([[0,0],[0,1],[1,0],[1,1]])
y_xor = np.array([0,1,1,0]) # 선형 분리 불가능
print("\n XOR Gate Training")
ppn_xor = Perceptron(input_size=2)
ppn_xor.train(X_xor, y_xor)
print("\n XOR Gate Test:")
for x in X_xor:
print(f"Input: {x}, Predicted Output: {ppn_xor.predict(x)}")
✅ Result : AND & OR & NAND & XOR Gate Perceptron
AND Gate Training
Epoch 1/10, Errors: 1
Epoch 2/10, Errors: 3
Epoch 3/10, Errors: 3
Epoch 4/10, Errors: 2
Epoch 5/10, Errors: 1
Epoch 6/10, Errors: 0
Epoch 7/10, Errors: 0
Epoch 8/10, Errors: 0
Epoch 9/10, Errors: 0
Epoch 10/10, Errors: 0
AND Gate Test:
Input: [0 0], Predicted Output: 0
Input: [0 1], Predicted Output: 0
Input: [1 0], Predicted Output: 0
Input: [1 1], Predicted Output: 1
OR Gate Training
Epoch 1/10, Errors: 1
Epoch 2/10, Errors: 2
Epoch 3/10, Errors: 1
Epoch 4/10, Errors: 0
Epoch 5/10, Errors: 0
Epoch 6/10, Errors: 0
Epoch 7/10, Errors: 0
Epoch 8/10, Errors: 0
Epoch 9/10, Errors: 0
Epoch 10/10, Errors: 0
OR Gate Test:
Input: [0 0], Predicted Output: 0
Input: [0 1], Predicted Output: 1
Input: [1 0], Predicted Output: 1
Input: [1 1], Predicted Output: 1
NAND Gate Training
Epoch 1/10, Errors: 2
Epoch 2/10, Errors: 3
Epoch 3/10, Errors: 3
Epoch 4/10, Errors: 0
Epoch 5/10, Errors: 0
Epoch 6/10, Errors: 0
Epoch 7/10, Errors: 0
Epoch 8/10, Errors: 0
Epoch 9/10, Errors: 0
Epoch 10/10, Errors: 0
NAND Gate Test:
Input: [0 0], Predicted Output: 1
Input: [0 1], Predicted Output: 1
Input: [1 0], Predicted Output: 1
Input: [1 1], Predicted Output: 0
XOR Gate Training
Epoch 1/10, Errors: 2
Epoch 2/10, Errors: 3
Epoch 3/10, Errors: 4
Epoch 4/10, Errors: 4
Epoch 5/10, Errors: 4
Epoch 6/10, Errors: 4
Epoch 7/10, Errors: 4
Epoch 8/10, Errors: 4
Epoch 9/10, Errors: 4
Epoch 10/10, Errors: 4
XOR Gate Test:
Input: [0 0], Predicted Output: 1
Input: [0 1], Predicted Output: 1
Input: [1 0], Predicted Output: 0
Input: [1 1], Predicted Output: 0
💡 Code : 경계 결정 시각화 함수 (AND, OR, NAND, XOR)
# 경계 결정 시각화 함수 (AND, OR, NAND, XOR)
from matplotlib.colors import ListedColormap
import matplotlib.pyplot as plt
import numpy as np
def plot_decision_boundary(X, y, model, title='Perceptron Decision Boundary'):
cmap_light = ListedColormap(['#FFDDDD', '#DDDDFF']) # 배경 색상
cmap_bold = ListedColormap(['#FF0000', '#0000FF']) # 점 색상
h = .02 # mesh grid 간격
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure(figsize=(6, 5))
plt.contourf(xx, yy, Z, cmap=cmap_light)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=cmap_bold,
edgecolor='k', s=100, marker='o')
plt.xlabel('Input 1')
plt.ylabel('Input 2')
plt.title(title)
plt.grid(True)
plt.show()
# AND 게이트 결정 경계 시각화
plot_decision_boundary(X_and, y_and, ppn_and, title='AND Gate Decision Boundary')
# OR 게이트 결정 경계 시각화
plot_decision_boundary(X_or, y_or, ppn_or, title='OR Gate Decision Boundary')
# NAND 게이트 결정 경계 시각화
plot_decision_boundary(X_nand, y_nand, ppn_nand, title='NAND Gate Decision Boundary')
# XOR 게이트 결정 경계 시각화
plot_decision_boundary(X_xor, y_xor, ppn_xor, title='XOR Gate Decision Boundary')
✅ Result : 경계 결정 시각화 함수 (AND, OR, NAND, XOR)
💡 Code : # 오류 시각화 (AND, OR, NAND, XOR)
# 오류 시각화 (AND, OR, NAND, XOR)
plt.figure(figsize=(8, 5))
plt.plot(range(1, len(ppn_and.errors) + 1), ppn_and.errors, marker='o', label='AND Gate')
plt.plot(range(1, len(ppn_or.errors) + 1), ppn_or.errors, marker='s', label='OR Gate')
plt.plot(range(1, len(ppn_nand.errors) + 1), ppn_nand.errors, marker='^', label='NAND Gate')
plt.plot(range(1, len(ppn_xor.errors) + 1), ppn_xor.errors, marker='x', label='XOR Gate')
plt.xlabel('Epochs')
plt.ylabel('Number of Errors')
plt.title('Perceptron Learning Error Over Epochs')
plt.legend()
plt.grid(True)
plt.show()
✅ Result : 오류 시각화 (AND, OR, NAND, XOR)
💬 Comment
퍼셉트론: 입력 벡터에 가중치를 곱한 합이 기준(0)을 넘는지 판단하고, 학습 과정에서는 틀린 만큼만 조정하며 선형 분리를 배우는 구조
XOR은 선형 분리 불가능한 문제이기 때문에
단층 퍼셉트론으로는 해결할 수 없다.
이를 해결하려면 다층 퍼셉트론(MLP)이나 비선형 변환이 필요하다.
💡 Code : MLP로 XOR 문제 해결
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
class MultiLayerPerceptron:
def __init__(self, input_size=2, hidden_size=4, output_size=1, lr=0.5, epochs=1000):
self.W1 = np.random.uniform(-1, 1, (input_size, hidden_size))
self.b1 = np.zeros((1, hidden_size))
self.W2 = np.random.uniform(-1, 1, (hidden_size, output_size))
self.b2 = np.zeros((1, output_size))
self.lr = lr
self.epochs = epochs
self.losses = []
def sigmoid(self, x):
return 1 / (1 + np.exp(-np.clip(x, -250, 250)))
def sigmoid_derivative(self, x):
return x * (1 - x)
def forward(self, X):
self.z1 = np.dot(X, self.W1) + self.b1
self.a1 = self.sigmoid(self.z1)
self.z2 = np.dot(self.a1, self.W2) + self.b2
self.a2 = self.sigmoid(self.z2)
return self.a2
def backward(self, X, y, output):
m = X.shape[0]
dZ2 = output - y
dW2 = (1 / m) * np.dot(self.a1.T, dZ2)
db2 = (1 / m) * np.sum(dZ2, axis=0, keepdims=True)
dZ1 = np.dot(dZ2, self.W2.T) * self.sigmoid_derivative(self.a1)
dW1 = (1 / m) * np.dot(X.T, dZ1)
db1 = (1 / m) * np.sum(dZ1, axis=0, keepdims=True)
self.W2 -= self.lr * dW2
self.b2 -= self.lr * db2
self.W1 -= self.lr * dW1
self.b1 -= self.lr * db1
def train(self, X, y):
for epoch in range(self.epochs):
output = self.forward(X)
loss = np.mean((output - y) ** 2)
self.losses.append(loss)
self.backward(X, y, output)
#if epoch % 200 == 0:
# print(f"Epoch {epoch}/{self.epochs}, Loss: {loss:.6f}")
def predict(self, X):
output = self.forward(X)
return (output > 0.5).astype(int)
def predict_prob(self, X):
return self.forward(X).ravel() # 결정 경계용
# === XOR 데이터 ===
X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
y_xor = np.array([[0], [1], [1], [0]])
# === 학습 ===
print("\n=== XOR Gate Multi-Layer Perceptron Training ===")
mlp = MultiLayerPerceptron(input_size=2, hidden_size=2, lr=0.5, epochs=10000)
mlp.train(X_xor, y_xor)
# === 예측 결과 출력 ===
print("\nXOR GATE Test (Multi-Layer Perceptron):")
xor_predictions = mlp.predict(X_xor)
for i, x in enumerate(X_xor):
predicted = xor_predictions[i][0]
actual = y_xor[i][0]
result = "✓" if predicted == actual else "✗"
print(f"Input: {x}, Predicted: {predicted}, Actual: {actual}, {result}")
# === 결정 경계 시각화 함수 ===
def plot_decision_boundary(X, y, model, title="Decision Boundary"):
cmap_light = ListedColormap(['#FFDDDD', '#DDDDFF'])
cmap_bold = ListedColormap(['#FF0000', '#0000FF'])
h = .01
x_min, x_max = X[:, 0].min() - 0.5, X[:, 0].max() + 0.5
y_min, y_max = X[:, 1].min() - 0.5, X[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
np.arange(y_min, y_max, h))
grid = np.c_[xx.ravel(), yy.ravel()]
Z = model.predict_prob(grid)
Z = Z.reshape(xx.shape)
plt.figure(figsize=(6, 5))
plt.contourf(xx, yy, Z > 0.5, cmap=cmap_light)
plt.scatter(X[:, 0], X[:, 1], c=y.ravel(), cmap=cmap_bold,
edgecolor='k', s=120)
plt.title(title)
plt.xlabel("Input 1")
plt.ylabel("Input 2")
plt.grid(True)
plt.show()
# === 결정 경계 시각화 ===
plot_decision_boundary(X_xor, y_xor, mlp, title="XOR MLP Decision Boundary")
# === 손실 곡선 시각화 ===
plt.figure(figsize=(8, 5))
plt.plot(range(mlp.epochs), mlp.losses, color='purple')
plt.title("MLP Training Loss on XOR Problem")
plt.xlabel("Epochs")
plt.ylabel("Mean Squared Error")
plt.grid(True)
plt.show()
✅ Result : MLP로 XOR 문제 해결
=== XOR Gate Multi-Layer Perceptron Training ===
XOR GATE Test (Multi-Layer Perceptron):
Input: [0 0], Predicted: 0, Actual: 0, ✓
Input: [0 1], Predicted: 1, Actual: 1, ✓
Input: [1 0], Predicted: 1, Actual: 1, ✓
Input: [1 1], Predicted: 0, Actual: 0, ✓
-
Day2 : OpenCV
📌 OpenCV란?
OpenCV(Open Source Computer Vision Library)는 실시간 컴퓨터 비전 및 머신러닝을 위한 오픈소스 라이브러리입니다.
다양한 이미지/비디오 처리 기능을 제공하며, Python, C++, Java 등 다양한 언어에서 사용 가능합니다.
🚀 CUDA 모듈의 역할
GPU 가속을 활용한 고속 이미지 처리 수행
OpenCV의 일부 함수들은 CUDA를 통해 병렬 처리되어 성능을 향상시킴
사용 예: cv2.cuda.GpuMat, cv2.cuda.filter2D(), cv2.cuda.resize() 등
🛠️ 작업할 디렉토리 생성 및 환경 설정
# 1. 작업 디렉토리 생성
mkdir opencv # 디렉토리 이름: opencv
cd opencv # 해당 디렉토리로 이동
# 2. 가상 환경 생성 및 활성화
python3 -m venv .env # 가상 환경 생성 (폴더 이름: .env)
source .env/bin/activate # 가상 환경 활성화
# 3. 패키지 설치
pip install opencv-python # OpenCV 기본 기능(core, imgproc 등)
pip install opencv-contrib-python # 추가 모듈(contrib 포함)
pip install -U pip # pip 최신 버전으로 업그레이드
✅ 설치 확인 (Python 인터프리터 실행)
>>> import numpy as np
>>> import cv2
>>> np.__version__
'2.2.6' # 설치된 NumPy 버전 출력
>>> cv2.__version__
'4.11.0' # 설치된 OpenCV 버전 출력
>>> exit() # Python 인터프리터 종료
🎨 색상 정보
🔗 참고 사이트
W3Schools - RGB Colors
🌈 RGB (Red, Green, Blue)
각 색상 채널: 0~255 (8bit)
R (Red): 8bit
G (Green): 8bit
B (Blue): 8bit
픽셀 1개 = 24bit (8bit × 3)
🎨 HSL (Hue, Saturation, Lightness)
H: 색상 (Hue) → 0 ~ 360°
S: 채도 (Saturation) → 0 ~ 100%
L: 밝기 (Lightness) → 0 ~ 100%
🔄 RGB vs HSL 차이점
항목
RGB
HSL
구성
Red, Green, Blue (각 0~255)
Hue (0~360), Saturation & Lightness (0~100%)
직관성
컴퓨터에서 사용하기 적합
사람이 색을 이해하기 쉬움
색 조절
색상 조정이 복잡함
채도/밝기 조절이 용이함
용도
디스플레이, 이미지 처리 등
디자인, 색상 선택 도구 등에 유용
✅ 요약:
RGB는 화면 출력/처리에 적합한 디지털 색 표현 방식
HSL은 색상 구성요소를 분리해 사람이 이해하거나 조절하기 쉬운 방식
📝 메모
vi ex1.py : python 스크립트 생성
python ex1.py : 생성된 스크립트 실행
jpg : 파일이 작고 속도가 빠르며, 주로 사진이나 웹 배경 이미지에 사용
png : 화질 보존, 투명 배경이 필요한 경우 사용
👨💻 실습
💡 Code : 이미지 Read / Write / Display
# ex1.py
import numpy as np
import cv2
# 이미지 파일을 Read
img = cv2.imread("Rengoku.jpg")
# Image 란 이름의 Display 창 생성
cv2.namedWindow("image", cv2.WINDOW_NORMAL)
# Numpy ndarray H/W/C order
print(img.shape)
# Read 한 이미지 파일을 Display
cv2.imshow("image", img)
# 별도 키 입력이 있을때 까지 대기
cv2.waitKey(0)
# ex1_output.jpg 로 읽은 이미지 파일을 저장
cv2.imwrite("ex1_output.jpg", img)
# Destory all windows
cv2.destroyAllWindows()
❓ Quiz: 이미지 Read / Write / Display
1. print(img.shape)의 출력 결과는 무슨 의미일까?
2. 본인이 좋아하는 사진을 web 에서 다운받아서 OpenCV API를 사용해서 Display 및 파일로 저장해보자.
3. 현재는 별도의 키 입력이 있을 때까지 cv2.waitKey(0) 함수에서 대기하게 된다. 코드를 추가해서 소문자 “s” 키를 입력받을 때만 이미지 파일을 저장하고 다른 키가 입력되면 이미지 파일을 저장하지 않게 수정해보자.
💡 Code : RGB/HSV Color Space (색 공간)
# ex2.py
import numpy as np
import cv2
# 이미지 파일을 Read 하고 Color space 정보 출력
color = cv2.imread("Rengoku.jpg", cv2.IMREAD_COLOR)
print(color.shape)
height,width,channels = color.shape
cv2.imshow("Original Image", color)
# Color channel 을 B,G,R 로 분할하여 출력
b,g,r = cv2.split(color)
rgb_split = np.concatenate((b,g,r),axis=1)
cv2.imshow("BGR Channels",rgb_split)
# 색공간을 BGR 에서 HSV 로 변환
hsv = cv2.cvtColor(color, cv2.COLOR_BGR2HSV)
# Channel 을 H,S,V 로 분할하여 출력
h,s,v = cv2.split(hsv)
hsv_split = np.concatenate((h,s,v),axis=1)
cv2.imshow("Split HSV", hsv_split)
❓ Quiz : RGB/HSV Color Space (색 공간)
1. 위 색공간 이미지의 링크로 이동해서 각 색 공간의 표현 방법을 이해해 보자.
2. HSV color space가 어떤 경우에 효과적으로 사용될까?
3. HSV로 변환된 이미지를 BGR이 아닌 RGB로 다시 변환해서 출력해 보자.
4. COLOR_RGB2GRAY를 사용해서 흑백으로 변환해 출력해 보자.
💡 Code : Crop / Resize (자르기 / 크기 조정)
# ex3.py
import numpy as np
import cv2
# 이미지 파일을 Read
img = cv2.imread("Rengoku.jpg")
# Crop 300x400 from original image from (100, 50)=(x, y)
# 세로(y): 100:500 → 500 - 100 = 400픽셀
# 가로(x): 500:1200 → 1200 - 500 = 700픽셀
cropped = img[100:500, 500:1200]
# Resize cropped image from 300x400 to 400x200
resized = cv2.resize(cropped, (800,200))
# Display all
cv2.imshow("Original", img)
cv2.imshow("Cropped image", cropped)
cv2.imshow("Resized image", resized)
cv2.imwrite("ex3_cropped.jpg", cropped)
cv2.imwrite("ex3_resized.jpg", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
❓ Quiz : Crop / Resize (자르기 / 크기 조정)
1. Input image 를 본인이 좋아하는 인물 사진으로 변경해서 적용하자. 그리고 본인이 사용한 input image 의 size 를 확인해 보자.
2. 본인이 사용한 이미지의 얼굴 영역만 crop 해서 display 해 보자.
3. 원본 이미지의 정확히 1.5배만큼 이미지를 확대해서 파일로 저장해 보자.
4. openCV 의 rotate API 를 사용해서 우측으로 90도만큼 회전된 이미지를 출력해 보자.
💡 Code : 역상 (Reverse Image)
# ex4.py
import numpy as np
import cv2
src = cv2.imread("Rengoku.jpg", cv2.IMREAD_COLOR)
dst = cv2.bitwise_not(src)
cv2.imshow("src", src)
cv2.imshow("dst", dst)
cv2.imwrite("ex4_reverse.jpg", dst)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 역상 (Reverse Image)
1. AND, OR, XOR 연산에 대해서 확인해 보자.
💡 Code : 이진화 (Binary)
# ex5.py
import numpy as np
import cv2
src = cv2.imread("Rengoku.jpg", cv2.IMREAD_COLOR)
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
ret, dst = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)
cv2.imshow("dst", dst)
cv2.imwrite("ex5_binary.jpg", dst)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 이진화 (Binary)
1. 임계값을 변화시켜 보자.
💡 Code : 흐림효과 (Blur)
# ex6.py
import numpy as np
import cv2
src = cv2.imread("Rengoku.jpg", cv2.IMREAD_COLOR)
dst = cv2.blur(src, (9, 9), anchor=(-1,- 1), borderType=cv2.BORDER_DEFAULT)
cv2.imshow("dst", dst)
cv2.imwrite("ex6_blur.jpg", dst)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 흐림효과 (Blur)
1. Kernel Size를 변경하여 보자.
2. borderType을 변경하여 보자.(cv2.BORDER_REFLECT)
💡 Code : 가장자리 검출 (Edge)
# ex7.py
import numpy as np
import cv2
src = cv2.imread("Rengoku.jpg", cv2.IMREAD_COLOR)
gray = cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)
sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, 3)
cv2.imshow("sobel", sobel)
cv2.imwrite("ex7_edge.jpg", sobel)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 가장자리 검출 (Edge)
1. Laplacian 변환을 적용해 보자.
2. Canny Edge Detection을 적용해 보자.
💡 Code : 배열 병합 (add Weighted)
# ex8.py
import numpy as np
import cv2
src = cv2.imread("RGB.png", cv2.IMREAD_COLOR)
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
# 1. Red 마스크 생성
lower_red = cv2.inRange(hsv, (0, 100, 100), (5, 255, 255))
upper_red = cv2.inRange(hsv, (170, 100, 100), (180, 255, 255))
mask_red = cv2.addWeighted(lower_red, 1.0, upper_red, 1.0, 0.0)
# 2. Green 마스크 생성
mask_green = cv2.inRange(hsv, (40, 100, 100), (85, 255, 255))
# 3. Blue 마스크 생성
mask_blue = cv2.inRange(hsv, (100, 100, 100), (130, 255, 255))
# 4. 각 색상 추출 (HSV → BGR 변환 포함)
red = cv2.bitwise_and(hsv, hsv, mask=mask_red)
green = cv2.bitwise_and(hsv, hsv, mask=mask_green)
blue = cv2.bitwise_and(hsv, hsv, mask=mask_blue)
red = cv2.cvtColor(red, cv2.COLOR_HSV2BGR)
green = cv2.cvtColor(green, cv2.COLOR_HSV2BGR)
blue = cv2.cvtColor(blue, cv2.COLOR_HSV2BGR)
# 5. 화면 출력
cv2.imshow("Original", src)
cv2.imshow("Red", red)
cv2.imshow("Green", green)
cv2.imshow("Blue", blue)
cv2.imwrite("ex8_original.png", src)
cv2.imwrite("ex8_red.png", red)
cv2.imwrite("ex8_green.png", green)
cv2.imwrite("ex8_blue.png", blue)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 배열 병합 (add Weighted)
1. lower_red 값의 범위를 변경해 보자.
2. upper_red 값의 범위를 변경해 보자.
3. addWeighted의 gamma 값을 변경해 보자.
💡 Code : 채널 분리 및 병합
# ex9.py
import numpy as np
import cv2
# 이미지 읽기
src = cv2.imread("RGB.png", cv2.IMREAD_COLOR)
# 채널 분리
b, g, r = cv2.split(src)
# 채널 순서 변경 (RGB처럼 보이게)
inverse = cv2.merge((r, g, b))
# 화면 출력
cv2.imshow("b", b)
cv2.imshow("g", g)
cv2.imshow("r", r)
cv2.imshow("inverse", inverse)
# 이미지 저장
cv2.imwrite("ex9_blue_gray.png", b)
cv2.imwrite("ex9_green_gray.png", g)
cv2.imwrite("ex9_red_gray.png", r)
cv2.imwrite("ex9_inverse.png", inverse)
cv2.waitKey()
cv2.destroyAllWindows()
❓ Quiz : 채널 분리 및 병합
1. Numpy 형태의 채널 분리를 적용해 보자.
b = src[:, :, 0]
g = src[:, :, 1]
r = src[:, :, 2]
2. 빈 이미지를 적용해 보자.
height, width, channel = src.shape
zero = np.zeros((height, width, 1), dtype=np.uint8)
bgz = cv2.merge((b, g, zero))
💡 Code : 동영상 파일을 읽고 보여주기
# ex10.py
import numpy as np
import cv2
cap = cv2.VideoCapture("son.mp4")
save_count = 1 # 저장할 이미지 번호 초기화
while cap.isOpened():
ret, frame = cap.read()
# (2) 프레임 읽기 실패 → 영상 끝 → 처음부터 다시
if not ret:
print("Restarting video...")
cap.set(cv2.CAP_PROP_POS_FRAMES, 0)
continue
# (3) 프레임 크기 50% 축소
resized = cv2.resize(frame, (0, 0), fx=0.5, fy=0.5)
# 출력
cv2.imshow("Resized Frame", resized)
# (1) 고정된 속도로 재생 (약 30fps)
key = cv2.waitKey(90)
# (4) 'c' 키 입력 시 이미지 저장
if key & 0xFF == ord('c'):
filename = f"{save_count:03}.jpg"
cv2.imwrite(filename, resized)
print(f"Saved {filename}")
save_count += 1
# 'q' 키 입력 시 종료
if key & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
❓ Quiz : 동영상 파일을 읽고 보여주기
1. 동영상이 너무 빠르게 재생된다. 이유를 찾아보고 정상적인 속도로 재생될 수 있도록 수정해 보자.
2. 동영상이 끝까지 재생되면 더 이상 frame을 읽지 못해 종료된다. 동영상이 끝까지 재생되면 다시 처음부터 반복될 수 있도록 수정해 보자.
3. 동영상 크기를 반으로 resize해서 출력해 보자.
4. 동영상 재생 중 'c' 키 입력을 받으면 해당 프레임을 이미지 파일로 저장하는 코드를 작성해 보자. 파일 이름은 001.jpg, 002.jpg 등으로 overwrite 되지 않게 하자.
💡 Code : 카메라로부터 input 을 받아 보여주고 동영상 파일로 저장하기
# ex11.py
import numpy as np
import cv2
# Read from the first camera device
cap = cv2.VideoCapture(0)
w = 640 #1280#1920
h = 480 #720#1080
cap.set(cv2.CAP_PROP_FRAME_WIDTH, w)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, h)
# 성공적으로 video device 가 열렸으면 while 문 반복
while(cap.isOpened()):
# 한 프레임을 읽어옴
ret, frame = cap.read()
if ret is False:
print("Can't receive frame (stream end?). Exiting ...")
break
# Display
cv2.imshow("Camera", frame)
# 1 ms 동안 대기하며 키 입력을 받고 'q' 입력 시 종료
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
❓ Quiz : 카메라로부터 input 을 받아 보여주고 동영상 파일로 저장하기
1. 가지고 있는 카메라의 지원 가능한 해상도를 확인 후 카메라 해상도를 변경해 보자.
2. 카메라 Input을 "output.mp4" 동영상 파일로 저장하도록 코드를 추가해 보자.
📝 메모
sudo apt install v4l-utils : 카메라 지원 해상도 확인용 도구 설치
v4l2-ctl -d /dev/video0 –list-formats-ext : 해당 카메라의 해상도 및 포맷 목록 출력
💡 Code : Text / Line / Ractangle
# ex12.py
import numpy as np
import cv2
cap = cv2.VideoCapture(0)
# 동그라미를 그릴 좌표를 저장할 리스트
circle_centers = []
def draw_circle(event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
# 마우스 왼쪽 버튼 클릭 시 좌표 저장
circle_centers.append((x, y))
cv2.namedWindow("Camera")
cv2.setMouseCallback("Camera", draw_circle)
topLeft = (50+100, 50)
bottomRight = (300+100, 300)
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Line
cv2.line(frame,
(topLeft[0] + 80, topLeft[1]),
(bottomRight[0] + 80, bottomRight[1]),
(0, 255, 0), 3)
# Rectangle
cv2.rectangle(frame,
[pt+80 for pt in topLeft], [pt+50 for pt in bottomRight], (255, 0, 255), 3)
# Text
font = cv2.FONT_ITALIC
cv2.putText(frame, 'hhhong',
[pt-180 for pt in bottomRight], font, 2, (0, 255, 255), 5)
# 저장된 좌표에 동그라미 그리기
for center in circle_centers:
cv2.circle(frame, center, 30, (255, 255, 0), 3) # 반지름 30, 두께 3, 색상 (BGR)
cv2.imshow("Camera", frame)
key = cv2.waitKey(1)
if key & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
❓ Quiz : Text / Line / Ractangle
1. Text 문구 / Font / 색상 / 크기 / 굵기 / 출력위치 등 모든 값을 변경해 보자.
2. 동그라미를 그리는 함수를 찾아서 적용해 보자.
3. 마우스 왼쪽 버튼을 click 하면 해당 위치에 동그라미가 그려지도록 코드를 추가해 보자.
(Reference : cv2.EVENT_LBUTTONDOWN)
💡 Code : Trackbar
# ex13.py
import cv2
topLeft = (50, 50)
bold = 0
r, g, b = 255, 255, 0 # 초기 텍스트 색상: 노란색 (BGR = (0, 255, 255))
# bold 트랙바 콜백
def on_bold_trackbar(value):
global bold
bold = value
global r
r = value
def on_g_trackbar(value):
global g
g = value
def on_b_trackbar(value):
global b
b = value
# 카메라 연결
cap = cv2.VideoCapture(0)
# 윈도우 및 트랙바 생성
cv2.namedWindow("Camera")
cv2.createTrackbar("bold", "Camera", bold, 30, on_bold_trackbar)
cv2.createTrackbar("R", "Camera", r, 255, on_r_trackbar)
cv2.createTrackbar("G", "Camera", g, 255, on_g_trackbar)
cv2.createTrackbar("B", "Camera", b, 255, on_b_trackbar)
# 루프 시작
while cap.isOpened():
ret, frame = cap.read()
if not ret:
print("Can't receive frame (stream end?). Exiting ...")
break
# 텍스트 출력 (트랙바에서 받아온 bold, color 값 사용)
cv2.putText(frame, "hhhong",
topLeft,
cv2.FONT_HERSHEY_SIMPLEX,
2,
(b, g, r), # BGR
1 + bold)
# 프레임 출력
cv2.imshow("Camera", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# 종료 처리
cap.release()
cv2.destroyAllWindows()
❓ Quiz: Trackbar
1. Trackbar를 control해서 TEXT의 굵기가 변하는 것을 확인해 보자.
2. Trackbar를 추가해서 font size를 변경 / 적용해 보자.
3. R/G/B Trackbar를 각각 추가해서 글자의 font color를 변경해 보자.
-
Touch background to close