개요
이전에 기술 구현 가능 여부를 조사하면서 파이썬을 사용한 내용을 정리한 내용이다.
사용 기술
언어: Python 3.10
이미지 생성: matplotlib
서비스: AWS Lambda, AWS API Gateway
이미지 저장 및 URL: AWS S3, AWS CloudFront
플로우는 다음과 같다.
요구사항
우측 상단의 경로 이미지를 생성하려고 한다.
경로 이미지 생성에 대한 요구사항은 다음과 같다.
- 위도, 경도로 이루어진 배열을 입력받는다.
- 이미지 생성
- 선과 점 표현
- 투명한 배경색
- 위경도 차이가 크든 작든 제공하는 이미지 내에 경로가 다 포함되어 있어야 한다.
이미지 출력 방식
- 위경도를 처리한 값으로 직접 경로를 그린 다음 이미지 형태로 저장
- 플롯을 그려주는 라이브러리 사용하여 이미지 형태로 저장
이미지 출력 방식의 경우 1번과 2번을 고민했었다.
파이썬으로는 플롯을 그려주는 라이브러리인 matplotlib을 사용했다.
로컬에서 기능 구현
import time
import matplotlib.pyplot as plt
def draw(point):
start = time.time()
x, y = zip(*point)
pixel_x, pixel_y = convert_to_pixel_values(x, y)
draw_lines(pixel_x, pixel_y)
end = time.time()
print(end - start)
def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)
def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
return scaled_coordinates
def draw_lines(x, y):
figure = plt.gcf()
figure.set_size_inches(5, 5)
plt.plot(x, y, c = 'w',linewidth=5)
plt.scatter(x[3],y[3], c = 'w', s = 125)
plt.axis('off')
plt.savefig('name.png', transparent=True, format='png')
point = [
[126.96352960597338, 37.590841000217125],
[126.96987292787792, 37.58435564234159],
[126.98128481452298, 37.58594375113966],
[126.99360339342958, 37.58248524741927],
[126.99867565340067, 37.56778118088622],
[127.001935378366117, 37.55985240444085],
[126.9831048919687, 37.548030119488665],
[126.97189273528845, 37.5119879225856],
[127.02689859997221, 37.48488593333883]
]
draw(point)
생성 결과는 아래와 같다. (예시를 위해 검은색으로 출력)
AWS Lambda
썸네일 생성 서버를 따로 두기는 기능 대비 비용이 너무 클 것이라고 생각했다.
따라서 서버리스로 파일을 처리했다.
추가로 s3 접근은 boto3를 사용했다.
람다 S3 접근을 위한 IAM 생성
AmazonS3FullAccess, AmazonS3ObjectLambdaExecutionRolePolicy 두가지를 추가해서 Lambda 전용 역할을 만들어 사용했다.
람다 배포용 코드
기술 구현 가능 여부를 확인할 땐 위치 점을 찍는 기능을 람다에 배포하지 않았다.
import io
import uuid
import boto3
import matplotlib.pyplot as plt
PIXEL = 255
BUCKET_NAME = 'image-plot'
S3 = 's3'
def lambda_handler(event, context):
x = event['x']
y = event['y']
image_name = str(uuid.uuid4())
img_data = draw(x, y)
s3 = boto3.client(S3)
s3.put_object(Body=img_data.getvalue(), ContentType='image/png', Bucket=BUCKET_NAME, Key=image_name)
url = f'https://{BUCKET_NAME}.s3.ap-northeast-2.amazonaws.com/{image_name}'
return {
'statusCode': 200,
'body': url
}
def draw(x, y):
pixel_x, pixel_y = convert_to_pixel_values(x, y)
img_data = draw_lines(pixel_x, pixel_y)
plt.close()
return img_data
def convert_to_pixel_values(x, y):
max_diff = max(max(x) - min(x), max(y) - min(y))
return scale_to_pixel_values(x, max_diff), scale_to_pixel_values(y, max_diff)
def scale_to_pixel_values(points, max_diff):
min_value = min(points)
scaled_coordinates = [(p - min_value) / max_diff for p in points]
pixel_values = [int(p * PIXEL) for p in scaled_coordinates]
return pixel_values
def draw_lines(x, y):
plt.plot(x, y, 'k-', linewidth=10)
plt.axis('off')
img_data = io.BytesIO()
plt.savefig(img_data, transparent=True, format='png')
img_data.seek(0)
return img_data
Layer 추가를 위 한 zip 파일 생성
matplotlib의 경우 외부 라이브러리기 때문에 따로 Layer를 추가해야 한다.
zip 파일을 만들어서 업로드해야한다.
이때 python의 Lambda 런타임에 대한 계층 경로는 python이다.
따라서 압축한 zip 파일은 다음과 같은 구조를 띄어야 한다.
pillow.zip
│ python/PIL
└ python/Pillow-5.3.0.dist-info
Ubuntu 기준 다음 명령어를 입력하여 생성을 진행했다.
sudo apt update
sudo apt install zip
sudo apt install python3-pip
mkdir python
pip3 install matplotlib -t python # pip3 install 설치할_패키지 -t 설치_경로
zip -r my_layer.zip python # zip -r 압축_파일명 압축_파일이_존재하는_경로