AWS AI CV PROJECT
2024/11/01 - 2024/11/30
프로젝트 내용
- 두부 공장에서 생산되는 제품의 정상, NG 이미지를 받아 두부의 결함 여부를 탐지하는 컴퓨팅비전 프로젝트
- 프론트엔드, AWS 인프라, 모델 별 역할 나눠서 진행
- 맡은 역할: 프론트엔드 화면 구현, AWS 아키텍처 설계 및 인프라 구현
구현 결과 정리
1. 프론트엔드(Angular)
구현 목표
- 실시간으로 두부가 나열되는 영상에서 이미지만을 추출해 양불여부를 판정하고, 통계 데이터를 확인할 수 있는 사이트를 구현한다.
세부 구현 내용
- 프로젝트 초기 설정은 angular, typescript 기반 오픈소스 활용
- 실시간 두부 영상을 받아 두부 이미지 캡처
- 실시간 동영상을 받아서 brightness>50이면 brightPixel값을 1 올림. 최종적으로 전체 화면 대비 brightPixel의 비율이 49% 초과이면 이미지를 캡처
- base64tofile 함수를 통해 base64를 파일 객체로 저장 후 전송
const detectAndCapture = async () => {
if (!videoElement || videoElement.paused || videoElement.ended || !canCapture) {
return;
}
canCapture = false; // 캡처 시작 시 즉시 false로 설정
const canvas = document.createElement("canvas");
canvas.width = videoElement.videoWidth;
canvas.height = videoElement.videoHeight;
const ctx = canvas.getContext("2d");
if (ctx) {
ctx.drawImage(videoElement, 0, 0, canvas.width, canvas.height);
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData.data;
let brightPixels = 0;
const totalPixels = pixels.length / 4;
for (let i = 0; i < pixels.length; i += 4) {
const r = pixels[i];
const g = pixels[i + 1];
const b = pixels[i + 2];
const brightness = 0.299 * r + 0.587 * g + 0.114 * b;
if (brightness > 50) {
brightPixels++;
}
}
const brightRatio = brightPixels / totalPixels;
if (brightRatio > 0.49) {
console.log("Bright area detected, capturing image...");
const capturedImageData = canvas.toDataURL("image/png");
if (inferenceImage) {
inferenceImage.src = capturedImageData;
}
const imageFile = base64ToFile(capturedImageData, "captured-image.png");
await uploadImage(imageFile);
}
}
- 캡처된 이미지를 API를 통해 백엔드로 전송
- sagemaker로 결함 여부 및 결함 종류 판정해 API 받아 화면에 띄우기
- POST 메소드를 통해 aws lambda 엔드포인트로 fetch
const uploadImage = async (imageFile) => {
const apiUrl = [lambda 엔드포인트]
const formData = new FormData();
formData.append("image", imageFile);
try {
const response = await fetch(apiUrl, {
method: "POST",
mode: "no-cors",
body: formData,
});
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
const result = await response.json();
console.log("Inference Result:", result);
// 양불 상태 처리
if (result.result === 'OK') {
okButton.style.backgroundColor = '#1AA13A'; // OK 버튼 활성화
statusMessage.innerText = '이 두부는 정상입니다.';
detectedMessage.innerTest='';
} else if (result.result === 'NG') {
ngButton.style.backgroundColor = '#FF4141'; // NG 버튼 활성화
statusMessage.innerText = '이 두부는 불량입니다.';
detectedMessage.innerTest='결함유형 :'+result.ng_classes
} else {
// 알 수 없는 상태 처리
statusMessage.innerText = '알 수 없는 상태입니다.';
console.warn('알 수 없는 상태:', result.result);
detectedMessage.innerTest='';
}
} catch (error) {
console.error('결과를 표시하는 중 오류가 발생했습니다:', error);
statusMessage.innerText = '결과를 로드하는 중 문제가 발생했습니다.';
}
};
2. AWS 인프라
- AWS 아키텍처 설계
- 담당한 부분: angular - cloud9 - api gateway - lambda
- 미구현: amazon cognito, github action
- 프론트엔드 cloud9, S3로 배포
- s3 버킷 생성
- 정적 웹 호스팅 허용, index document: index.html 설정
- 버킷 권한 허용
- 버킷 정책 편집
- s3에 업로드
- github에 올린 프로젝트 파일을 cloud9에 clone
- ng build --prod 명령어를 통해 프로젝트 파일을 build(build된 파일의 최상단에 index.html이 위치되어야 함)
- build된 파일을 s3에 업로드
- s3 버킷 생성
{
"Version": "2012-10-17",
"Id": "Policy1706126466848",
"Statement": [
{
"Sid": "s3-public",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::<<BUCKET_NAME>>/*"
}
]
}
- lambda, api gateway를 통해 프론트엔드와 sagemaker 통신
- 프론트엔드와 lambda 간의 연결을 위한 api gateway 구현
- lambda 코드
import json
import boto3
import base64
import time
# SageMaker 설정
ENDPOINT_NAME = "binary-endpoint"
# SageMaker Runtime 클라이언트 생성
runtime = boto3.client('runtime.sagemaker')
# Base64 패딩을 자동으로 추가하는 함수
def fix_base64_padding(base64_string):
# 패딩이 부족한 경우 "="을 추가
missing_padding = len(base64_string) % 4
if missing_padding:
base64_string += '=' * (4 - missing_padding)
return base64_string
# 데이터 URI 스키마 제거 함수
def remove_data_uri_scheme(base64_string):
if "," in base64_string:
return base64_string.split(",")[1] # "data:image/png;base64," 이후의 순수 데이터 반환
return base64_string
def lambda_handler(event, context):
try:
start_time = time.time()
# 요청에서 이미지 데이터 추출
body = event["body"]
body_json = json.loads(body)
payload = body_json["image"]
# 이미지 데이터가 없을 경우 처리
if not payload:
return {
'statusCode': 400,
'body': json.dumps({'message': 'No image data provided'})
}
# 디버깅을 위한 로깅 (수신한 base64 문자열 확인)
print(f"Received Base64 Payload: {payload[:100]}...") # 처음 100자만 로깅
# 데이터 URI 스키마 제거
payload = remove_data_uri_scheme(payload)
print(f"Base64 Without Data URI Scheme: {payload[:100]}...") # 수정된 base64 문자열 확인
# Base64 패딩 수정
payload = fix_base64_padding(payload)
print(f"Fixed Base64 Payload: {payload[:100]}...") # 패딩 수정 후 데이터 확인
# Base64 디코딩
image_binary = base64.b64decode(payload)
# SageMaker Endpoint로 요청 전송
response = runtime.invoke_endpoint(
EndpointName=ENDPOINT_NAME,
ContentType="application/json",
Body=json.dumps({'image': payload}) # 순수 Base64 데이터를 전달
)
# SageMaker 결과 처리
result = json.loads(response['Body'].read().decode())
print(f"SageMaker Response: {result}")
end_time = time.time()
response_time_ms = (end_time - start_time) * 1000
# 결과값 반환
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Prediction successful',
'result': result,
'response_time_ms': round(response_time_ms, 2)
})
}
except Exception as e:
# 예외 처리 및 로그 출력
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps({'message': 'Error during processing', 'error': str(e)})
}
에러 발생 및 디버깅
1. angular 기반 프로젝트 파일이 s3 버킷에 업로드 되어 바로 정적 호스팅한 화면이 뜨지 않는 상황
[해결방법] cloud9 내에서 build 후 최상단에 index.html이 있는지 확인한 후 버킷에 업로드하기
build 명령어 : ng build --prod
(angular cli의 프로젝트 매니저명: ng)
2. base64로 인코딩되어 전달된 이미지 파일이 sagemaker에서 인식되지 않는 상황
[해결방법] lambda에서 순수 데이터 정제 후 디코딩
※ base64 인코딩 디코딩 과정 정리
1. 프론트엔드에서 이미지 파일을 받아 base 64로 인코딩한 후 파일 객체를 백엔드 단으로 전달
2. 람다에서 base64를 정제하여 바이너리 데이터로 디코딩하여 sagemaker로 전달
- 전처리: data:image/png;base64,...에서 순수 base64만을 추출
- 디코딩: 전처리한 결과를 바이너리 데이터로 디코딩하여 sagemaker에 전달
※base64 패딩 전처리
일부 클라이언트에서는 base64 데이터 생성 시 패딩(=)을 생략하거나 제공하지 않는 경우 발생
이처럼 base64 데이터가 생략되어 4의 배수가 아닌 경우 패딩 문자를 추가해야 함
# Base64 패딩을 자동으로 추가하는 함수
def fix_base64_padding(base64_string):
# 패딩이 부족한 경우 "="을 추가
missing_padding = len(base64_string) % 4
if missing_padding:
base64_string += '=' * (4 - missing_padding)
return base64_string
깃허브 주소
'Project' 카테고리의 다른 글
[SpringBoot]CRUD 게시판 구현 및 디벨롭 (1) | 2025.01.30 |
---|---|
[React]대학생을 위한 설문조사 중개 서비스, 썰매 (0) | 2025.01.30 |
[AWS]AWS 이용해 AI 챗봇 만들기: 텔레그램에서 chat-GPT와 대화하는 법 (0) | 2023.09.16 |