색상 곡간

색상 곡간

κ°œμš”

컴퓨터 λΉ„μ „μ—μ„œ 색상 곡간(Color Space)은 색상을 ν‘œν˜„ν•˜λŠ” λ°©λ²•μž…λ‹ˆλ‹€. OpenCVλŠ” 기본적으둜 BGR 색상 곡간을 μ‚¬μš©ν•˜μ§€λ§Œ, νŠΉμ • μž‘μ—…μ—λŠ” HSV, LAB λ“± λ‹€λ₯Έ 색상 곡간이 더 νš¨κ³Όμ μž…λ‹ˆλ‹€. 이 λ¬Έμ„œμ—μ„œλŠ” λ‹€μ–‘ν•œ 색상 κ³΅κ°„μ˜ νŠΉμ„±κ³Ό λ³€ν™˜ 방법, 그리고 색상 기반 객체 좔적을 ν•™μŠ΅ν•©λ‹ˆλ‹€.

λ‚œμ΄λ„: ⭐⭐ (μ΄ˆκΈ‰-쀑급)

ν•™μŠ΅ λͺ©ν‘œ: - BGRκ³Ό RGB의 차이 이해 - HSV 색상 κ³΅κ°„μ˜ 원리와 ν™œμš© - cv2.cvtColor()λ₯Ό μ‚¬μš©ν•œ 색상 곡간 λ³€ν™˜ - 채널 뢄리/병합 - 색상 기반 객체 좔적 κ΅¬ν˜„


λͺ©μ°¨

  1. BGR vs RGB
  2. cv2.cvtColor()와 색상 λ³€ν™˜ μƒμˆ˜
  3. HSV 색상 곡간
  4. LAB 색상 곡간
  5. κ·Έλ ˆμ΄μŠ€μΌ€μΌ λ³€ν™˜
  6. 채널 뢄리와 병합
  7. 색상 기반 객체 좔적
  8. μ—°μŠ΅ 문제
  9. λ‹€μŒ 단계
  10. 참고 자료

1. BGR vs RGB

OpenCV의 κΈ°λ³Έ 색상 μˆœμ„œ

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    BGR vs RGB 비ꡐ                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   OpenCV (BGR)                 λŒ€λΆ€λΆ„μ˜ 라이브러리 (RGB)         β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                 β”‚
β”‚   β”‚ B β”‚ G β”‚ R β”‚               β”‚ R β”‚ G β”‚ B β”‚                   β”‚
β”‚   β”‚[0]β”‚[1]β”‚[2]β”‚               β”‚[0]β”‚[1]β”‚[2]β”‚                   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β”‚
β”‚                                                                 β”‚
β”‚   μˆœμˆ˜ν•œ 빨간색:               μˆœμˆ˜ν•œ 빨간색:                    β”‚
β”‚   [0, 0, 255]                  [255, 0, 0]                      β”‚
β”‚                                                                 β”‚
β”‚   μˆœμˆ˜ν•œ νŒŒλž€μƒ‰:               μˆœμˆ˜ν•œ νŒŒλž€μƒ‰:                    β”‚
β”‚   [255, 0, 0]                  [0, 0, 255]                      β”‚
β”‚                                                                 β”‚
β”‚   OpenCV μ‚¬μš© 라이브러리:       RGB μ‚¬μš© 라이브러리:             β”‚
β”‚   - cv2.imread()               - matplotlib                     β”‚
β”‚   - cv2.imshow()               - PIL/Pillow                     β”‚
β”‚   - cv2.imwrite()              - Tkinter                        β”‚
β”‚                                - μ›Ή λΈŒλΌμš°μ € (CSS/HTML)          β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

BGR을 μ‚¬μš©ν•˜λŠ” 이유

역사적인 μ΄μœ μž…λ‹ˆλ‹€. 초기 카메라와 λ””μŠ€ν”Œλ ˆμ΄ ν•˜λ“œμ›¨μ–΄κ°€ BGR μˆœμ„œλ‘œ 데이터λ₯Ό μ €μž₯ν–ˆκ³ , OpenCVλŠ” 이 κ΄€λ‘€λ₯Ό λ”°λžμŠ΅λ‹ˆλ‹€.

BGR ↔ RGB λ³€ν™˜

import cv2
import numpy as np

# 이미지 읽기 (BGR)
img_bgr = cv2.imread('image.jpg')

# BGR β†’ RGB λ³€ν™˜
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)

# RGB β†’ BGR λ³€ν™˜
img_bgr_back = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)

# NumPy둜 직접 λ³€ν™˜ (μŠ¬λΌμ΄μ‹±)
img_rgb_np = img_bgr[:, :, ::-1]  # 채널 μˆœμ„œ λ’€μ§‘κΈ°
img_rgb_np = img_bgr[..., ::-1]   # λ™μΌν•œ κ²°κ³Ό

# 채널별 μŠ€μ™‘
b, g, r = cv2.split(img_bgr)
img_rgb_split = cv2.merge([r, g, b])

matplotlibκ³Ό ν•¨κ»˜ μ‚¬μš©ν•˜κΈ°

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('image.jpg')

# 잘λͺ»λœ ν‘œμ‹œ (BGR κ·ΈλŒ€λ‘œ β†’ 색상이 λ’€λ°”λ€œ)
plt.figure(figsize=(12, 4))

plt.subplot(1, 3, 1)
plt.imshow(img)  # BGR κ·ΈλŒ€λ‘œ β†’ λΉ¨κ°•κ³Ό νŒŒλž‘μ΄ λ’€λ°”λ€œ
plt.title('Wrong (BGR)')
plt.axis('off')

# μ˜¬λ°”λ₯Έ ν‘œμ‹œ (RGB둜 λ³€ν™˜)
plt.subplot(1, 3, 2)
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.title('Correct (RGB)')
plt.axis('off')

# κ·Έλ ˆμ΄μŠ€μΌ€μΌ
plt.subplot(1, 3, 3)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
plt.imshow(gray, cmap='gray')
plt.title('Grayscale')
plt.axis('off')

plt.tight_layout()
plt.show()

2. cv2.cvtColor()와 색상 λ³€ν™˜ μƒμˆ˜

κΈ°λ³Έ μ‚¬μš©λ²•

import cv2

img = cv2.imread('image.jpg')

# cv2.cvtColor(src, code) - 색상 곡간 λ³€ν™˜
dst = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

μ£Όμš” λ³€ν™˜ μ½”λ“œ

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     μ£Όμš” 색상 λ³€ν™˜ μ½”λ“œ                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   BGR ↔ 기타 색상 곡간                                          β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2RGB / COLOR_RGB2BGR                             β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2GRAY / COLOR_GRAY2BGR                           β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2HSV / COLOR_HSV2BGR                             β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2LAB / COLOR_LAB2BGR                             β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2YCrCb / COLOR_YCrCb2BGR                         β”‚
β”‚   └── COLOR_BGR2HLS / COLOR_HLS2BGR                             β”‚
β”‚                                                                 β”‚
β”‚   RGB ↔ 기타 색상 곡간                                          β”‚
β”‚   β”œβ”€β”€ COLOR_RGB2GRAY / COLOR_GRAY2RGB                           β”‚
β”‚   β”œβ”€β”€ COLOR_RGB2HSV / COLOR_HSV2RGB                             β”‚
β”‚   β”œβ”€β”€ COLOR_RGB2LAB / COLOR_LAB2RGB                             β”‚
β”‚   └── COLOR_RGB2HLS / COLOR_HLS2RGB                             β”‚
β”‚                                                                 β”‚
β”‚   특수 λ³€ν™˜                                                      β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2HSV_FULL  (H: 0-255)                            β”‚
β”‚   β”œβ”€β”€ COLOR_BGR2HSV       (H: 0-179)                            β”‚
β”‚   └── COLOR_BayerBG2BGR   (Bayer β†’ BGR)                         β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

λ³€ν™˜ μ˜ˆμ‹œ

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('image.jpg')
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# λ‹€μ–‘ν•œ 색상 κ³΅κ°„μœΌλ‘œ λ³€ν™˜
conversions = {
    'Original (RGB)': img_rgb,
    'Grayscale': cv2.cvtColor(img, cv2.COLOR_BGR2GRAY),
    'HSV': cv2.cvtColor(img, cv2.COLOR_BGR2HSV),
    'LAB': cv2.cvtColor(img, cv2.COLOR_BGR2LAB),
    'YCrCb': cv2.cvtColor(img, cv2.COLOR_BGR2YCrCb),
    'HLS': cv2.cvtColor(img, cv2.COLOR_BGR2HLS),
}

fig, axes = plt.subplots(2, 3, figsize=(12, 8))
axes = axes.flatten()

for ax, (name, converted) in zip(axes, conversions.items()):
    if len(converted.shape) == 2:
        ax.imshow(converted, cmap='gray')
    else:
        ax.imshow(converted)
    ax.set_title(name)
    ax.axis('off')

plt.tight_layout()
plt.show()

3. HSV 색상 곡간

HSVλž€?

HSVλŠ” 색상(Hue), 채도(Saturation), λͺ…도(Value)둜 색을 ν‘œν˜„ν•©λ‹ˆλ‹€.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      HSV 색상 곡간                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   H (Hue) - 색상                                                β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0Β°    60Β°   120Β°   180Β°   240Β°   300Β°   360Β°          β”‚   β”‚
β”‚   β”‚  λΉ¨κ°•   λ…Έλž‘   초둝   청둝   νŒŒλž‘   보라   λΉ¨κ°•          β”‚   β”‚
β”‚   β”‚  β”œβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€            β”‚   β”‚
β”‚   β”‚  0     30     60     90    120    150    179            β”‚   β”‚
β”‚   β”‚      (OpenCVμ—μ„œ H λ²”μœ„: 0-179)                          β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚   S (Saturation) - 채도 (0-255)                                 β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0 (무채색/νšŒμƒ‰)  ──────────────▢  255 (μˆœμˆ˜ν•œ 색)       β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚   V (Value) - λͺ…도 (0-255)                                      β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0 (검은색)  ──────────────────▢  255 (밝은 색)          β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚                        V (밝기)                                  β”‚
β”‚                          β–²                                       β”‚
β”‚                          β”‚    흰색                               β”‚
β”‚                          β”‚   /                                   β”‚
β”‚                          β”‚  /                                    β”‚
β”‚                          β”‚ /     μˆœμˆ˜ν•œ 색                       β”‚
β”‚                          β”‚/───────●                              β”‚
β”‚                          β”‚        β•²                              β”‚
β”‚                          β”‚         β•²  S (채도)                   β”‚
β”‚                          β”‚          β•²                            β”‚
β”‚                          ●───────────╲───▢ H (색상, μ›ν˜•)        β”‚
β”‚                        검은색                                    β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

HSV λ³€ν™˜ 및 채널 확인

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('image.jpg')

# BGR β†’ HSV λ³€ν™˜
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 채널 뢄리
h, s, v = cv2.split(hsv)

# μ‹œκ°ν™”
fig, axes = plt.subplots(2, 2, figsize=(10, 10))

axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0, 0].set_title('Original')

axes[0, 1].imshow(h, cmap='hsv')  # HueλŠ” hsv 컬러맡 μ‚¬μš©
axes[0, 1].set_title('H (Hue)')

axes[1, 0].imshow(s, cmap='gray')
axes[1, 0].set_title('S (Saturation)')

axes[1, 1].imshow(v, cmap='gray')
axes[1, 1].set_title('V (Value)')

for ax in axes.flatten():
    ax.axis('off')

plt.tight_layout()
plt.show()

HSV의 μž₯점

import cv2
import numpy as np

# RGB/BGRμ—μ„œλŠ” μ‘°λͺ… 변화에 민감
# HSVμ—μ„œλŠ” V μ±„λ„λ§Œ 영ν–₯λ°›μŒ β†’ 색상 κ²€μΆœμ— 유리

# 예: 빨간색 κ²€μΆœ
img = cv2.imread('red_objects.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# 빨간색 λ²”μœ„ μ •μ˜ (Hueκ°€ 0 λ˜λŠ” 180 근처)
# 빨간색은 Hue λ²”μœ„μ˜ μ–‘ 끝에 있음
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])

lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([179, 255, 255])

# 마슀크 생성
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = mask1 | mask2  # 두 마슀크 ν•©μΉ˜κΈ°

# κ²°κ³Ό ν‘œμ‹œ
result = cv2.bitwise_and(img, img, mask=mask)

cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

μ£Όμš” μƒ‰μƒμ˜ HSV λ²”μœ„

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    μ£Όμš” 색상 HSV λ²”μœ„ (OpenCV)                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   색상       H (Hue)        S (Saturation)   V (Value)          β”‚
β”‚   ──────────────────────────────────────────────────────────    β”‚
β”‚   λΉ¨κ°•       0-10, 160-179   100-255         100-255            β”‚
β”‚   μ£Όν™©       10-25           100-255         100-255            β”‚
β”‚   λ…Έλž‘       25-35           100-255         100-255            β”‚
β”‚   초둝       35-85           100-255         100-255            β”‚
β”‚   청둝       85-95           100-255         100-255            β”‚
β”‚   νŒŒλž‘       95-130          100-255         100-255            β”‚
β”‚   보라       130-160         100-255         100-255            β”‚
β”‚                                                                 β”‚
β”‚   흰색       0-179           0-30            200-255            β”‚
β”‚   κ²€μ •       0-179           0-255           0-50               β”‚
β”‚   νšŒμƒ‰       0-179           0-30            50-200             β”‚
β”‚                                                                 β”‚
β”‚   주의: μ‘°λͺ… 쑰건에 따라 λ²”μœ„ μ‘°μ • ν•„μš”                           β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

4. LAB 색상 곡간

LABμ΄λž€?

LAB(λ˜λŠ” CIELAB)은 μΈκ°„μ˜ 색상 인지에 κΈ°λ°˜ν•œ 색상 κ³΅κ°„μž…λ‹ˆλ‹€.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                      LAB 색상 곡간                               β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   L (Lightness) - 밝기                                          β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0 (κ²€μ •)  ──────────────────────▢  255 (흰색)          β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚   A - 초둝(-) ↔ λΉ¨κ°•(+)                                        β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0 (초둝)  ────── 128 (쀑립) ──────  255 (λΉ¨κ°•)          β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚   B - νŒŒλž‘(-) ↔ λ…Έλž‘(+)                                        β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚  0 (νŒŒλž‘)  ────── 128 (쀑립) ──────  255 (λ…Έλž‘)          β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚                     +B (λ…Έλž‘)                                    β”‚
β”‚                        β–²                                        β”‚
β”‚                        β”‚                                        β”‚
β”‚            -A ◀────────┼────────▢ +A                            β”‚
β”‚          (초둝)        β”‚        (λΉ¨κ°•)                          β”‚
β”‚                        β”‚                                        β”‚
β”‚                        β–Ό                                        β”‚
β”‚                     -B (νŒŒλž‘)                                    β”‚
β”‚                                                                 β”‚
β”‚   μž₯점:                                                         β”‚
β”‚   - 인간 μ‹œκ°κ³Ό μœ μ‚¬ν•œ 색상 거리 계산                             β”‚
β”‚   - 밝기와 색상이 뢄리됨                                         β”‚
β”‚   - 색상 보정, 색상 전이에 유용                                   β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

LAB λ³€ν™˜ 및 ν™œμš©

import cv2
import numpy as np
import matplotlib.pyplot as plt

img = cv2.imread('image.jpg')

# BGR β†’ LAB λ³€ν™˜
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

# 채널 뢄리
l, a, b = cv2.split(lab)

# L 채널 μ‘°μ •μœΌλ‘œ 밝기 보정
l_adjusted = cv2.add(l, 30)  # 밝기 증가
l_adjusted = np.clip(l_adjusted, 0, 255).astype(np.uint8)

# λ‹€μ‹œ ν•©μΉ˜κΈ°
lab_adjusted = cv2.merge([l_adjusted, a, b])
result = cv2.cvtColor(lab_adjusted, cv2.COLOR_LAB2BGR)

# μ‹œκ°ν™”
fig, axes = plt.subplots(2, 3, figsize=(12, 8))

axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0, 0].set_title('Original')

axes[0, 1].imshow(l, cmap='gray')
axes[0, 1].set_title('L (Lightness)')

axes[0, 2].imshow(a, cmap='RdYlGn_r')
axes[0, 2].set_title('A (Green-Red)')

axes[1, 0].imshow(b, cmap='YlGnBu_r')
axes[1, 0].set_title('B (Blue-Yellow)')

axes[1, 1].imshow(cv2.cvtColor(result, cv2.COLOR_BGR2RGB))
axes[1, 1].set_title('Brightness Adjusted')

for ax in axes.flatten():
    ax.axis('off')
axes[1, 2].axis('off')

plt.tight_layout()
plt.show()

CLAHE둜 LAB 밝기 보정

import cv2

img = cv2.imread('dark_image.jpg')

# LAB λ³€ν™˜
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)

# CLAHE 적용 (Contrast Limited Adaptive Histogram Equalization)
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
l_clahe = clahe.apply(l)

# λ‹€μ‹œ ν•©μΉ˜κΈ°
lab_clahe = cv2.merge([l_clahe, a, b])
result = cv2.cvtColor(lab_clahe, cv2.COLOR_LAB2BGR)

cv2.imshow('Original', img)
cv2.imshow('CLAHE Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

5. κ·Έλ ˆμ΄μŠ€μΌ€μΌ λ³€ν™˜

λ³€ν™˜ 원리

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   κ·Έλ ˆμ΄μŠ€μΌ€μΌ λ³€ν™˜ 원리                          β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   BGR β†’ Grayscale λ³€ν™˜ 곡식:                                    β”‚
β”‚                                                                 β”‚
β”‚   Gray = 0.114 Γ— B + 0.587 Γ— G + 0.299 Γ— R                     β”‚
β”‚                                                                 β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”‚
β”‚   β”‚   μ™œ λ‹¨μˆœ 평균이 μ•„λ‹κΉŒ?                                  β”‚   β”‚
β”‚   β”‚                                                         β”‚   β”‚
β”‚   β”‚   μΈκ°„μ˜ λˆˆμ€ 녹색에 κ°€μž₯ λ―Όκ°ν•˜κ³ , νŒŒλž€μƒ‰μ— κ°€μž₯ 둔감함    β”‚   β”‚
β”‚   β”‚   λ”°λΌμ„œ 녹색(G)의 κ°€μ€‘μΉ˜κ°€ κ°€μž₯ λ†’μŒ (0.587)              β”‚   β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β”‚
β”‚                                                                 β”‚
β”‚   컬러 이미지                     κ·Έλ ˆμ΄μŠ€μΌ€μΌ                   β”‚
β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”             β”‚
β”‚   β”‚ B β”‚ G β”‚ R β”‚               β”‚     Gray      β”‚             β”‚
β”‚   β”‚200β”‚100β”‚ 50β”‚    ───▢       β”‚      121      β”‚             β”‚
β”‚   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜             β”‚
β”‚   0.114Γ—200 + 0.587Γ—100 + 0.299Γ—50 = 121.45                    β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

κ·Έλ ˆμ΄μŠ€μΌ€μΌ λ³€ν™˜ 방법

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 방법 1: cvtColor (ꢌμž₯)
gray1 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 방법 2: imread둜 직접 읽기
gray2 = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# 방법 3: NumPy둜 직접 계산 (ν•™μŠ΅μš©)
b, g, r = cv2.split(img)
gray3 = (0.114 * b + 0.587 * g + 0.299 * r).astype(np.uint8)

# 방법 4: λ‹¨μˆœ 평균 (λΉ„μΆ”μ²œ - μ‹œκ°μ μœΌλ‘œ λΆ€μžμ—°μŠ€λŸ¬μ›€)
gray4 = np.mean(img, axis=2).astype(np.uint8)

# κ²°κ³Ό 비ꡐ
print(f"cvtColor κ²°κ³Ό: {gray1.shape}")
print(f"직접 계산 κ²°κ³Ό: {gray3.shape}")
print(f"차이 μ΅œλŒ€κ°’: {np.max(np.abs(gray1.astype(int) - gray3.astype(int)))}")

κ·Έλ ˆμ΄μŠ€μΌ€μΌ β†’ 컬러 (μ˜μ‚¬ 컬러)

import cv2

gray = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)

# κ·Έλ ˆμ΄μŠ€μΌ€μΌ β†’ 3채널 (μ—¬μ „νžˆ 흑백)
gray_3ch = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)

# 컬러맡 적용 (히트맡 λ“±)
# COLORMAP_JET, COLORMAP_HOT, COLORMAP_RAINBOW λ“±
colormap = cv2.applyColorMap(gray, cv2.COLORMAP_JET)

cv2.imshow('Grayscale', gray)
cv2.imshow('Colormap', colormap)
cv2.waitKey(0)
cv2.destroyAllWindows()

6. 채널 뢄리와 병합

cv2.split()κ³Ό cv2.merge()

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 채널 뢄리
b, g, r = cv2.split(img)

# λ˜λŠ” NumPy 인덱싱 μ‚¬μš© (더 빠름)
b = img[:, :, 0]
g = img[:, :, 1]
r = img[:, :, 2]

# 채널 병합
merged = cv2.merge([b, g, r])  # BGR μˆœμ„œ

# 채널 μˆœμ„œ λ³€κ²½ν•˜μ—¬ 병합 (BGR β†’ RGB)
rgb = cv2.merge([r, g, b])

# 빈 채널과 μ‘°ν•© (단일 μ±„λ„λ§Œ ν‘œμ‹œ)
zeros = np.zeros_like(b)
only_blue = cv2.merge([b, zeros, zeros])
only_green = cv2.merge([zeros, g, zeros])
only_red = cv2.merge([zeros, zeros, r])

채널별 μ‹œκ°ν™”

import cv2
import matplotlib.pyplot as plt

img = cv2.imread('image.jpg')
b, g, r = cv2.split(img)

fig, axes = plt.subplots(2, 3, figsize=(12, 8))

# 원본
axes[0, 0].imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
axes[0, 0].set_title('Original')

# 각 채널 (κ·Έλ ˆμ΄μŠ€μΌ€μΌλ‘œ)
axes[0, 1].imshow(r, cmap='gray')
axes[0, 1].set_title('Red Channel')

axes[0, 2].imshow(g, cmap='gray')
axes[0, 2].set_title('Green Channel')

axes[1, 0].imshow(b, cmap='gray')
axes[1, 0].set_title('Blue Channel')

# 각 채널 (컬러둜)
zeros = np.zeros_like(b)
axes[1, 1].imshow(cv2.merge([zeros, zeros, r]))  # RGB μˆœμ„œ
axes[1, 1].set_title('Red Only')

axes[1, 2].imshow(cv2.merge([zeros, g, zeros]))
axes[1, 2].set_title('Green Only')

for ax in axes.flatten():
    ax.axis('off')

plt.tight_layout()
plt.show()

채널 μ‘°μž‘ 예제

import cv2
import numpy as np

img = cv2.imread('image.jpg')

# 1. νŠΉμ • 채널 증폭
b, g, r = cv2.split(img)
r_boost = np.clip(r.astype(np.int16) + 50, 0, 255).astype(np.uint8)
warm = cv2.merge([b, g, r_boost])  # λ”°λœ»ν•œ 색쑰

# 2. 채널 μŠ€μ™‘
b, g, r = cv2.split(img)
swapped = cv2.merge([r, g, b])  # Rκ³Ό B κ΅ν™˜

# 3. 채널 ν‰κ· μœΌλ‘œ κ·Έλ ˆμ΄μŠ€μΌ€μΌ
b, g, r = cv2.split(img)
gray_avg = ((b.astype(np.int16) + g + r) // 3).astype(np.uint8)

# 4. νŠΉμ • μ±„λ„λ§Œ μœ μ§€ (λ‚˜λ¨Έμ§€ 0으둜)
b, g, r = cv2.split(img)
only_r = cv2.merge([np.zeros_like(b), np.zeros_like(g), r])

7. 색상 기반 객체 좔적

inRange()λ₯Ό μ‚¬μš©ν•œ 색상 필터링

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   색상 기반 객체 좔적 νŒŒμ΄ν”„λΌμΈ                   β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚                                                                 β”‚
β”‚   μž…λ ₯ 이미지 (BGR)                                              β”‚
β”‚        β”‚                                                        β”‚
β”‚        β–Ό                                                        β”‚
β”‚   HSV λ³€ν™˜                                                      β”‚
β”‚        β”‚                                                        β”‚
β”‚        β–Ό                                                        β”‚
β”‚   cv2.inRange(hsv, lower, upper) ──▢ 이진 마슀크                 β”‚
β”‚        β”‚                                                        β”‚
β”‚        β–Ό                                                        β”‚
β”‚   λ…Έμ΄μ¦ˆ 제거 (λͺ¨ν΄λ‘œμ§€ μ—°μ‚°)                                     β”‚
β”‚        β”‚                                                        β”‚
β”‚        β–Ό                                                        β”‚
β”‚   μœ€κ³½μ„  κ²€μΆœ                                                    β”‚
β”‚        β”‚                                                        β”‚
β”‚        β–Ό                                                        β”‚
β”‚   객체 μœ„μΉ˜/크기 μΆ”μΆœ                                            β”‚
β”‚                                                                 β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

색상 좔적 κ΅¬ν˜„

import cv2
import numpy as np

def track_color(img, lower_hsv, upper_hsv):
    """νŠΉμ • 색상 λ²”μœ„μ˜ 객체λ₯Ό 좔적"""
    # HSV λ³€ν™˜
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

    # 마슀크 생성
    mask = cv2.inRange(hsv, lower_hsv, upper_hsv)

    # λ…Έμ΄μ¦ˆ 제거
    kernel = np.ones((5, 5), np.uint8)
    mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)

    # μœ€κ³½μ„  κ²€μΆœ
    contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL,
                                    cv2.CHAIN_APPROX_SIMPLE)

    # 결과 그리기
    result = img.copy()
    for contour in contours:
        area = cv2.contourArea(contour)
        if area > 500:  # μ΅œμ†Œ 면적 ν•„ν„°
            x, y, w, h = cv2.boundingRect(contour)
            cv2.rectangle(result, (x, y), (x+w, y+h), (0, 255, 0), 2)

            # 쀑심점
            cx, cy = x + w//2, y + h//2
            cv2.circle(result, (cx, cy), 5, (0, 0, 255), -1)

    return result, mask


# μ‚¬μš© 예: νŒŒλž€μƒ‰ 좔적
img = cv2.imread('blue_objects.jpg')

lower_blue = np.array([100, 100, 100])
upper_blue = np.array([130, 255, 255])

result, mask = track_color(img, lower_blue, upper_blue)

cv2.imshow('Original', img)
cv2.imshow('Mask', mask)
cv2.imshow('Result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

μ‹€μ‹œκ°„ 색상 좔적 (μ›ΉμΊ )

import cv2
import numpy as np

def nothing(x):
    pass

# νŠΈλž™λ°” 생성
cv2.namedWindow('Trackbars')
cv2.createTrackbar('H_Low', 'Trackbars', 0, 179, nothing)
cv2.createTrackbar('H_High', 'Trackbars', 179, 179, nothing)
cv2.createTrackbar('S_Low', 'Trackbars', 100, 255, nothing)
cv2.createTrackbar('S_High', 'Trackbars', 255, 255, nothing)
cv2.createTrackbar('V_Low', 'Trackbars', 100, 255, nothing)
cv2.createTrackbar('V_High', 'Trackbars', 255, 255, nothing)

cap = cv2.VideoCapture(0)

while True:
    ret, frame = cap.read()
    if not ret:
        break

    # νŠΈλž™λ°” κ°’ 읽기
    h_low = cv2.getTrackbarPos('H_Low', 'Trackbars')
    h_high = cv2.getTrackbarPos('H_High', 'Trackbars')
    s_low = cv2.getTrackbarPos('S_Low', 'Trackbars')
    s_high = cv2.getTrackbarPos('S_High', 'Trackbars')
    v_low = cv2.getTrackbarPos('V_Low', 'Trackbars')
    v_high = cv2.getTrackbarPos('V_High', 'Trackbars')

    lower = np.array([h_low, s_low, v_low])
    upper = np.array([h_high, s_high, v_high])

    # HSV λ³€ν™˜ 및 마슀크
    hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(hsv, lower, upper)
    result = cv2.bitwise_and(frame, frame, mask=mask)

    cv2.imshow('Frame', frame)
    cv2.imshow('Mask', mask)
    cv2.imshow('Result', result)

    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

닀쀑 색상 좔적

import cv2
import numpy as np

# μ—¬λŸ¬ 색상 μ •μ˜
colors = {
    'red': {
        'lower1': np.array([0, 100, 100]),
        'upper1': np.array([10, 255, 255]),
        'lower2': np.array([160, 100, 100]),
        'upper2': np.array([179, 255, 255]),
        'color': (0, 0, 255)
    },
    'green': {
        'lower': np.array([35, 100, 100]),
        'upper': np.array([85, 255, 255]),
        'color': (0, 255, 0)
    },
    'blue': {
        'lower': np.array([100, 100, 100]),
        'upper': np.array([130, 255, 255]),
        'color': (255, 0, 0)
    }
}

def track_multiple_colors(img, colors):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    result = img.copy()

    for name, params in colors.items():
        # 마슀크 생성
        if 'lower1' in params:  # λΉ¨κ°„μƒ‰μ²˜λŸΌ λ²”μœ„κ°€ 두 개인 경우
            mask1 = cv2.inRange(hsv, params['lower1'], params['upper1'])
            mask2 = cv2.inRange(hsv, params['lower2'], params['upper2'])
            mask = mask1 | mask2
        else:
            mask = cv2.inRange(hsv, params['lower'], params['upper'])

        # μœ€κ³½μ„  κ²€μΆœ
        contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL,
                                        cv2.CHAIN_APPROX_SIMPLE)

        for contour in contours:
            if cv2.contourArea(contour) > 500:
                x, y, w, h = cv2.boundingRect(contour)
                cv2.rectangle(result, (x, y), (x+w, y+h), params['color'], 2)
                cv2.putText(result, name, (x, y-10),
                           cv2.FONT_HERSHEY_SIMPLEX, 0.5, params['color'], 2)

    return result

8. μ—°μŠ΅ 문제

μ—°μŠ΅ 1: 색상 νŒ”λ ˆνŠΈ 생성

16κ°€μ§€ μ£Όμš” 색상(λΉ¨κ°•, μ£Όν™©, λ…Έλž‘, 초둝, 청둝, νŒŒλž‘, 보라, 뢄홍, 흰색, κ²€μ •, νšŒμƒ‰ λ“±)을 BGR κ°’μœΌλ‘œ μ •μ˜ν•˜κ³ , 100x100 크기의 색상 칩을 4x4 격자둜 λ°°μΉ˜ν•œ νŒ”λ ˆνŠΈ 이미지λ₯Ό μƒμ„±ν•˜μ„Έμš”.

μ—°μŠ΅ 2: HSV 색상 선택기

마우슀둜 이미지λ₯Ό ν΄λ¦­ν•˜λ©΄ ν•΄λ‹Ή ν”½μ…€μ˜ HSV 값을 좜λ ₯ν•˜κ³ , κ·Έ 색상과 μœ μ‚¬ν•œ λͺ¨λ“  μ˜μ—­μ„ ν•˜μ΄λΌμ΄νŠΈν•˜λŠ” ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν•˜μ„Έμš”.

# 힌트: cv2.setMouseCallback() μ‚¬μš©
def on_click(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        # ν΄λ¦­ν•œ μœ„μΉ˜μ˜ HSV κ°’ 좜λ ₯
        pass

μ—°μŠ΅ 3: 채널 μŠ€μ™‘ 효과

μ΄λ―Έμ§€μ˜ 채널을 λ‹€μ–‘ν•˜κ²Œ μ‘°ν•©ν•˜μ—¬ 6κ°€μ§€ 효과(BGR, BRG, GBR, GRB, RBG, RGB)λ₯Ό λ§Œλ“€κ³  λΉ„κ΅ν•˜μ„Έμš”.

μ—°μŠ΅ 4: 피뢀색 κ²€μΆœ

HSV와 YCrCb 색상 곡간을 μ‚¬μš©ν•˜μ—¬ μ΄λ―Έμ§€μ—μ„œ 피뢀색 μ˜μ—­μ„ κ²€μΆœν•˜μ„Έμš”. 두 λ°©λ²•μ˜ κ²°κ³Όλ₯Ό λΉ„κ΅ν•˜μ„Έμš”.

# 피뢀색 HSV λ²”μœ„ μ˜ˆμ‹œ
# H: 0-50, S: 20-150, V: 70-255

# 피뢀색 YCrCb λ²”μœ„ μ˜ˆμ‹œ
# Y: 0-255, Cr: 135-180, Cb: 85-135

μ—°μŠ΅ 5: 색상 전이 μ• λ‹ˆλ©”μ΄μ…˜

H 채널을 μ μ§„μ μœΌλ‘œ μ¦κ°€μ‹œμΌœ μ΄λ―Έμ§€μ˜ 색상이 λ¬΄μ§€κ°œμ²˜λŸΌ λ³€ν•˜λŠ” μ• λ‹ˆλ©”μ΄μ…˜μ„ λ§Œλ“œμ„Έμš”.

# 힌트
for h_shift in range(0, 180, 5):
    h_channel = (original_h + h_shift) % 180
    # ...

9. λ‹€μŒ 단계

04_Geometric_Transforms.mdμ—μ„œ 이미지 크기 쑰절, νšŒμ „, λ’€μ§‘κΈ°, μ–΄νŒŒμΈ/원근 λ³€ν™˜ 등을 ν•™μŠ΅ν•©λ‹ˆλ‹€!

λ‹€μŒμ— 배울 λ‚΄μš©: - cv2.resize()와 보간법 - νšŒμ „, λ’€μ§‘κΈ° ν•¨μˆ˜ - μ–΄νŒŒμΈ λ³€ν™˜ (이동, νšŒμ „, μŠ€μΌ€μΌ) - 원근 λ³€ν™˜ (λ¬Έμ„œ μŠ€μΊ”)


10. 참고 자료

곡식 λ¬Έμ„œ

κ΄€λ ¨ ν•™μŠ΅ 자료

폴더 κ΄€λ ¨ λ‚΄μš©
02_Image_Basics.md 이미지 읽기, ν”½μ…€ μ ‘κ·Ό
07_Thresholding.md HSV 기반 μž„κ³„μ²˜λ¦¬

색상 곡간 μ°Έκ³ 

to navigate between lessons