From b3d3a6cf9253d2b5385f4e330a52e916d5103159 Mon Sep 17 00:00:00 2001 From: Wang Guan Date: Mon, 18 Mar 2024 03:48:53 +0900 Subject: [PATCH] wip --- src/App.tsx | 2 +- src/apis/_request.ts | 16 ++++++ src/apis/mit_preprocess.ts | 36 ++++++++++--- src/apis/project.ts | 3 ++ .../MitPreprocess/TranslateCompanion.tsx | 50 ++++++++++++++++--- src/components/layout/site-banner.tsx | 3 ++ 6 files changed, 93 insertions(+), 17 deletions(-) create mode 100644 src/apis/_request.ts create mode 100644 src/components/layout/site-banner.tsx diff --git a/src/App.tsx b/src/App.tsx index 3170595..9a30ac8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,7 +20,7 @@ const publicPaths = [ '/login', '/register', '/reset-password', - '/demo/mit-preprocess', + '/temp/mit-preprocess', ] as readonly string[]; const App: React.FC = () => { diff --git a/src/apis/_request.ts b/src/apis/_request.ts new file mode 100644 index 0000000..d2dd5ec --- /dev/null +++ b/src/apis/_request.ts @@ -0,0 +1,16 @@ +import { BasicSuccessResult, request } from '.'; +import { AxiosRequestConfig } from 'axios'; + +export async function uploadRequest( + data: FormData, + configs: AxiosRequestConfig, +): Promise { + return request({ + data, + ...configs, + headers: { + ...configs.headers, + 'Content-Type': 'multipart/form-data', + }, + }); +} diff --git a/src/apis/mit_preprocess.ts b/src/apis/mit_preprocess.ts index 5bfe600..9c34e2b 100644 --- a/src/apis/mit_preprocess.ts +++ b/src/apis/mit_preprocess.ts @@ -1,19 +1,39 @@ import { request } from '.'; +import { uploadRequest } from './_request'; -async function createTask(files: File[]) { - return request({ +interface MitPreprocessResponse { + id: string; + result?: MitPreprocessResult; + status: 'success' | 'pending' | 'fail'; +} + +export interface MitPreprocessResult { + target_lang: string; + text_quads: TextQuad[]; +} + +type CoordTuple = [number, number]; // x, y in non-normalized pixels + +interface TextQuad { + pts: [CoordTuple, CoordTuple, CoordTuple, CoordTuple]; + raw_text: string; + translated: string; +} + +async function createTask(file: File) { + const formData = new FormData(); + formData.append('file', file); + + return uploadRequest<{ id: string }>(formData, { method: 'POST', - url: '/v1/mit-preprocess/jobs', - data: { - files: [], - }, + url: '/v1/mit/preprocess/tasks', }); } async function getTask(taskId: string) { - return request({ + return request({ method: 'GET', - url: `/v1/mit-preprocess/jobs/${taskId}`, + url: `/v1/mit/preprocess/tasks/${taskId}`, }); } diff --git a/src/apis/project.ts b/src/apis/project.ts index 0bde13d..8736d4b 100644 --- a/src/apis/project.ts +++ b/src/apis/project.ts @@ -232,6 +232,9 @@ const finishProject = ({ }); }; +/** + * @deprecated being retired + */ const startProjectOCR = ({ id, configs, diff --git a/src/components/MitPreprocess/TranslateCompanion.tsx b/src/components/MitPreprocess/TranslateCompanion.tsx index 1c3c354..8337905 100644 --- a/src/components/MitPreprocess/TranslateCompanion.tsx +++ b/src/components/MitPreprocess/TranslateCompanion.tsx @@ -1,24 +1,58 @@ import { FC } from '../../interfaces'; -import { useRef, useState } from 'react'; +import { RefObject, useRef, useState } from 'react'; import { FilePond } from 'react-filepond'; import { css } from '@emotion/core'; import { Button } from '../Button'; import { createMoeflowProjectZip, LPFile } from './moeflow-packager'; +import { api } from '../../apis'; +import { wait } from '@jokester/ts-commonutil/lib/concurrency/timing'; +import { measureImgSize } from '@jokester/ts-commonutil/lib/frontend/measure-img'; +import { sumBy } from 'lodash-es'; -async function translateFile(image: File): Promise { - return { file_name: image.name, labels: [] }; +const MAX_FILE_COUNT = 20; + +async function translateFile( + image: File, + running: RefObject, +): Promise { + const size = await measureImgSize(image); + const created = await api.mitPreprocess.createTask(image); + console.debug('task created', created); + while (running.current) { + const task = await api.mitPreprocess.getTask(created.data.id); + console.debug('task status', created); + if (task.data.status === 'success') { + return { + file_name: image.name, + // TODO: should sort the bubbles + labels: task.data.result!.text_quads.map((q) => { + const x = sumBy(q.pts, (p) => p[0]) / q.pts.length; + const y = sumBy(q.pts, (p) => p[1]) / q.pts.length; + return { + x: x / size.width, + y: y / size.height, + position_type: 1, + translation: q.translated, + }; + }), + }; + } else if (task.data.status !== 'fail') { + await wait(1e3); + } + } + throw new Error('todo'); } async function startOcr(files: File[]): Promise { const translations: LPFile[] = []; for (const f of files) { - const translated = await translateFile(f); + const translated = await translateFile(f, { current: true }); translations.push(translated); } const zipBlob = await createMoeflowProjectZip( { name: `${files[0]!.name}`, - intro: `这是由<萌翻+MitOCR demo>生成的项目. https://moeflow-.ihate.work/demo/ocr`, + intro: `这是由<萌翻+MitOCR demo>生成的项目. https://moeflow-mit-poc.voxscape.io/temp/mit-preprocess`, default_role: 'supporter', allow_apply_type: 3, application_check_type: 1, @@ -65,8 +99,8 @@ export const DemoOcrFiles: FC<{}> = (props) => { onupdatefiles={(_files) => { const files = _files.map((f) => f.file) as File[]; console.debug('onaddfile', files); - if (!(files.length > 0 && files.length <= 5)) { - setError('一次最多只能上传5张图片'); + if (!(files.length > 0 && files.length <= MAX_FILE_COUNT)) { + setError(`一次最多只能上传${MAX_FILE_COUNT}张图片`); setOrigFiles([]); filePondRef.current!.removeFiles(); } else { @@ -81,7 +115,7 @@ export const DemoOcrFiles: FC<{}> = (props) => { type="button" icon="plus" > - 1. Select up to 5 image files {error} + 1. Select up to ${MAX_FILE_COUNT} image files {error}