색의 속성에 대한 단어들

contributor : Jerry Kim

  • 색을 고르다 보면 이런 단어들을 만납니다:
    Brightness, Lightness, Value, Luminosity, Luma
  • 우리 말로는 모두 명도, 명도, 명도, 명도, 명도이지만 의미가 모두 다릅니다.
  • 같은 뜻으로 생각하면 실수할 수도 있으니 한번 짚고 넘어갑시다.

1. 용어 정리

Gilchrist, “Lightness and Brightness”, Curr Biol. 2007
Lightness, Lumiosity, Luminance and Other Tonal Descriptors
wikipedia: CIELAB color space
wikipedia: HSL and HSV
데이터 시각화, 인지과학을 만나다 (에이콘)

  • lightnessbrightness에 대한 글은 2007년의 논문 설명이 가장 와닿았습니다.
  • value의 의미는 wikipedia가 가장 이해하기 좋았고,
  • 전반적으로는 이 글에 용어들이 잘 정리되어 있습니다.
  • 제 블로그 글에서도 용어가 왔다갔다 해왔습니다. 이제부터라도 통일하려고 합니다.
  • 데이터 시각화, 인지과학을 만나다의 번역을 주로 따랐습니다.
    • brightness : 명도(明度)
    • lightness : 밝기
    • luminance : 휘도(輝度)
    • luminosity(luminous intensity) : 광도(光度)
    • hue : 색상(色相)
    • value : 밸류
      • 모든 문서에 명도로 번역되어 있지만 brightness, lightness와 충돌합니다.
      • 고민을 거듭한 끝에 영어 발음을 그대로 사용하기로 했습니다.
    • relative luminance : 상대 휘도

1.1. 간단한 역사

Young-Helmholtz theory
wolfcrow: What is the difference between CIE LAB, CIE RGB, CIE xyY and CIE XYZ?

  • 복잡한 일은 역사를 들여다보면 체계가 잡히기도 합니다.

  • 1850년경 - RGB 3원색 이론: 영-헬름홀츠 색각설

    • “빛의 3원색” = Red, Green, Blue
    • R + G + B = White. 섞을 수록 밝아짐.

      최초의 컬러 사진 (1861)
  • 1906년 - CMYK 4색 인쇄: the Eagle Printing Ink Company
    • “색의 3원색” = Cyan, Magenta, Yellow
    • C + M + Y = Black. 섞을 수록 어두워짐.
    • 수백년 전 RYB → CMY → CMYK

      CMY 3색 인쇄 (1902)
  • 1913년 - 국제 조명 위원회(CIE) 설립
    • Commission Internationale de l’éclairage
    • 색에 대한 표준 등을 정립하는 단체
  • 1931년 - CIERGB, CIEXYZ 색공간 정립
    • RGB : 1920년대 수행된 인간 시각에 대한 연구 기반 (W. David Wright and John Guild)
    • XYZ : 당시까지 알려진 인간 시각의 수학적 한계. Y=휘도, Z~blue, X=휘도와 직교하는 RGB의 나머지 요소
  • 1938년 - HSL 색공간 정립
    • Hue, Saturation, Luminosity (or Lightness)
    • 목적 : 흑백으로 진행되던 TV 방송에 휘도(L) 데이터 변경 없이 color encoding 추가

      HSL cylinder
  • 1956년 - 세 가지 원뿔세포가 R, G, B를 인식한다는 것이 알려짐 (Svaetichin, Acta Physiol Scand Suppl. 1956)
    • 감응하는 파장은 558 nm, 531 nm, 419 nm 영역.
    • 원뿔세포의 비율은 R : G : B = 40 : 20 : 1

      망막 세포 구조와 원뿔세포 배열
  • 1970년대 중반 - HSV 색공간 정립
    • Hue, Saturation, Value
    • 목적 : HSL보다 전통적이고 직관적인 색 혼합 모델
    • 색이 어떻게 혼합되고 사람의 눈에 어떻게 보이는지에 중점.

      HSV cylinder
  • 1976년 - CIELAB 색공간 정립
    • 목적 : 사람의 인식과 최대한 비슷한 색 공간 구현
    • : 휘도. black = 0, white = 100
    • : green-red 요소. -128 ~ 127
    • : blue-yellow 요소. -128 ~ 127

      CIELAB color space top view

1.2. 용어의 뜻

  • luminosity(광도) luminance(휘도)
    • 광도 : 물체에서 발산되는 빛 에너지.
    • 휘도 : 관찰자의 눈에 들어오는 빛. 단위 면적당 광도.
    • 상황에 따라 둘 다 HSL의 구성요소로 사용됨.
  • brightness(명도) lightness(밝기)
    • 명도 : 어두움과 밝음의 정도. 주관적인 수치.
      • 빛의 인식 : “perception of luminance”
    • 밝기 : 흰색과 검정의 정도. 객관적인 수치.
      • 빛이 반사되는 표면의 인식 : “perception of reflectance”
      • 상황에 따라 HSL의 구성요소로 사용됨.
  • lightness(밝기) value(밸류)
    • 유채색의 밝기 : 유채색에 하얀 물감을 섞은 정도.
      • 하얀 물감을 아주 많이 섞으면 본래 색이 사라지고 하얀 색이 됨.
    • 유채색의 정도 : 유채색에 밝은 빛을 비춘 정도.
      • 밝은 빛을 아주 강하게 비춰도 본래 색이 드러남.
  • relative luminance(상대 휘도)
    • RGB 모니터의 밝기
    • CIEXYZ의 Y(휘도)와 유사.
    • $Y = 0.2126R + 0.7152G + 0.0722B$

2. Matplotlib Colormap 속성 분석

matplotlib: Choosing Colormaps in Matplotlib

  • matplotlib에는 컬러맵이 다수 내장되어 있습니다.
  • 이들 컬러맵은 어떤 색들을 가지고 있는지 살펴봅시다.
  • 주의할 사항이 있습니다.
    • Matplotlib 공식문서lightness($L^*$)라는 표현이 있습니다.
    • 이 $L^*$는 CIELAB 색공간의 휘도(luminance)이고, 밝기(lightness)와는 다릅니다.
    • 사람이 인지하는 밝은 정도휘도($L^*$)가 가깝습니다.
  • 색(color)이라는 데이터의 속성이 어떻게 다른지 엄밀히 따져보겠습니다.
    • 흔히 명도로 통용되는 데이터에 집중합니다.
    • 밸류(V), 휘도(), 상대 휘도(r), 밝기(L)가 그 대상입니다.

2.1. Perpetually Uniform Sequential Colormaps

  • Perpetually Uniform Sequential Colormap은 휘도($L^*$)가 직선으로 증가한다고 합니다.
  • 맞는지 확인해봅시다.




  • 휘도가 넓은 범위에 걸쳐 선형으로 증가하고 있습니다.
  • 밸류와 휘도, 밝기가 완전히 따로 놉니다.
    • 시각적으로 어두운 색의 밸류가 높고, 밝은 색의 밸류가 낮기도 합니다.
    • 밝기는 휘도와 비슷하게 가다가 어느 순간 이탈합니다.
    • 밸류, 밝기, 휘도를 똑같이 명도로 번역하면 안될 것 같습니다.

2.2. Qualitative colormaps

  • 범주(category)별 비교에 주로 사용되는 qualitative colormap은 휘도($L^*$)가 거의 균일합니다.
  • 사람의 눈은 휘도를 데이터의 크고 작음으로 생각하는 경향이 있기 때문에,
  • 혼동을 주지 않도록 비슷한 휘도의 색을 나열하는 것입니다.





  • Perpetually Uniform Sequential Colormap에 비해 $L^*$가 특정 구간에 몰려있습니다.
  • 하지만 Accent는 그렇지 않네요. 사용시 주의해야겠습니다.

2.3. Miscellaneous colormaps

  • 기타 컬러맵입니다.
  • 자주 사용되는 것들을 살펴봅니다.





  • 비슷한 무지개 계열임에도 gluplot은 순서를 바꿔서 휘도가 순차적으로 증가합니다.
  • 전체적으로 상대 휘도, 휘도는 값이 유사하지만, value(밸류)와는 상당히 다릅니다.
  • 밝기도 어느 정도 휘도와 비슷한 경향을 따릅니다.
  • 많은 문서에서 value를 명도라고 번역하는 바람에 lightness(밝기), brightness(명도)와 혼동되지만 이렇게 그려놓고 보니 절대 혼용하면 안 되는 개념이라는 생각이 듭니다.

3. Matplotlib 구현

  • 앞에 나열된 그림들을 그리는 코드를 설명합니다.
    • V(value: 밸류), r$L^*$(relative luminance: 상대 휘도), $L^*$(lightness: 휘도), L(lightness: 밝기)를 중심으로,
    • H(hue: 색상), S(saturation: 채도)의 변화도 함께 봅니다.

3.1. 시각화 설계

  • 이렇게 화면을 구상했습니다.

    • 컬러맵의 색상들을 좌측 상단에 나열하고,
    • 각 색상에 대응되는 H, S, V, r$L^$, $L^$, L을 바로 아래에 line plot으로 그립니다.
    • 그리고 속성들의 분포를 우측 하단에 violin plot으로 그립니다.
    • 우측 상단은 비웁니다. 단, 우측 하단의 xticklabel 대신 우측 상단 공간을 활용하겠습니다.


  • 위 그림을 그린 코드는 이렇습니다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import matplotlib.pyplot as plt

    # subplots 나누기
    fig, axes = plt.subplots(ncols=2, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7, 3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 글자 넣기
    axes[0, 0].text(0.5, 0.5, "colors in colormap (list)", fontdict=fonts_ex)
    axes[0, 1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    axes[1, 0].text(0.5, 0.5, "HSV (line plot)", fontdict=fonts_ex)
    axes[1, 1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.2. 컬러맵 뿌리기

3.2.1. 컬러맵 가져오기

matplotlib: Choosing Colormaps in Matplotlib

  • 컬러맵을 하나 골라서 뿌려봅니다.
    • 좌측 상단 구역을 색 갯수만큼 나눠야 합니다.
  • 컬러맵의 종류에 따라 색상의 갯수를 지정하는 방식이 달라집니다.

    • Qualitative colormap은 갯수가 지정되어 있습니다.
    • 그 외의 컬러맵은 갯수를 지정해야 합니다.
    • 10개로 지정합니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import matplotlib.pyplot as plt
    from matplotlib import cm

    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

3.2.2. 컬러맵 subplots 나누기

  • 목적에 맞게 subplots를 준비합니다.
    • 좌측 상단 구역에 색상을 하나씩 넣을 것입니다.
    • line plot을 그릴 공간까지 잘려버렸지만 일단 넘어갑니다.

  • 위 그림을 그린 코드는 이렇습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 글자 넣기
    fig.text(0.35, 0.83, "colors in colormap (list)", fontdict=fonts_ex)
    axes[0, -1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    fig.text(0.35, 0.33, "HSV (line plot)", fontdict=fonts_ex)
    axes[1, -1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.2.3. 컬러맵 색상 칠하기

  • 컬러맵 색상을 하나씩 넣습니다.
    • 색상에서 RGB를 추출해 글자로 넣습니다.
    • 컬러맵에 정수 i를 넣거나 0~1 사이의 값을 넣으면 이에 해당하는 색이 추출됩니다.

  • 위 그림을 그린 코드는 이렇습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 글자 넣기
    axes[0, -1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    fig.text(0.35, 0.33, "HSV (line plot)", fontdict=fonts_ex)
    axes[1, -1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # 컬러맵으로 subplot 채우고 RGB 정보 입력
    for i, ax in enumerate(axes[0, :-1]):
    ax.set_facecolor(cmap_(i/ncmap))
    ax.set_xticks([])
    ax.set_yticks([])
    R, G, B, A = cmap_(i/ncmap)

    fontcolor = "k"
    ax.text(0.1, 0.8, f"R: {R:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.6, f"G: {G:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.4, f"B: {B:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)

    # 좌측 하단 subplots 합침
    gs = axes[1, 0].get_gridspec()
    for ax in axes[1, :-1]:
    ax.remove()
    axbig = fig.add_subplot(gs[1, :-1])

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.3. 색상 정보 line plot

3.3.1. 좌측 하단 subplots 합치기

matplotlib: Customizing Figure Layouts Using GridSpec and Other Functions

  • Matplotlib에서 subplots를 합치는 과정은 다음 두 단계를 거칩니다.
    1. 기존의 subplots를 삭제합니다.
    2. 빈 공간에 맞게 새 subplot을 그립니다.
  • 좌측 하단 공간에 axbig이라는 새로운 이름이 붙었습니다.

  • 코드는 이렇습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 컬러맵으로 subplot 채우고 RGB 정보 입력
    for i, ax in enumerate(axes[0, :-1]):
    ax.set_facecolor(cmap_(i/ncmap))
    ax.set_xticks([])
    ax.set_yticks([])
    R, G, B, A = cmap_(i/ncmap)

    fontcolor = "k"
    ax.text(0.1, 0.8, f"R: {R:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.6, f"G: {G:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.4, f"B: {B:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)

    # 좌측 하단 subplots 합침: axbig
    gs = axes[1, 0].get_gridspec()
    for ax in axes[1, :-1]:
    ax.remove()
    axbig = fig.add_subplot(gs[1, :-1])

    # 글자 넣기
    axes[0, -1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    axbig.text(0.5, 0.5, "HSV (line plot)", fontdict=fonts_ex)
    axes[1, -1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.3.2. 색 속성 추출

  • RGB 데이터를 HSV로 변환합니다.
  • 다른 한편으로 휘도(luminance)와 밝기(lightness)로도 변환합니다.
    • 휘도 변환에는 colorspacious 라이브러리를 사용하고,
    • 밝기 변환에는 colorsys를 사용합니다.
    • 그리고 이들을 list로 모읍니다.
    • 어두운 색들의 글자 색도 하얗게 바꿉니다.

  • 여기까지 코드는 이렇습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    import matplotlib.pyplot as plt
    from matplotlib import cm
    from colorspacious import cspace_converter
    import colorsys

    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 컬러맵 데이터 모음 list
    Hs, Ss, Vs, rLs, Ls, ls = [], [], [], [], [], []

    # 컬러맵으로 subplot 채우고 RGB 정보 입력
    for i, ax in enumerate(axes[0, :-1]):
    ax.set_facecolor(cmap_(i/ncmap))
    ax.set_xticks([])
    ax.set_yticks([])
    # RGB + alpha
    R, G, B, A = cmap_(i/ncmap)
    # HSV
    H, S, V = colors.rgb_to_hsv((R, G, B))
    # 휘도(luminance)
    L, _, _ = cspace_converter("sRGB1", "CAM02-UCS")([R, G, B])
    # 상대 휘도(relative luminance)
    rL = 0.2126*R + 0.7152*G + 0.0722*B
    # 밝기 (lightness)
    _, l, _ = colorsys.rgb_to_hls(R, G, B)

    # 데이터 모음
    Hs.append(H)
    Ss.append(S)
    Vs.append(V)
    Ls.append(L/100)
    rLs.append(rL)
    ls.append(l)

    fontcolor = "k"

    # 밝기 기준 어두운 색은 밝은 색으로 표시
    if L/100 < 0.6:
    fontcolor = "w"
    ax.text(0.1, 0.8, f"R: {R:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.6, f"G: {G:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.4, f"B: {B:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)

    # 좌측 하단 subplots 합침: axbig
    gs = axes[1, 0].get_gridspec()
    for ax in axes[1, :-1]:
    ax.remove()
    axbig = fig.add_subplot(gs[1, :-1])

    # 글자 넣기
    axes[0, -1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    axbig.text(0.5, 0.5, "HSV (line plot)", fontdict=fonts_ex)
    axes[1, -1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.3.3. 색별 속성 시각화

  • 컬러맵 색상들 아래에 속성을 line plot으로 시각화합니다.

    • x축 눈금은 불필요하므로 제거하고 y축만 남깁니다.
    • 데이터를 상단의 컬러맵과 매칭하기 좋도록 grid를 추가합니다.
    • 전체 윤곽선도 불필요하므로 제거합니다.


  • 이것 하나만 해도 일반적인 하나의 plot이네요.

  • 코드가 제법 길어졌습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    import matplotlib.pyplot as plt
    from matplotlib import cm
    from colorspacious import cspace_converter
    import colorsys

    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3]}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 컬러맵 데이터 모음 list
    Hs, Ss, Vs, rLs, Ls, ls = [], [], [], [], [], []

    # 컬러맵으로 subplot 채우고 RGB 정보 입력
    for i, ax in enumerate(axes[0, :-1]):
    ax.set_facecolor(cmap_(i/ncmap))
    ax.set_xticks([])
    ax.set_yticks([])
    # RGB + alpha
    R, G, B, A = cmap_(i/ncmap)
    # HSV
    H, S, V = colors.rgb_to_hsv((R, G, B))
    # 휘도(luminance)
    L, _, _ = cspace_converter("sRGB1", "CAM02-UCS")([R, G, B])
    # 상대 휘도(relative luminance)
    rL = 0.2126*R + 0.7152*G + 0.0722*B
    # 밝기(lightness)
    _, l, _ = colorsys.rgb_to_hls(R, G, B)

    # 데이터 모음
    Hs.append(H)
    Ss.append(S)
    Vs.append(V)
    Ls.append(L/100)
    rLs.append(rL)
    ls.append(l)

    fontcolor = "k"

    # 밝기 기준 어두운 색은 밝은 색으로 표시
    if L/100 < 0.6:
    fontcolor = "w"
    ax.text(0.1, 0.8, f"R: {R:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.6, f"G: {G:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.4, f"B: {B:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)

    # 좌측 하단 subplots 합침: axbig
    gs = axes[1, 0].get_gridspec()
    for ax in axes[1, :-1]:
    ax.remove()
    axbig = fig.add_subplot(gs[1, :-1])

    # 색상별 데이터 plot
    axbig.plot(Hs, "-o", c="c", alpha=0.5) # hue (색상)
    axbig.plot(Ss, "-o", c="m", alpha=0.5) # saturation (채도)
    axbig.plot(Vs, "-o", c="y") # value (밸류)
    axbig.plot(rLs, "-o", c="gray") # relative luminance (상대 휘도)
    axbig.plot(Ls, "-o", c="k") # luminance (휘도)
    axbig.plot(ls, "-o", mec="b", mfc="w", c="b") # lightness (밝기)

    # 색상별 데이터 subplot 설정
    axbig.set_xticks([]) # xticks 삭제
    axbig.set_xlim(-0.4, ncmap-1 + 0.4) # x 범위 지정
    axbig.set_ylim(-0.1, 1.2) # y 범위 지정

    for x in range(ncmap): # x축 grid 대체 점선
    axbig.axvline(x, ls=":", c="lightgray", zorder=0)
    axbig.grid(axis="y", ls=":") # y축 grid
    axbig.set_frame_on(False) # 윤곽선 삭제

    # 글자 넣기
    axes[0, -1].text(0.5, 0.5, "(blank)", fontdict=fonts_ex)
    axes[1, -1].text(0.5, 0.5, "HSV distribution\n(violin plot)", fontdict=fonts_ex)

    # ticks 제거
    for ax in axes.ravel():
    ax.set_xticks([])
    ax.set_yticks([])

    fig.tight_layout()

3.3.4. 컬러맵별 속성 분포

  • 데이터가 여섯 개나 되고, 뭐가 뭔지 알아보기 힘듭니다.
  • violin plot으로 분포를 확인합니다.
    • .get_children()으로 violin plot을 하나씩 지정하고,
    • .set_facecolor()로 violin plot의 색상을 데이터 종류에 맞추어 지정합니다.
    • 비슷하게 .set_alpha()를 사용해서 불투명도를 제어합니다.
  • 속성 분포를 개별 색의 데이터와 비교하기 좋게 배열합니다.
    • y 범위를 맞춰 우측 아래 공간에 넣습니다.
    • 색상별 데이터 이름을 함께 적어줍니다.
    • plt.subplots()gridspec_kw"hspace":0을 넣어 위 아래 사이 공간을 좁혀줍니다.

  • 그럴싸한 통계 분석이 되었습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    import matplotlib.pyplot as plt
    from matplotlib import cm
    from colorspacious import cspace_converter
    import colorsys
    import seaborn as sns

    # 컬러맵 불러오기
    cmap_ = plt.get_cmap("tab10")
    # 컬러맵의 색 수
    ncmap = len(cmap_.colors)

    try: # qualitative colormap
    ncmap = len(cmap_.colors)
    except AttributeError: # others
    ncmap = 10
    cmap_ = cm.get_cmap(cmap, ncmap)

    # 수가 너무 많으면 10개로 제한
    if ncmap > 15:
    ncmap = 10

    # subplots 나누기 (ncmap 반영)
    fig, axes = plt.subplots(ncols=ncmap+1, nrows=2, figsize=(10, 3),
    gridspec_kw={"height_ratios":[1, 2],
    "width_ratios": [7/ncmap]*ncmap + [3],
    "hspace":0}
    )

    # 폰트 설정
    fonts_ex = {"color":"gray", "ha":"center", "va":"center", "fontsize":"x-large"}

    # 컬러맵 데이터 모음 list
    Hs, Ss, Vs, rLs, Ls, ls = [], [], [], [], [], []

    # 컬러맵으로 subplot 채우고 RGB 정보 입력
    for i, ax in enumerate(axes[0, :-1]):
    ax.set_facecolor(cmap_(i/ncmap))
    ax.set_xticks([])
    ax.set_yticks([])
    # RGB + alpha
    R, G, B, A = cmap_(i/ncmap)
    # HSV
    H, S, V = colors.rgb_to_hsv((R, G, B))
    # 휘도 (luminance)
    L, _, _ = cspace_converter("sRGB1", "CAM02-UCS")([R, G, B])
    # 상대 휘도 (relative luminance)
    rL = 0.2126*R + 0.7152*G + 0.0722*B
    # 밝기 (lightness)
    _, l, _ = colorsys.rgb_to_hls(R, G, B)

    # 데이터 모음
    Hs.append(H)
    Ss.append(S)
    Vs.append(V)
    Ls.append(L/100)
    rLs.append(rL)
    ls.append(l)

    fontcolor = "k"

    # 밝기 기준 어두운 색은 밝은 색으로 표시
    if L/100 < 0.6:
    fontcolor = "w"
    ax.text(0.1, 0.8, f"R: {R:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.6, f"G: {G:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)
    ax.text(0.1, 0.4, f"B: {B:.2f}", c=fontcolor, transform=ax.transAxes, fontsize=8)

    # 좌측 하단 subplots 합침: axbig
    gs = axes[1, 0].get_gridspec()
    for ax in axes[1, :-1]:
    ax.remove()
    axbig = fig.add_subplot(gs[1, :-1])

    # 색상별 데이터 plot
    axbig.plot(Hs, "-o", c="c", alpha=0.5) # hue (색상)
    axbig.plot(Ss, "-o", c="m", alpha=0.5) # saturation (채도)
    axbig.plot(Vs, "-o", c="y") # value (밸류)
    axbig.plot(rLs, "-o", c="gray") # relative luminance (상대 휘도)
    axbig.plot(Ls, "-o", c="k") # luminance (휘도)
    axbig.plot(ls, "-o", mec="b", mfc="w", c="b") # lightness (밝기)

    # 색상별 데이터 subplot 설정
    axbig.set_xticks([]) # xticks 삭제
    axbig.set_xlim(-0.4, ncmap-1 + 0.4) # x 범위 지정
    axbig.set_ylim(-0.1, 1.2) # y 범위 지정

    for x in range(ncmap): # x축 grid 대체 점선
    axbig.axvline(x, ls=":", c="lightgray", zorder=0)
    axbig.grid(axis="y", ls=":") # y축 grid
    axbig.set_frame_on(False) # 윤곽선 삭제

    # 색 속성 분포
    sns.violinplot(data=[Hs, Ss, Vs, rLs, Ls, ls], alpha=0.5, cut=0, ax=axes[1, -1])
    # violin plot 색상, 불투명도 지정
    for i, c in enumerate(["c", "m", "y", "gray", "k", "b"]):
    axes[1, -1].get_children()[2*i].set_facecolor(c)
    axes[1, -1].get_children()[2*i].set_alpha(0.5)
    # violin plot subplot 설정
    axes[1, -1].set_ylim(-0.1, 1.2)
    axes[1, -1].grid(axis="y", ls=":")
    axes[1, -1].set_frame_on(False)
    axes[1, -1].set_yticks([0, 0.5, 1])
    axes[1, -1].set_yticklabels([])
    axes[1, -1].set_xticks([0, 1, 2, 3, 4])
    axes[1, -1].set_xticklabels([])
    axes[1, -1].tick_params(length=0)

    # 우측 상단 subplot 설정: violin plot의 xticklabels로 활용.
    axes[0, -1].axis(False)
    axes[0, -1].set_xlim(axes[1, -1].get_xlim())
    axes[0, -1].set_xticks([0, 1, 2, 3, 4])
    font_hsv = {"fontsize": 14, "ha": "center"}
    axes[0, -1].text(0, 0, "H", fontdict=font_hsv, color="c")
    axes[0, -1].text(1, 0, "S", fontdict=font_hsv, color="m")
    axes[0, -1].text(2, 0, "V", fontdict=font_hsv, color="y")
    axes[0, -1].text(3, 0, r"r$L^*$", fontdict=font_hsv, color="gray")
    axes[0, -1].text(4, 0, r"$L^*$", fontdict=font_hsv, color="k")
    axes[0, -1].text(5, 0, r"L", fontdict=font_hsv, color="b")

    fig.tight_layout()


도움이 되셨나요?

카페인을 투입하시면 다음 포스팅으로 변환됩니다

Share