곡선 밑에 그라데이션을 그리는 방법은 matplotlib는 직접 제공하지 않습니다. Stackoverflow.com에 소개된 방법을 사용해야 합니다. 소개된 방법은 근사하지만 설명이 부족해서 응용하기가
어려운 듯합니다. 그래서 여기에서 주석도
더 달고 변형된 예를 보여줌으로써 이해와 응용에 도움이 되었으면 합니다.
아래 프로그램을 실행하면
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 | import numpy as np import matplotlib.pyplot as plt import matplotlib.colors as mcolors import matplotlib.patches as patches from PIL import Image from PIL import ImageDraw from PIL import ImageFilter def generate_data(num): x = np.linspace(0, 100, num) y = np.random.normal(0, 1, num).cumsum() return x, y def zfunc(x, y, fill_color='k', alpha=1.0): # Image 에 그려지는 x,y가 1-10 사이의 정수라서 뭉개지는 현상이 생겨 10를 곱함. # x, y가 1 이하라면 100 이상의 scale이 필요 scale = 10 x = (x*scale).astype(int) y = (y*scale).astype(int) # Image의 크기를 정하기 위하여 x, y의 최대값과 최소값을 찾음 xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max() # x 사이를 폭으로하고 y 사이를 높이로 함 w, h = xmax-xmin, ymax-ymin # numpy array을 만듬. z = np.empty((h, w, 4), dtype=float) # fill 의 색을 rgb로 바꿈 rgb = mcolors.colorConverter.to_rgb(fill_color) # z[*, *, 0-2]는 rgb로 채우고 z[:,:,:3] = rgb # z[*, *, 3]는 색상 경사를 뜻하는 gradient 혹은 alpha로 채울 것임 # w, h를 크기로 하는 Iimage를 만드나 실제 화면에 나타나는 것은 아님 # 후에 z[*, *, 3]에 들어가는 zalpha을 만들기 위함 img = Image.new('L', (w, h), 0) draw = ImageDraw.Draw(img) # 줄(line)을 그리기 위한 데이터 변환 xy = (np.column_stack([x, y])) xy -= xmin, ymin # Image에 x와 y를 좌표로 줄을 그리는데 width는 x, y에 따라 달라 질 수 있음 draw.line(tuple(map(tuple, xy.tolist())), fill=255, width=15) # 줄을 뿌옇게 만듬. radius는 범위인데 클 수록 gradient가 넓어지는 효과가 있음 img = img.filter(ImageFilter.GaussianBlur(radius=100)) # Image를 array로 변환 zalpha = np.asarray(img).astype(float) # 최대값으로 normalization zalpha *= alpha/zalpha.max() # 밑 부분의 색상이 없어지도록 1/4 이하에서는 alpha를 점진적으로 zero화함 n = zalpha.shape[0] // 4 zalpha[:n] *= np.linspace(0, 1, n)[:, None] # z[*, *, 3]에 alpha을 넣어 gradient화 시킴. z[:,:,-1] = zalpha return z def gradient_fill(x, y, fill_color=None, ax=None, **kwargs): if ax is None: ax = plt.gca() # x, y을 좌표로 줄을 그림 line, = ax.plot(x, y, **kwargs) if fill_color is None: fill_color = line.get_color() zorder = line.get_zorder() alpha = line.get_alpha() alpha = 1.0 if alpha is None else alpha # z는 x, y를 근거로 gradient 된 배경 z = zfunc(x, y, fill_color=fill_color, alpha=alpha) xmin, xmax, ymin, ymax = x.min(), x.max(), y.min(), y.max() # 배경을 그리고 im = ax.imshow(z, aspect='auto', extent=[xmin, xmax, ymin, ymax], origin='lower', zorder=zorder) # x, y 좌표와 xmin, ymin를 둘레로 다각형을 만들고 이를 기준으로 clipping xy = np.column_stack([x, y]) xy = np.vstack([[xmin, ymin], xy, [xmax, ymin], [xmin, ymin]]) clip_path = patches.Polygon(xy, facecolor='none', edgecolor='none', closed=True) ax.add_patch(clip_path) im.set_clip_path(clip_path) ax.autoscale(True) return line, im # x 축으로 0부터 99, y 축으로는 random하게 np.random.seed(2019) gradient_fill(*generate_data(100)) plt.show() |
곡선 밑에 그라데이션이 달린 그림을 볼 수 있습니다.
그라데이션이 없으면 흔히 보는 그래프가 됩니다.
먼저 line 18에 나타나는 scale는 x, y의 범위에 따라 적당하게 사용해야 합니다. Scale를 1로 하면 아래 왼쪽 그림과 같이 단절적인 그라데이션이 되고 100으로 하면 그라데이션 띠가 좁아지는 효과가 있습니다.
뿌연 곡선 효과를 만드는 line 44에서 반경을 10으로 줄이면
img = img.filter(ImageFilter.GaussianBlur(radius=10))
그라데이션 띠가 줄어 들게 할 수 있습니다.
그라데이션은 곡선을 중심으로 만든 전체 배경에서 곡선과 x,
y 범위를 기준을 만든 다각형을 오려낸 형태입니다. 라인 82를 실행하지
않으면 오리기 전 상황을 알 수 있습니다.
아래 쪽이 아닌 위쪽을 그라데이션하려면 라인 51를 바꾸어 위 부분 1/4을 점진적으로 0으로 만들고
zalpha[zalpha.shape[0]-n:] *=
np.linspace(1, 0, n)[:, None]
라인 78의 다각형을
xy = np.vstack([[xmin, ymax], xy, [xmax,
ymax], [xmin, ymax]])
로 위로 만들도록 합니다. 그러면 아래 그림을 얻을 수 있습니다.
댓글 없음:
댓글 쓰기