Py
수업 시작 전 · Python 3.12.10 설치
Windows 64비트 기준입니다. 수업용 PC의 Python 버전을 동일하게 맞추면 실습 오류를 줄일 수 있습니다.
기존 버전 확인: 이미 Python이 설치되어 있다면 CMD에서 py -0p를 입력해 설치된 버전을 확인할 수 있습니다. 다른 버전은 삭제하지 않아도 되며, 수업에서는 py -3.12를 사용합니다.
1
설치 파일 다운로드python-3.12.10-amd64.exe 파일을 다운로드한 뒤 실행합니다.2
PATH 항목 체크설치 첫 화면 아래의 Add python.exe to PATH를 반드시 체크합니다.3
Install NowInstall Now를 눌러 설치하고, 완료 화면이 나타날 때까지 기다립니다.4
CMD에서 확인새 CMD를 열고 py -3.12 --version을 입력해 Python 3.12.10이 표시되는지 확인합니다.중요: 기존에 다른 Python 버전이 설치되어 있어도 먼저 삭제할 필요는 없습니다. Python 3.12.10 설치 후 기존 CMD 창을 닫고 새 CMD 창을 연 다음 py -3.12 방식으로 수업을 진행합니다. 여러 버전이 설치된 PC에서도 3.12 버전을 정확히 지정할 수 있습니다.
오늘 만들 프로그램
1. 이미지셋 준비robogram.net 데이터 저장소에서 사물 이미지셋을 다운로드합니다.
2. 모델 학습이미지셋으로 사물 인식 모델 파일을 만듭니다.
3. 인식률 측정노트북 웹캠으로 목표 사물의 인식률을 측정합니다.
중요: 이 수업에서는 아두이노 제어를 하지 않습니다. 노트북 웹캠과 Python으로만 실습합니다.
1단계. 실습 폴더 만들기
폴더 위치: 학교 PC의 바탕화면(Desktop)이 OneDrive 등으로 변경된 경우를 피하기 위해 실습 폴더를 %USERPROFILE% 아래에 만듭니다. 예: C:\Users\학생계정\object_recognition_laptop_app
Windows CMD 폴더 생성 명령어
cd %USERPROFILE%
mkdir object_recognition_laptop_app
cd object_recognition_laptop_app
mkdir obj_data
mkdir model
dir2단계. Python과 필요한 라이브러리 설치
설치는 현재 폴더 안이 아니라 노트북의 Python 환경에 됩니다. 학교 PC에서는 pip 대신 py -3.12 -m pip 방식으로 설치하는 것이 더 안정적입니다.
권장 환경: Python 3.12.10 64-bit를 권장합니다. 설치 후 CMD를 새로 열고 아래 명령어를 순서대로 실행하세요.
Python / pip 확인
py -3.12 --version
py -3.12 -m pip --versionpip 업데이트
py -3.12 -m pip install --upgrade pip필요한 라이브러리 설치
py -3.12 -m pip install numpy
py -3.12 -m pip install opencv-contrib-python==4.11.0.86
py -3.12 -m pip install scikit-learn joblib참고: 이 교재의 사물 인식 코드는 OpenCV 기본 기능만 사용해 opencv-python만으로도 충분하지만, opencv-contrib-python으로 통일해서 설치합니다. 두 패키지를 같이 설치하면 서로 충돌해 cv2가 깨지는 경우가 있어, 1단계(얼굴인식)·3단계(페르소나)와 버전까지 하나로(4.11.0.86) 맞추는 것입니다. 최신 5.0.0 계열은 Haar Cascade XML 파일이 기본 포함되지 않을 수 있습니다.
설치 확인 명령어
py -3.12 -c "import cv2, numpy, sklearn, joblib; print('설치 확인 완료'); print('OpenCV:', cv2.__version__); print('NumPy:', numpy.__version__)"설치가 모두 실패한다면: Python 버전 문제보다 학교 네트워크, 방화벽, 프록시 또는 PyPI 접속 제한 때문일 수 있습니다. Retrying, ProxyError, CERTIFICATE_VERIFY_FAILED, Could not fetch URL 같은 메시지가 나오면 네트워크 환경을 확인하세요.
3단계. robogram.net 데이터 저장소에서 이미지셋 다운로드
- robogram.net에 접속합니다.
- 수업에서 안내받은 데이터 저장소 메뉴 또는 링크로 이동합니다.
- 사물 인식 이미지셋을 다운로드합니다. 예: obj_data.zip
- 압축을 풀어 obj_data 폴더 안에 사물별 폴더가 보이게 정리합니다.
폴더 이름은 모델의 정답 이름입니다. 예: obj_data/keyboard, obj_data/mouse
권장 이미지 수: 사물별로 최소 20~30장 이상을 준비하고, 가능하면 30~50장 정도의 다양한 사진을 사용하는 것이 좋습니다. 이미지 수가 너무 적으면 학습 데이터와 평가 데이터가 겹쳐 실제보다 높은 정확도가 나올 수 있습니다.
4단계 캡스톤을 이어서 할 계획이라면 꼭 읽어주세요: 캡스톤에서는 물건을 손으로 들고 카메라에 보여줍니다. 그런데 책상 위에 물건만 놓고 찍은 사진으로 학습하면, 실제로 손에 들었을 때(손·전선 등이 함께 찍힘) 잘 못 맞히는 경우가 많습니다. 사진의 절반 정도는 책상 위, 나머지 절반은 손으로 들고 있는 모습으로 섞어서 찍으면 캡스톤에서 훨씬 더 정확하게 인식됩니다.
데이터 폴더 확인 명령어
cd %USERPROFILE%\object_recognition_laptop_app
dir obj_data4단계. 웹캠 테스트 파일 만들기
파일 만드는 방법: CMD에서 notepad 0_webcam_test.py를 실행해 새 파일을 만들고, 아래 전체 코드를 붙여넣은 뒤 Ctrl+S로 저장합니다. 메모장을 닫고 CMD에서 py -3.12 0_webcam_test.py으로 실행합니다.
파일 만들기 명령어
cd %USERPROFILE%\object_recognition_laptop_app
notepad 0_webcam_test.py0_webcam_test.py 전체 코드
import cv2
# 0단계: 노트북 웹캠 테스트
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not cap.isOpened():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("웹캠을 열 수 없습니다.")
print("Windows 설정 > 개인정보 및 보안 > 카메라 권한을 확인하세요.")
print("Zoom, Teams, 카메라 앱처럼 웹캠을 사용 중인 프로그램도 닫아주세요.")
exit()
print("웹캠 테스트를 시작합니다.")
print("웹캠 화면이 뜨면 성공입니다. 종료하려면 q 키를 누르세요.")
while True:
ret, frame = cap.read()
if not ret:
print("웹캠 화면을 가져오지 못했습니다.")
break
cv2.imshow("Webcam Test", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
실행 명령어
py -3.12 0_webcam_test.py5단계. 모델 학습 파일 만들기
파일 만드는 방법: CMD에서 notepad 1_train_object_model.py를 실행해 새 파일을 만들고, 아래 전체 코드를 붙여넣은 뒤 Ctrl+S로 저장합니다. 메모장을 닫고 CMD에서 py -3.12 1_train_object_model.py으로 실행합니다.
파일 만들기 명령어
cd %USERPROFILE%\object_recognition_laptop_app
notepad 1_train_object_model.py1_train_object_model.py 전체 코드
import os
import cv2
import joblib
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
# 1단계: robogram.net 이미지셋으로 사물 인식 모델 학습
DATA_DIR = "obj_data"
MODEL_DIR = "model"
MODEL_PATH = os.path.join(MODEL_DIR, "object_model.joblib")
LABEL_PATH = os.path.join(MODEL_DIR, "labels.txt")
REPORT_PATH = os.path.join(MODEL_DIR, "training_report.txt")
os.makedirs(MODEL_DIR, exist_ok=True)
IMAGE_EXTS = (".jpg", ".jpeg", ".png", ".bmp", ".webp")
def extract_features(image):
# 색상 특징 + 모양 특징을 숫자로 변환합니다.
image = cv2.resize(image, (96, 96))
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1, 2], None, [8, 8, 8], [0, 180, 0, 256, 0, 256])
hist = cv2.normalize(hist, hist).flatten()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (64, 64))
hog = cv2.HOGDescriptor(_winSize=(64,64), _blockSize=(16,16), _blockStride=(8,8), _cellSize=(8,8), _nbins=9)
hog_feature = hog.compute(gray).flatten()
return np.concatenate([hist, hog_feature])
def load_dataset():
X, y = [], []
if not os.path.exists(DATA_DIR):
print("obj_data 폴더가 없습니다. 이미지셋을 obj_data 폴더 안에 정리하세요.")
exit()
class_names = sorted([name for name in os.listdir(DATA_DIR) if os.path.isdir(os.path.join(DATA_DIR, name))])
if len(class_names) == 0:
print("obj_data 안에 사물별 폴더가 없습니다. 예: obj_data/bag, obj_data/keyboard")
exit()
print("발견한 사물 클래스:", class_names)
for class_name in class_names:
class_dir = os.path.join(DATA_DIR, class_name)
count = 0
for file_name in os.listdir(class_dir):
if not file_name.lower().endswith(IMAGE_EXTS):
continue
image_path = os.path.join(class_dir, file_name)
image = cv2.imread(image_path)
if image is None:
continue
X.append(extract_features(image))
y.append(class_name)
count += 1
print(f"{class_name}: {count}장")
if len(X) == 0:
print("학습할 이미지가 없습니다.")
exit()
return np.array(X), np.array(y), class_names
X, y, class_names = load_dataset()
print("\n총 이미지 수:", len(X))
print("클래스 수:", len(class_names))
try:
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25, random_state=42, stratify=y)
except ValueError:
print("클래스별 이미지 수가 적어 전체 데이터로 학습/평가합니다.")
print("주의: 이 경우 학습에 사용한 이미지를 다시 평가하므로 실제 성능보다 높게 나올 수 있습니다.")
print("사물별 이미지를 20~30장 이상으로 늘린 뒤 다시 실행하는 것을 권장합니다.")
X_train, X_test, y_train, y_test = X, X, y, y
model = KNeighborsClassifier(n_neighbors=3, weights="distance")
model.fit(X_train, y_train)
pred = model.predict(X_test)
acc = accuracy_score(y_test, pred)
report = classification_report(y_test, pred, zero_division=0)
cm = confusion_matrix(y_test, pred, labels=class_names)
print("\n==============================")
print("모델 학습 완료")
print(f"테스트 인식률: {acc * 100:.2f}%")
print("==============================")
print("\n[분류 리포트]\n", report)
print("[혼동 행렬]")
print(class_names)
print(cm)
joblib.dump({"model": model, "class_names": class_names, "feature_info": "HSV color histogram + HOG"}, MODEL_PATH)
with open(LABEL_PATH, "w", encoding="utf-8") as f:
for name in class_names:
f.write(name + "\n")
with open(REPORT_PATH, "w", encoding="utf-8") as f:
f.write(f"테스트 인식률: {acc * 100:.2f}%\n\n")
f.write("[클래스 목록]\n" + str(class_names) + "\n\n")
f.write("[분류 리포트]\n" + report + "\n")
f.write("[혼동 행렬]\n" + str(cm) + "\n")
print("\n저장된 모델 파일:", MODEL_PATH)
print("저장된 라벨 파일:", LABEL_PATH)
print("저장된 리포트:", REPORT_PATH)
모델 학습 실행 명령어
cd %USERPROFILE%\object_recognition_laptop_app
py -3.12 1_train_object_model.py성공하면 model/object_model.joblib, model/labels.txt, model/training_report.txt가 만들어집니다.
6단계. 실시간 사물 인식 파일 만들기
파일 만드는 방법: CMD에서 notepad 2_live_object_recognition.py를 실행해 새 파일을 만들고, 아래 전체 코드를 붙여넣은 뒤 Ctrl+S로 저장합니다. 메모장을 닫고 CMD에서 py -3.12 2_live_object_recognition.py으로 실행합니다.
파일 만들기 명령어
cd %USERPROFILE%\object_recognition_laptop_app
notepad 2_live_object_recognition.py2_live_object_recognition.py 전체 코드
import os
import cv2
import joblib
import numpy as np
# 2단계: 학습된 모델로 노트북 웹캠 실시간 사물 인식
MODEL_PATH = "model/object_model.joblib"
if not os.path.exists(MODEL_PATH):
print("모델 파일이 없습니다. 먼저 py -3.12 1_train_object_model.py 를 실행하세요.")
exit()
bundle = joblib.load(MODEL_PATH)
model = bundle["model"]
class_names = bundle["class_names"]
def extract_features(image):
image = cv2.resize(image, (96, 96))
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1, 2], None, [8, 8, 8], [0, 180, 0, 256, 0, 256])
hist = cv2.normalize(hist, hist).flatten()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (64, 64))
hog = cv2.HOGDescriptor(_winSize=(64,64), _blockSize=(16,16), _blockStride=(8,8), _cellSize=(8,8), _nbins=9)
return np.concatenate([hist, hog.compute(gray).flatten()])
def predict_object(image):
feature = extract_features(image).reshape(1, -1)
pred = model.predict(feature)[0]
probs = model.predict_proba(feature)[0]
confidence = float(np.max(probs)) * 100
return pred, confidence
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not cap.isOpened():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("웹캠을 열 수 없습니다.")
exit()
print("실시간 사물 인식을 시작합니다. 물체를 화면 가운데 사각형 안에 보여주세요. q: 종료")
while True:
ret, frame = cap.read()
if not ret:
print("웹캠 화면을 가져오지 못했습니다.")
break
h, w = frame.shape[:2]
box_size = min(w, h) // 2
x1, y1 = w//2 - box_size//2, h//2 - box_size//2
x2, y2 = x1 + box_size, y1 + box_size
roi = frame[y1:y2, x1:x2]
pred, confidence = predict_object(roi)
cv2.rectangle(frame, (x1,y1), (x2,y2), (0,255,0), 2)
cv2.putText(frame, f"{pred} / {confidence:.1f}%", (x1, y1-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0,255,0), 2)
cv2.putText(frame, "Put object inside the box. Press q to quit.", (20,30), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (255,255,255), 2)
cv2.imshow("Object Recognition - Laptop Webcam", frame)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
Confidence란? 화면에 표시되는 %는 “정답일 실제 확률”이 아니라 KNN 모델이 주변 학습 데이터와 비교해 계산한 상대적인 분류 신뢰도입니다. 값이 높을수록 현재 입력이 해당 클래스의 학습 특징과 더 비슷하다고 이해하면 됩니다.
실행 명령어
cd %USERPROFILE%\object_recognition_laptop_app
py -3.12 2_live_object_recognition.py7단계. 목표 사물 인식률 측정 테스트
5단계 학습 결과와 비교해보세요: 1_train_object_model.py를 실행했을 때 model/training_report.txt에 "테스트 인식률"이 저장됐습니다. 지금 이 7단계는 그 숫자가 실제 webcam 환경에서도 그대로 나오는지 직접 확인하는 과정입니다. 두 숫자가 비슷하면 모델이 일반화를 잘한 것이고, 지금 숫자가 훨씬 낮다면 학습 때 사진과 지금 조명·각도·배경이 달라서 생기는 "일반화 문제"를 직접 목격하고 있는 것입니다.
왜 0.5초마다 측정하나요? 매 프레임(1초에 수십 번)을 다 세면 사실상 똑같은 순간이 중복으로 과도하게 반영됩니다. 0.5초 간격으로 "정답/오답" 한 번씩만 기록해야 측정 횟수가 사람이 보기에도 의미 있는 숫자가 됩니다.
이번 버전에서 추가된 것: ① 전체 누적 인식률뿐 아니라 최근 20회 인식률도 함께 표시해 "지금 이 순간"의 성능을 더 빠르게 보여줍니다 ② 인식률·confidence를 색깔 막대그래프로 표시합니다 ③ r 키를 누르면 바로 초기화되지 않고, 지금까지의 구간 리포트(평균 confidence, 가장 많이 헷갈린 사물 등)를 CMD에 먼저 출력한 뒤 초기화됩니다.
파일 만드는 방법: CMD에서 notepad 3_measure_recognition_rate.py를 실행해 새 파일을 만들고, 아래 전체 코드를 붙여넣은 뒤 Ctrl+S로 저장합니다. 메모장을 닫고 CMD에서 py -3.12 3_measure_recognition_rate.py으로 실행합니다.
파일 만들기 명령어
cd %USERPROFILE%\object_recognition_laptop_app
notepad 3_measure_recognition_rate.py3_measure_recognition_rate.py 전체 코드
import os
import cv2
import joblib
import numpy as np
import time
from collections import deque, Counter
# 7단계: 목표 사물 인식률 측정 테스트 (교육용 업그레이드판)
MODEL_PATH = "model/object_model.joblib"
if not os.path.exists(MODEL_PATH):
print("모델 파일이 없습니다. 먼저 py -3.12 1_train_object_model.py 를 실행하세요.")
exit()
bundle = joblib.load(MODEL_PATH)
model = bundle["model"]
class_names = bundle["class_names"]
print("학습된 사물 목록:")
for name in class_names:
print("-", name)
target = input("\n인식률을 측정할 목표 사물 이름을 입력하세요: ").strip()
if target == "":
print("목표 사물 이름이 비어 있습니다.")
exit()
matched_target = None
for name in class_names:
if name.strip().lower() == target.strip().lower():
matched_target = name
break
if matched_target is None:
print("입력한 사물이 학습 목록에 없습니다.")
print("다음 중 하나를 정확히 입력하세요:")
for name in class_names:
print("-", name)
exit()
target = matched_target
def extract_features(image):
image = cv2.resize(image, (96, 96))
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1, 2], None, [8, 8, 8], [0, 180, 0, 256, 0, 256])
hist = cv2.normalize(hist, hist).flatten()
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.resize(gray, (64, 64))
hog = cv2.HOGDescriptor(_winSize=(64, 64), _blockSize=(16, 16), _blockStride=(8, 8), _cellSize=(8, 8), _nbins=9)
return np.concatenate([hist, hog.compute(gray).flatten()])
def predict_object(image):
feature = extract_features(image).reshape(1, -1)
pred = model.predict(feature)[0]
probs = model.predict_proba(feature)[0]
confidence = float(np.max(probs)) * 100
return pred, confidence
def rate_color(rate):
if rate >= 70:
return (0, 200, 0)
if rate >= 40:
return (0, 200, 255)
return (0, 0, 255)
def draw_bar(frame, x, y, w, h, ratio, color):
ratio = max(0.0, min(1.0, ratio))
cv2.rectangle(frame, (x, y), (x + w, y + h), (70, 70, 70), -1)
cv2.rectangle(frame, (x, y), (x + int(w * ratio), y + h), color, -1)
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 255), 1)
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
if not cap.isOpened():
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("웹캠을 열 수 없습니다.")
exit()
WINDOW_SIZE = 20
recent = deque(maxlen=WINDOW_SIZE)
total = 0
correct = 0
confidence_sum = 0.0
wrong_guess_counter = Counter()
high_confidence_wrong = 0
last_check = 0
interval = 0.5
print("\n인식률 측정을 시작합니다.")
print("r: 지금까지 구간 리포트 출력 후 초기화 (다른 사물로 바꿔 비교할 때 사용) / q: 종료")
def print_segment_report():
rate = (correct / total * 100) if total > 0 else 0
avg_conf = (confidence_sum / total) if total > 0 else 0
print("\n----- 구간 리포트 -----")
print(f"목표 사물: {target}")
print(f"정답 횟수 / 전체 횟수: {correct}/{total}")
print(f"인식률: {rate:.1f}%")
print(f"평균 confidence: {avg_conf:.1f}%")
if wrong_guess_counter:
most_common, cnt = wrong_guess_counter.most_common(1)[0]
print(f"가장 많이 헷갈린 사물: '{most_common}' ({cnt}회)")
print(f"자신있게 틀린 횟수(확신 60% 이상인데 오답): {high_confidence_wrong}")
print("-----------------------\n")
while True:
ret, frame = cap.read()
if not ret:
break
h, w = frame.shape[:2]
box_size = min(w, h) // 2
x1, y1 = w // 2 - box_size // 2, h // 2 - box_size // 2
x2, y2 = x1 + box_size, y1 + box_size
roi = frame[y1:y2, x1:x2]
pred, confidence = predict_object(roi)
is_correct = pred.strip().lower() == target.strip().lower()
now = time.time()
if now - last_check >= interval:
total += 1
confidence_sum += confidence
recent.append(1 if is_correct else 0)
if is_correct:
correct += 1
else:
wrong_guess_counter[pred] += 1
if confidence >= 60:
high_confidence_wrong += 1
last_check = now
overall_rate = (correct / total * 100) if total > 0 else 0
recent_rate = (sum(recent) / len(recent) * 100) if recent else 0
box_color = (0, 255, 0) if is_correct else (0, 0, 255)
cv2.rectangle(frame, (x1, y1), (x2, y2), box_color, 3)
overlay = frame.copy()
cv2.rectangle(overlay, (0, 0), (w, 175), (20, 20, 20), -1)
frame = cv2.addWeighted(overlay, 0.55, frame, 0.45, 0)
cv2.putText(frame, f"Target: {target} | Pred: {pred} ({confidence:.0f}%)", (20, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(frame, f"Recent {len(recent)}/{WINDOW_SIZE}: {recent_rate:.0f}%", (20, 62),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, rate_color(recent_rate), 2)
draw_bar(frame, 260, 48, 200, 18, recent_rate / 100, rate_color(recent_rate))
cv2.putText(frame, f"Overall: {overall_rate:.0f}% ({correct}/{total})", (20, 94),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, rate_color(overall_rate), 2)
draw_bar(frame, 260, 80, 200, 18, overall_rate / 100, rate_color(overall_rate))
cv2.putText(frame, f"Confidence now: {confidence:.0f}%", (20, 126),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2)
draw_bar(frame, 260, 112, 200, 18, confidence / 100, (0, 255, 255))
cv2.putText(frame, "r: segment report + reset q: quit", (20, 158),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200, 200, 200), 1)
cv2.imshow("Recognition Rate Test", frame)
key = cv2.waitKey(1) & 0xFF
if key == ord("r"):
print_segment_report()
total, correct, confidence_sum = 0, 0, 0.0
recent.clear()
wrong_guess_counter.clear()
high_confidence_wrong = 0
print("측정값을 초기화했습니다. 다음 구간 측정을 시작하세요.")
if key == ord("q"):
break
cap.release()
cv2.destroyAllWindows()
print_segment_report()
print("측정을 종료했습니다.")
인식률 측정 실행 명령어
cd %USERPROFILE%\object_recognition_laptop_app
py -3.12 3_measure_recognition_rate.py목표 사물 이름은 obj_data 안의 폴더 이름과 똑같이 입력하세요.
화면 읽는 법 — 색깔이 알려주는 것
| 화면 요소 | 의미 |
|---|---|
| 중앙 사각형 테두리 색 | 초록 = 지금 이 순간 목표 사물로 정확히 인식 / 빨강 = 다른 것으로 인식 |
| Recent 20회 막대 | 최근 20번 측정 중 몇 번 맞았는지. 지금 이 자세·조명에서의 실력을 가장 빠르게 보여줍니다. |
| Overall 막대 | 측정 시작(또는 마지막 r)부터 지금까지 전체 누적 인식률 |
| 막대 색(초록/주황/빨강) | 70% 이상 초록, 40~70% 주황, 40% 미만 빨강 — 숫자를 안 읽어도 상태를 바로 알 수 있습니다. |
confidence와 정답 여부, 함께 읽는 법
정답/오답과 confidence(모델의 확신 정도)는 서로 다른 축입니다. 네 가지 경우를 구분해서 이해하면 모델의 약점이 보입니다.
| confidence 높음 | confidence 낮음 | |
|---|---|---|
| 정답 | ✅ 이상적인 상태 — 모델이 확실히 잘 압니다. | 🤔 맞혔지만 확신은 부족 — 운이 좋았거나 애매한 사례입니다. |
| 오답 | ⚠️ 가장 위험한 경우 — 자신 있게 틀렸습니다. 학습 데이터에 이 상황과 비슷한 "가짜 정답" 사진이 섞였을 가능성이 있습니다. | 😕 모델도 헷갈려합니다 — 두 사물이 색·모양이 비슷하거나 학습 사진이 부족할 수 있습니다. |
프로그램이 CMD에 출력하는 "자신있게 틀린 횟수(확신 60% 이상인데 오답)"가 많다면 위 표의 ⚠️ 칸에 해당합니다 — 해당 사물의 학습 사진을 더 다양하게 보강하는 게 좋다는 신호입니다.
비교 실험 해보기
숫자 하나만 보고 끝내지 말고, 조건을 바꿔가며 비교해 보세요. r을 누르면 그 구간의 리포트가 CMD에 먼저 출력된 뒤 초기화되므로, 조건을 바꿀 때마다 눌러 기록을 남기세요.
| 실험 | 방법 | 인식률(%) | 평균 confidence(%) |
|---|---|---|---|
| ① 목표 사물 | 목표 사물을 중앙 사각형에 크게 보여주고 20~30초 측정 → r | ||
| ② 다른 사물 | 목표가 아닌 다른 학습된 사물로 교체 후 측정 → r | ||
| ③ 조명 바꾸기 | ①과 같은 사물, 조명만 어둡게/밝게 바꿔 측정 → r |
③번에서 인식률이 크게 떨어진다면, 이 모델이 "조명 변화에 약하다"는 걸 직접 증명한 것입니다 — 5단계 학습 데이터에 다양한 조명의 사진이 부족했다는 뜻이기도 합니다.
잘 안 될 때
| 증상 | 해결 |
|---|---|
| 웹캠이 안 열림 | Windows 카메라 권한을 켜고 Zoom/Teams/카메라 앱을 닫습니다. |
| No module named cv2 | py -3.12 -m pip install opencv-contrib-python==4.11.0.86을 실행한 뒤 CMD를 다시 엽니다. |
| AttributeError: module 'cv2' has no attribute ... | 다른 실습에서 opencv-python·opencv-python-headless·opencv-contrib-python을 섞어 설치했을 때 생기는 충돌입니다. 아래 명령으로 전부 지운 뒤 버전을 고정해 하나만 다시 설치하세요: py -3.12 -m pip uninstall opencv-python opencv-python-headless opencv-contrib-python -y 실행 후 py -3.12 -m pip install opencv-contrib-python==4.11.0.86 |
| pip 명령을 찾을 수 없음 | pip 대신 py -3.12 -m pip을 사용합니다. |
| Retrying / ProxyError / 인증서 오류 | 학교 네트워크의 방화벽·프록시·PyPI 접속 제한 가능성을 확인합니다. |
| obj_data 안에 이미지가 없다고 나옴 | obj_data/bag/사진.jpg처럼 사물별 폴더 안에 이미지가 있어야 합니다. |
| 인식률이 낮음 | 물체를 중앙 사각형 안에 크게 보여주고, 데이터셋의 사진을 다양하게 준비합니다. "Recent 20회" 막대가 낮다면 지금 조건(조명·각도) 문제, "Overall"만 낮고 Recent는 높다면 초반에 자세를 못 잡았던 영향이니 r로 리셋 후 다시 측정해 보세요. |
| 화면 위쪽 정보 패널이 안 보이거나 너무 큼 | 웹캠 해상도가 매우 작은 경우 패널(세로 175px)이 화면 많은 부분을 가릴 수 있습니다. 코드의 (0, 0), (w, 175) 숫자를 줄여서 패널 높이를 조절할 수 있습니다. |
수업 끝나고 전체 삭제하기
Windows CMD 삭제 명령어
cd %USERPROFILE%
rmdir /s /q object_recognition_laptop_app
dir주의: 삭제 명령은 되돌리기 어렵습니다.