OpenCV 비전 실무4장 · 채널 처리와 히스토그램 분석16 / 21← 이전목차다음 →MetaContents

4장. 채널 처리와 히스토그램 분석

실습 3 — 채널 평균으로 사진의 색 경향 분석하기

색 경향이 다른 가상의 풍경 세 장(ch04_scenes/ 의 노을, 호수, 숲)에서 채널마다 평균을 구해 표로 만들고, 기준에 따라 "따뜻함, 차가움, 초록"을 판정한 뒤 막대그래프로 견줍니다.

1단계. 세 장의 채널 평균 표 만들기

Python
import cv2
import pandas as pd

scenes = {"sunset.png": "노을", "lake.png": "호수", "forest.png": "숲"}
rows = []
for file, label in scenes.items():
    img = cv2.imread(f"ch04_scenes/{file}")
    # cv2.mean: (B 평균, G 평균, R 평균, 0) 네 칸을 돌려준다. 앞의 세 칸만 쓴다
    b, g, r = cv2.mean(img)[:3]
    rows.append({"그림": label, "B": round(b, 1), "G": round(g, 1), "R": round(r, 1)})

table = pd.DataFrame(rows)
print(table.to_string(index=False))
출력
그림     B     G     R
노을  61.1  86.6 185.5
호수 200.2 151.0 104.2
 숲  93.6 161.3 109.9

2단계. 기준으로 색 경향 판정하기

Python
def judge(b, g, r, gap=30):
    """채널 평균 세 개로 색 경향을 판정한다. gap 은 '뚜렷이 크다'의 기준."""
    if g > b and g > r:
        return "초록이 많음"
    if r - b > gap:
        return "따뜻함"
    if b - r > gap:
        return "차가움"
    return "쏠림 적음"

# 가장 큰 채널의 이름: B, G, R 세 칸 가운데 값이 가장 큰 칸의 이름
table["가장 큰 채널"] = table[["B", "G", "R"]].idxmax(axis=1)
table["R-B"] = (table["R"] - table["B"]).round(1)
table["판정"] = [judge(row.B, row.G, row.R) for row in table.itertuples()]
print(table.to_string(index=False))
출력
그림     B     G     R 가장 큰 채널   R-B     판정
노을  61.1  86.6 185.5       R 124.4    따뜻함
호수 200.2 151.0 104.2       B -96.0    차가움
 숲  93.6 161.3 109.9       G  16.3 초록이 많음

3단계. 막대그래프로 견주기

Python
import matplotlib.pyplot as plt
import numpy as np

plt.rcParams["font.family"] = "Malgun Gothic"
plt.rcParams["axes.unicode_minus"] = False

x = np.arange(len(table))   # 그림 세 장의 가로 자리: 0, 1, 2
width = 0.25                # 막대 하나의 너비
fig, ax = plt.subplots(figsize=(8, 4))
# 채널마다 막대를 조금씩 옆으로 밀어 나란히 세운다. 막대 색은 채널의 색으로 정한다
for k, (ch, color) in enumerate([("B", "blue"), ("G", "green"), ("R", "red")]):
    ax.bar(x + (k - 1) * width, table[ch], width, label=ch, color=color)
ax.set_xticks(x, labels=[f"{n}\n({j})" for n, j in zip(table["그림"], table["판정"])])
ax.set_ylabel("채널 평균 (0~255)")
ax.set_ylim(0, 255)
ax.legend(loc="upper center", ncol=3)
ax.set_title("그림마다의 B·G·R 평균")
plt.tight_layout()
plt.show()
노을, 호수, 숲 그림의 B, G, R 평균을 파랑, 초록, 빨강 막대로 나란히 세운 막대그래프

코드 설명

  • cv2.mean(img)[:3] 은 B, G, R 평균 세 개입니다. 그림 한 장의 색을 숫자 세 개로 줄여 견줄 수 있게 합니다.
  • idxmax(axis=1) 은 줄마다 값이 가장 큰 칸의 이름을 돌려줍니다. idxmax 는 index of maximum(가장 큰 값의 이름)이라는 뜻입니다.
  • judge 함수는 초록이 가장 크면 "초록이 많음", 아니면 R 과 B 의 차이가 30 을 넘는 쪽으로 판정합니다. 노을은 R-B 가 124.4 라 따뜻함, 호수는 -96.0 이라 차가움, 숲은 G 가 161.3 으로 가장 커서 초록이 많음입니다. 숲의 R-B 는 16.3 이라, 초록 칸을 먼저 따지지 않았다면 쏠림 적음이 됐을 것입니다.
  • 막대 색은 color="blue" 처럼 채널의 색으로 직접 정했습니다. 기본 색을 쓰면 G 막대가 주황으로 나와 헷갈리기 때문입니다.
← 이전 절4장 · 실습 2 — 채널 하나만 색으로 보기와 바꿔치기다음 절 →4장 · 실습 4 — 흑백 히스토그램 그리기