비버놀로지

[선형대수학 Numpy] 벡터 연산으로 그림 그리기 본문

인공지능 머신러닝

[선형대수학 Numpy] 벡터 연산으로 그림 그리기

KUNDUZ 2022. 11. 3. 08:57
728x90

벡터 연산으로 그림 그리기

이번 미션에서는 Numpy를 이용한 벡터 연산으로 원과 다이아몬드 그림을 그리고, 이것들을 조합해 smile 그림을 그려보겠습니다.


캔버스

그림을 그리기 위해서는 그림을 그릴 공간이 필요합니다. 이 프로젝트에서는 이 공간을 xrange, yrange 라는 변수로 지정하겠습니다.

만약,

xrange = [1, 3]
yrange = [2, 4]

라면, 그림을 그릴 캔버스는 (1,2)(1, 2), (3,4)(3, 4) 로 지정된 공간을 사용하게 됩니다.


그림 그리기

그림을 그리기 위해서, 다음 방식을 사용하겠습니다. 어떤 함수 f와 매우 작은 숫자 threshold에 대해,

  • 캔버스 내에 점 P = (x, y)을 임의로 생성한다.
  • f(P) < threshold 라면 점을 찍는다. 만약 그렇지 않다면, 점을 찍지 않는다.
  • 이것을 100,000 회 반복한다.

 f(P) == 0 일 때 점을 찍지 않고, 아주 작은 값 threshold 보다 작을 때 점을 찍는지, 한번 생각해 보세요!


원 그리기

(0,0)(0, 0) 이 중심이고, 반지름 1인 원의 방정식은 다음과 같습니다.

x2+y2=1x^2 + y^2 = 1

원의 그림을 그리는 방식을 생각하면, 정확히 원 위에 있는 점들에 대해서 circle(P) 는 0을 가져야 합니다. 그러므로, circle(P) 는 다음과 같이 정의할 수 있습니다.

x = P[0]
y = P[1]
return sqrt(x ** 2 + y ** 2) - 1

위 코드의 리턴값은 수업시간에 배운 것과 같이 다음과 같습니다.

return sqrt(np.sum(P * P)) - 1

Norm의 개념을 사용한다면 다음과도 같습니다.

return np.linalg.norm(P) - 1

다이아몬드 그리기

(0,0)(0, 0) 이 중심이고, 원점에서 각 꼭지점까지 이르는 거리가 1인 다이아몬드의 방정식은 다음과 같습니다.

∣x∣+∣y∣=1|x| + |y| = 1

원 그리기와 같은 방식으로 생각해본다면, right_eye(P)는 다음과 같이 정의할 수 있습니다.

return np.abs(P[0]) + np.abs(P[1]) - 1
 

실습

  1. circle함수의 구성을 확인해보세요.
  2. diamond함수의 구성을 확인해보세요.
  3. smile함수의 구성을 확인해보세요. 어떻게
  4. 동작하는지 한 번 생각해보세요.
  5. sample함수의 구성을 확인해보고, 53번째 줄의 변수들을 바꿔보면서 실행해보세요. 결과가 어떻게 달라지나요?
import matplotlib as mpl
mpl.use("Agg")
import matplotlib.pyplot as plt
import elice_utils
import numpy as np
elice = elice_utils.EliceUtils()

def circle(P):
    return np.linalg.norm(P) - 1 # 밑의 코드와 동일하게 동작합니다.
    # return np.sqrt(np.sum(P * P)) - 1
    
def diamond(P):
    return np.abs(P[0]) + np.abs(P[1]) - 1
    
def smile(P):
    def left_eye(P):
        eye_pos = P - np.array([-0.5, 0.5])
        return np.sqrt(np.sum(eye_pos * eye_pos)) - 0.1
    
    def right_eye(P):
        eye_pos = P - np.array([0.5, 0.5])
        return np.sqrt(np.sum(eye_pos * eye_pos)) - 0.1
    
    def mouth(P):
        if P[1] < 0:
            return np.sqrt(np.sum(P * P)) - 0.7
        else:
            return 1
    
    return circle(P) * left_eye(P) * right_eye(P) * mouth(P)

def checker(P, shape, tolerance):
    return abs(shape(P)) < tolerance

def sample(num_points, xrange, yrange, shape, tolerance):
    accepted_points = []
    rejected_points = []
    
    for i in range(num_points):
        x = np.random.random() * (xrange[1] - xrange[0]) + xrange[0]
        y = np.random.random() * (yrange[1] - yrange[0]) + yrange[0]
        P = np.array([x, y])
        
        if (checker(P, shape, tolerance)):
            accepted_points.append(P)
        else:
            rejected_points.append(P)
    
    return np.array(accepted_points), np.array(rejected_points)

xrange = [-1.5, 1.5] # X축 범위입니다.
yrange = [-1.5, 1.5] # Y축 범위입니다.
accepted_points, rejected_points = sample(
    100000, #  점의 개수를 줄이거나 늘려서 실행해 보세요. 너무 많이 늘리면 시간이 오래 걸리는 것에 주의합니다.
    xrange, 
    yrange, 
    smile, # smile을 circle 이나 diamond 로 바꿔서 실행해 보세요.
    0.005) # Threshold를 0.01이나 0.0001 같은 다른 값으로 변경해 보세요.

plt.figure(figsize=(xrange[1] - xrange[0], yrange[1] - yrange[0]), 
           dpi=150) # 그림이 제대로 로드되지 않는다면 DPI를 줄여보세요.
           
plt.scatter(rejected_points[:, 0], rejected_points[:, 1], c='lightgray', s=0.1)
plt.scatter(accepted_points[:, 0], accepted_points[:, 1], c='black', s=1)

plt.savefig("graph.png")
elice.send_image("graph.png")
 

 

 

 

728x90
Comments