AWS Lambda와 API Gateway를 통해 입력 받은 이미지 처리하기

    목차
반응형

0. 목적

s3에 저장되어 있는 이미지가 아닌 외부 이미지를 받아서 SageMaker의 딥러닝 모델 엔드포인트로 다루고자 함(public API)

 

1. lambda 함수 작성

- `pillow`, `numpy`, `requests-toolbelt` 라이브러리 필요

- 라이브러리 설치 방법:

from PIL import Image
import numpy as np
import boto3
import io
import json
import base64
from requests_toolbelt.multipart import decoder
from urllib.parse import parse_qs

s3_client = boto3.client('s3')
runtime = boto3.client("sagemaker-runtime")

ep = 'ep' # sagemaker endpoint
    
def lambda_handler(event, context):
    # 첨부된 이미지 파일 open
    if 'body' in event:
        body = base64.b64decode(event['body']) 
    else:
        body = base64.b64decode(event['body-json'])
    
    
    if 'content-type' in event['params']['header']:
        content_type = event['params']['header']['content-type']
    else:
        content_type = event['params']['header']['Content-Type']
    
    decode = decoder.MultipartDecoder(body, content_type)
        
    for part in decode.parts:
        content = part.content
        headers = part.headers
        
        disposition = headers[b'Content-Disposition'].decode()
        lst = list(map(lambda x:x.strip(), disposition.replace('form-data;','').split(';')))
        key = list(filter(lambda x:'name' in x, list(map(lambda x:parse_qs(x), lst))))[0]['name'][0].strip('"')

        if key == 'image':
            img = content
        elif key == 'key1':
            key1 = int(content.decode())
        elif key == 'key2':
            key2 = str(content.decode())
    
    img = Image.open(io.BytesIO(content))
    
    img = img.resize((256, 256))
    img = np.array(img).astype('uint8')
    
    # 추론
    isfail = True
    round_num = 5
    while (isfail) and (round_num >= 0):
        try:
            img_input = (img / 255.).astype('float32').round(round_num)
            
            payload = json.dumps([img_input.tolist()])
            response = runtime.invoke_endpoint(
                EndpointName=ep1, ContentType="application/json", Body=payload
            )
            
            isfail = False
            
        except:
            round_num -= 1
    result = json.loads(response['Body'].read().decode())
    
    return result

 

코드 설명

- 17~20: base64로 암호화 되어있는 이미지 파일(body 혹은 body-json)을 복호화

- `decode = decoder.MultipartDecoder(body, content_type)`: content-type과 body를 multipartDecoder를 통해 복호화(이미지 파일)

- 30~32: content와 headers로 나누는 과정

- 34~43: API에서 여러 key를 넘겨줄 경우, 이런 방식을 통해 나눠주어야 함

- `img = Image.open(io.BytesIO(content))`: content를 python에서 다룰 수 있도록 `pillow` 패키지를 이용하여 이미지 열기

- 50~: sagemaker endpoint에 넣기 위한 전처리 과정

    - 소수점 아래로 숫자가 너무 많으면 input 용량이 너무 크다는 오류가 발생한다. (AWS 설명=최대 5MB; Getting error while invoking sagemaker endpoint · Issue #245 · aws/amazon-sagemaker-examples)

    `[ERROR] ValidationError: An error occurred (ValidationError) when calling the InvokeEndpoint operation: Request ~~~~~ has oversize body.`

    이를 해결하기 위해 소수점 아래 n자리까지 반올림하는 과정을 거치며 될 때까지 반복 (또는 serverless가 아닌 async 사용을 권장)

 

 

2. API Gateway 설정

2-1. Lambda와 연결

- REST API 구축

- API 생성

 

 

- 작업-리소스 생성-리소스 이름 입력

- 작업-메서드 생성(POST)-Lambda 함수를 지정

 

 

 

2-2. 받는 데이터 형식 지정

- 설정에서 이진 미디어 형식에 `multipart/form-data` 추가

- 위에서 만든 메서드의 통합 요청 설정 (리소스-POST 클릭)

 

 

 

2-3. API 키 생성(보안)

vpc로 감싸진 내부용 API가 아닌 외부에서도 쓸 수 있도록 만든 API이기 때문에, 보안을 위해 API 키 발급

- 메서드 요청에서 API 키가 필요함을 `true`로 설정

- API 키에서 API 생성(이름: `x-api-key`), API 키가 유출되지 않도록 주의

 

 

- 사용량 계획 생성

 

 

- API 사용량 할당(사용량, 유저수 등을 고려)

 

 

2-4. API 배포

- 리소스에서 POST 메서드 선택 후 API 배포

 

 

- 호출 API 생성

    - 앞서 생성한 리소스를 주소 끝에 붙여줘야함(`https://~~~.amazonaws.com/v1/{resource_name}`)

 

 

3. API 테스트

- Postman 환경에서 테스트

- 주소 입력

- API 키 설정

 

- Body에서 form-data로 설정 후 파일 첨부

 

- 요청 결과 확인

728x90
반응형