Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {sessionAuthMiddleware} from './auth.config.js';
import cookieParser from 'cookie-parser';
import {ValidateError} from 'tsoa';
import {labelDetectionController} from './controllers/tags-ai.controller.js';
import upload from './ai/ai-upload.js';

// routers
import {RegisterRoutes} from './routers/tsoaRoutes.js';
Expand Down Expand Up @@ -115,7 +116,7 @@ app.use('/challenge', challengeRouter);
app.use('/user/mypage', myPageRouter);
app.use('/tag', tagRouter);
app.use('/trust', trustRouter);
app.post('/image/ai', labelDetectionController);
app.post('/image/ai', upload.single('base64_image'), labelDetectionController);

RegisterRoutes(app);

Expand Down
168 changes: 89 additions & 79 deletions src/controllers/tags-ai.controller.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,113 @@
import {NextFunction, Request, Response} from 'express';
import {detectLabels} from '../services/tags-ai.service.js';
import {StatusCodes} from 'http-status-codes';
import {DataValidationError} from '../errors.js';
import {PhotoDataNotFoundError, PhotoValidationError} from '../errors.js';

export const labelDetectionController = async (
req: Request,
res: Response,
next: NextFunction,
): Promise<void> => {
/*
#swagger.tags = ['label-detection']
#swagger.summary = '이미지 라벨링'
#swagger.description = 'Base64 데이터를 JSON으로 받아 이미지를 분석하여 상위 3개의 라벨과 정확도를 반환합니다.'
#swagger.requestBody = {
required: true,
content: {
"application/json": {
schema: {
type: "object",
properties: {
base64_image: {
type: "string",
description: "Base64 인코딩된 이미지 데이터",
example: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
}
}
}
}
}
}
#swagger.responses[200] = {
description: "라벨링 결과 반환",
content: {
"application/json": {
schema: {
type: "object",
properties: {
topLabels: {
type: "array",
items: {
type: "object",
properties: {
description: { type: "string", example: "Mountain" },
score: { type: "number", example: 0.95 }
}
}
}
}
}
}
}
}
#swagger.responses[400] = {
description: "잘못된 요청 데이터",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string", example: "Base64 이미지 데이터가 제공되지 않았습니다." }
}
}
}
}
}
#swagger.responses[500] = {
description: "서버 내부 오류",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string", example: "라벨링 중 오류가 발생했습니다." }
}
}
}
}
}
*/
#swagger.tags = ['label-detection']
#swagger.summary = '이미지 라벨링'
#swagger.description = 'Base64 데이터를 JSON으로 받아 이미지를 분석하여 상위 3개의 라벨과 정확도를 반환합니다.'
#swagger.requestBody = {
required: true,
content: {
"multipart/form-data": {
schema: {
type: "object",
properties: {
base64_image: {
type: "string",
format: "binary",
description: "업로드할 이미지 파일"
}
}
}
}
}
}
#swagger.responses[200] = {
description: "라벨링 결과 반환",
content: {
"application/json": {
schema: {
type: "object",
properties: {
topLabels: {
type: "array",
items: {
type: "object",
properties: {
description: { type: "string", example: "Mountain" },
score: { type: "number", example: 0.95 }
}
}
}
}
}
}
}
}
#swagger.responses[400] = {
description: "잘못된 요청 데이터",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string", example: "Base64 이미지 데이터가 제공되지 않았습니다." }
}
}
}
}
}
#swagger.responses[500] = {
description: "서버 내부 오류",
content: {
"application/json": {
schema: {
type: "object",
properties: {
error: { type: "string", example: "라벨링 중 오류가 발생했습니다." }
}
}
}
}
}
*/

try {
// Base64 이미지 데이터가 요청에 포함되었는지 확인
const {base64_image} = req.body;

if (!base64_image) {
throw new DataValidationError({
reason: 'Base64 이미지 데이터가 제공되지 않았습니다.',
if (!req.file) {
throw new PhotoDataNotFoundError({
reason: '라벨링을 추출 할 사진이 없습니다',
});
}

// Base64 데이터에서 MIME 타입 제거
const base64Data = base64_image.replace(/^data:image\/\w+;base64,/, '');
const base64_image = req.file.buffer.toString('base64');

// 서비스 호출
const labels = await detectLabels(base64Data);
if (!isValidBase64(base64_image)) {
throw new PhotoValidationError({
reason: '올바른 Base64 이미지 형식이 아닙니다.',
});
}

//서비스 호출
const labels = await detectLabels(base64_image);

// 라벨 반환
res.status(StatusCodes.OK).json({topLabels: labels});
} catch (error) {
next(error);
}
};

const isValidBase64 = (base64String: string): boolean => {
// base64 문자열 검증 함수
const base64Regex =
/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
return base64Regex.test(base64String);
};
4 changes: 2 additions & 2 deletions src/routers/tsoaRoutes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import { MemoImageController } from './../controllers/tsoa.memo-image.controller
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { MemoFolderController } from './../controllers/tsoa.memo-folder.controller.js';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { MemoCreateFolderOCRController } from '../controllers/memo-updateFolderOCR.controller.js';
import { MemoCreateFolderOCRController } from './../controllers/memo-updateFolderOCR.controller.js';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { MemoCreateUpdateOCRController } from '../controllers/memo-createFolderOCR.controller.js';
import { MemoCreateUpdateOCRController } from './../controllers/memo-createFolderOCR.controller.js';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
import { MostTaggedController } from './../controllers/history.controller.js';
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
Expand Down
6 changes: 3 additions & 3 deletions swagger/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,14 @@
"requestBody": {
"required": true,
"content": {
"application/json": {
"multipart/form-data": {
"schema": {
"type": "object",
"properties": {
"base64_image": {
"type": "string",
"description": "Base64 인코딩된 이미지 데이터",
"example": "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD..."
"format": "binary",
"description": "업로드할 이미지 파일"
}
}
}
Expand Down