Home > Study > Linux > Day2 : OpenCV

Day2 : OpenCV
Study Language

πŸ“Œ 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 인터프리터 μ’…λ£Œ

🎨 색상 정보


πŸ”— μ°Έκ³  μ‚¬μ΄νŠΈ


🌈 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()

alt text

❓ 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()

alt text
alt text

❓ 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()

alt text

❓ 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()

alt text

❓ 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()

alt text

❓ 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()

alt text

❓ 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()

alt text
alt text
alt text
alt text

❓ 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()

alt text
alt text
alt text
alt text

❓ 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λ₯Ό λ³€κ²½ν•΄ 보자.